Compare commits
11 Commits
v0.0.5
...
89b3354a80
Author | SHA1 | Date | |
---|---|---|---|
89b3354a80 | |||
488c723726 | |||
76d0785b33 | |||
add0af11ad | |||
2c0adf4a8b | |||
e8ac78cce6 | |||
13d447c9fe | |||
da7523c5cf | |||
a074bc6f72 | |||
1553a44986 | |||
859ac352e6 |
73
Bench.cpp
73
Bench.cpp
@@ -258,4 +258,75 @@ void benchConflictSet() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) { benchConflictSet(); }
|
void benchWorstCaseForRadixRangeRead() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
ConflictSet cs{0};
|
||||||
|
|
||||||
|
int64_t version = 0;
|
||||||
|
|
||||||
|
constexpr int kKeyLength = 50;
|
||||||
|
|
||||||
|
for (int i = 0; i < kKeyLength; ++i) {
|
||||||
|
for (int j = 0; j < 256; ++j) {
|
||||||
|
auto b = std::vector<uint8_t>(i, 0);
|
||||||
|
b.push_back(j);
|
||||||
|
auto e = std::vector<uint8_t>(i, 255);
|
||||||
|
e.push_back(j);
|
||||||
|
weaselab::ConflictSet::WriteRange w[] = {{
|
||||||
|
{b.data(), int(b.size())},
|
||||||
|
{nullptr, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{e.data(), int(e.size())},
|
||||||
|
{nullptr, 0},
|
||||||
|
}};
|
||||||
|
cs.addWrites(w, sizeof(w) / sizeof(w[0]), version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defeat short-circuiting on the left
|
||||||
|
{
|
||||||
|
auto k = std::vector<uint8_t>(kKeyLength, 0);
|
||||||
|
weaselab::ConflictSet::WriteRange w[] = {
|
||||||
|
{
|
||||||
|
{k.data(), int(k.size())},
|
||||||
|
{nullptr, 0},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cs.addWrites(w, sizeof(w) / sizeof(w[0]), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defeat short-circuiting on the right
|
||||||
|
{
|
||||||
|
auto k = std::vector<uint8_t>(kKeyLength, 255);
|
||||||
|
weaselab::ConflictSet::WriteRange w[] = {
|
||||||
|
{
|
||||||
|
{k.data(), int(k.size())},
|
||||||
|
{nullptr, 0},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cs.addWrites(w, sizeof(w) / sizeof(w[0]), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin = std::vector<uint8_t>(kKeyLength - 1, 0);
|
||||||
|
begin.push_back(1);
|
||||||
|
auto end = std::vector<uint8_t>(kKeyLength - 1, 255);
|
||||||
|
end.push_back(254);
|
||||||
|
|
||||||
|
weaselab::ConflictSet::Result result;
|
||||||
|
weaselab::ConflictSet::ReadRange r{
|
||||||
|
{begin.data(), int(begin.size())}, {end.data(), int(end.size())}, 1};
|
||||||
|
|
||||||
|
bench.run("worst case for radix tree", [&]() {
|
||||||
|
result = weaselab::ConflictSet::TooOld;
|
||||||
|
cs.check(&r, &result, 1);
|
||||||
|
if (result != weaselab::ConflictSet::Commit) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
benchConflictSet();
|
||||||
|
benchWorstCaseForRadixRangeRead();
|
||||||
|
}
|
||||||
|
@@ -1,14 +1,16 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
project(
|
project(
|
||||||
conflict-set
|
conflict-set
|
||||||
VERSION 0.0.5
|
VERSION 0.0.7
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
||||||
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
||||||
LANGUAGES C CXX)
|
LANGUAGES C CXX)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
file(WRITE ${CMAKE_BINARY_DIR}/version.txt ${PROJECT_VERSION})
|
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/version.txt ${PROJECT_VERSION})
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.txt.in
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/paper/version.txt)
|
||||||
|
|
||||||
include(CMakePushCheckState)
|
include(CMakePushCheckState)
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
@@ -47,7 +49,8 @@ if(HAS_FULL_RELRO)
|
|||||||
endif()
|
endif()
|
||||||
cmake_pop_check_state()
|
cmake_pop_check_state()
|
||||||
|
|
||||||
set(version_script_flags LINKER:--version-script=${CMAKE_SOURCE_DIR}/linker.map)
|
set(version_script_flags
|
||||||
|
LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.map)
|
||||||
cmake_push_check_state()
|
cmake_push_check_state()
|
||||||
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${version_script_flags})
|
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${version_script_flags})
|
||||||
check_cxx_source_compiles("int main(){}" HAS_VERSION_SCRIPT FAIL_REGEX
|
check_cxx_source_compiles("int main(){}" HAS_VERSION_SCRIPT FAIL_REGEX
|
||||||
@@ -59,7 +62,7 @@ option(USE_SIMD_FALLBACK
|
|||||||
|
|
||||||
# This is encouraged according to
|
# This is encouraged according to
|
||||||
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
||||||
include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/third_party/valgrind)
|
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/valgrind)
|
||||||
|
|
||||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
|
||||||
|
|
||||||
@@ -106,19 +109,20 @@ add_library(${PROJECT_NAME}-object OBJECT ConflictSet.cpp)
|
|||||||
target_compile_options(${PROJECT_NAME}-object PRIVATE -fno-exceptions
|
target_compile_options(${PROJECT_NAME}-object PRIVATE -fno-exceptions
|
||||||
-fvisibility=hidden)
|
-fvisibility=hidden)
|
||||||
target_include_directories(${PROJECT_NAME}-object
|
target_include_directories(${PROJECT_NAME}-object
|
||||||
PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||||
"${CMAKE_BINARY_DIR}/radix_tree")
|
"${CMAKE_CURRENT_BINARY_DIR}/radix_tree")
|
||||||
if(NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
if(NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C)
|
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HAS_VERSION_SCRIPT)
|
if(HAS_VERSION_SCRIPT)
|
||||||
target_link_options(${PROJECT_NAME} PRIVATE
|
target_link_options(
|
||||||
LINKER:--version-script=${CMAKE_SOURCE_DIR}/linker.map)
|
${PROJECT_NAME} PRIVATE
|
||||||
|
LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.map)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(${PROJECT_NAME}-static STATIC
|
add_library(${PROJECT_NAME}-static STATIC
|
||||||
@@ -131,7 +135,7 @@ if(APPLE)
|
|||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${PROJECT_NAME}-static
|
TARGET ${PROJECT_NAME}-static
|
||||||
PRE_LINK
|
PRE_LINK
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/privatize_symbols_macos.sh
|
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/privatize_symbols_macos.sh
|
||||||
$<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
$<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
||||||
else()
|
else()
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
@@ -139,21 +143,22 @@ else()
|
|||||||
POST_BUILD
|
POST_BUILD
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_OBJCOPY}
|
${CMAKE_OBJCOPY}
|
||||||
--keep-global-symbols=${CMAKE_SOURCE_DIR}/symbol-exports.txt
|
--keep-global-symbols=${CMAKE_CURRENT_SOURCE_DIR}/symbol-exports.txt
|
||||||
$<TARGET_FILE:${PROJECT_NAME}-static> || echo
|
$<TARGET_FILE:${PROJECT_NAME}-static> || echo
|
||||||
"Proceeding with all symbols global in static library")
|
"Proceeding with all symbols global in static library")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(TEST_FLAGS -Wall -Wextra -Wunreachable-code -Wpedantic -UNDEBUG)
|
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
# disable tests if this is being used through e.g. FetchContent
|
||||||
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||||
|
|
||||||
|
set(TEST_FLAGS -Wall -Wextra -Wunreachable-code -Wpedantic -UNDEBUG)
|
||||||
|
|
||||||
# corpus tests, which are tests curated by libfuzzer. The goal is to get broad
|
# corpus tests, which are tests curated by libfuzzer. The goal is to get broad
|
||||||
# coverage with a small number of tests.
|
# coverage with a small number of tests.
|
||||||
|
|
||||||
file(GLOB CORPUS_TESTS ${CMAKE_SOURCE_DIR}/corpus/*)
|
file(GLOB CORPUS_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/corpus/*)
|
||||||
|
|
||||||
# extra testing that relies on shared libraries, which aren't available with
|
# extra testing that relies on shared libraries, which aren't available with
|
||||||
# wasm
|
# wasm
|
||||||
@@ -162,9 +167,11 @@ if(BUILD_TESTING)
|
|||||||
add_library(skip_list SHARED SkipList.cpp)
|
add_library(skip_list SHARED SkipList.cpp)
|
||||||
target_compile_options(skip_list PRIVATE -fno-exceptions
|
target_compile_options(skip_list PRIVATE -fno-exceptions
|
||||||
-fvisibility=hidden)
|
-fvisibility=hidden)
|
||||||
target_include_directories(skip_list PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
target_include_directories(skip_list
|
||||||
set_target_properties(skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
"${CMAKE_BINARY_DIR}/skip_list")
|
set_target_properties(
|
||||||
|
skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/skip_list")
|
||||||
set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
skip_list PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
skip_list PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
||||||
@@ -175,10 +182,11 @@ if(BUILD_TESTING)
|
|||||||
add_library(hash_table SHARED HashTable.cpp)
|
add_library(hash_table SHARED HashTable.cpp)
|
||||||
target_compile_options(hash_table PRIVATE -fno-exceptions
|
target_compile_options(hash_table PRIVATE -fno-exceptions
|
||||||
-fvisibility=hidden)
|
-fvisibility=hidden)
|
||||||
target_include_directories(hash_table PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
target_include_directories(hash_table
|
||||||
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||||
"${CMAKE_BINARY_DIR}/hash_table")
|
"${CMAKE_CURRENT_BINARY_DIR}/hash_table")
|
||||||
set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
||||||
@@ -266,15 +274,19 @@ if(BUILD_TESTING)
|
|||||||
set_property(
|
set_property(
|
||||||
DIRECTORY
|
DIRECTORY
|
||||||
APPEND
|
APPEND
|
||||||
PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/test_conflict_set.py)
|
PROPERTY CMAKE_CONFIGURE_DEPENDS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test_conflict_set.py)
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/test_conflict_set.py
|
COMMAND ${Python3_EXECUTABLE}
|
||||||
list OUTPUT_VARIABLE SCRIPT_TESTS)
|
${CMAKE_CURRENT_SOURCE_DIR}/test_conflict_set.py list
|
||||||
|
OUTPUT_VARIABLE SCRIPT_TESTS)
|
||||||
foreach(TEST ${SCRIPT_TESTS})
|
foreach(TEST ${SCRIPT_TESTS})
|
||||||
add_test(
|
add_test(
|
||||||
NAME script_test_${TEST}
|
NAME script_test_${TEST}
|
||||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/test_conflict_set.py
|
COMMAND
|
||||||
test ${TEST} --build-dir ${CMAKE_BINARY_DIR})
|
${Python3_EXECUTABLE}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test_conflict_set.py test ${TEST}
|
||||||
|
--build-dir ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -308,25 +320,26 @@ if(BUILD_TESTING)
|
|||||||
# symbol visibility tests
|
# symbol visibility tests
|
||||||
if(NOT WASM AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
if(NOT WASM AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(symbol_exports ${CMAKE_SOURCE_DIR}/apple-symbol-exports.txt)
|
set(symbol_exports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-exports.txt)
|
||||||
set(symbol_imports ${CMAKE_SOURCE_DIR}/apple-symbol-imports.txt)
|
set(symbol_imports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-imports.txt)
|
||||||
else()
|
else()
|
||||||
set(symbol_exports ${CMAKE_SOURCE_DIR}/symbol-exports.txt)
|
set(symbol_exports ${CMAKE_CURRENT_SOURCE_DIR}/symbol-exports.txt)
|
||||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
||||||
set(symbol_imports ${CMAKE_SOURCE_DIR}/aarch64-symbol-imports.txt)
|
set(symbol_imports
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/aarch64-symbol-imports.txt)
|
||||||
else()
|
else()
|
||||||
set(symbol_imports ${CMAKE_SOURCE_DIR}/symbol-imports.txt)
|
set(symbol_imports ${CMAKE_CURRENT_SOURCE_DIR}/symbol-imports.txt)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
add_test(
|
add_test(
|
||||||
NAME conflict_set_shared_symbols
|
NAME conflict_set_shared_symbols
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_SOURCE_DIR}/test_symbols.sh $<TARGET_FILE:${PROJECT_NAME}>
|
${CMAKE_CURRENT_SOURCE_DIR}/test_symbols.sh
|
||||||
${symbol_exports} ${symbol_imports})
|
$<TARGET_FILE:${PROJECT_NAME}> ${symbol_exports} ${symbol_imports})
|
||||||
add_test(
|
add_test(
|
||||||
NAME conflict_set_static_symbols
|
NAME conflict_set_static_symbols
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_SOURCE_DIR}/test_symbols.sh
|
${CMAKE_CURRENT_SOURCE_DIR}/test_symbols.sh
|
||||||
$<TARGET_FILE:${PROJECT_NAME}-static> ${symbol_exports}
|
$<TARGET_FILE:${PROJECT_NAME}-static> ${symbol_exports}
|
||||||
${symbol_imports})
|
${symbol_imports})
|
||||||
endif()
|
endif()
|
||||||
@@ -369,13 +382,13 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
|
|||||||
if(APPLE)
|
if(APPLE)
|
||||||
find_program(PANDOC_EXE pandoc)
|
find_program(PANDOC_EXE pandoc)
|
||||||
if(PANDOC_EXE)
|
if(PANDOC_EXE)
|
||||||
execute_process(COMMAND ${PANDOC_EXE} ${CMAKE_SOURCE_DIR}/README.md -o
|
execute_process(COMMAND ${PANDOC_EXE} ${CMAKE_CURRENT_SOURCE_DIR}/README.md
|
||||||
${CMAKE_BINARY_DIR}/README.txt)
|
-o ${CMAKE_CURRENT_BINARY_DIR}/README.txt)
|
||||||
set(CPACK_RESOURCE_FILE_README ${CMAKE_BINARY_DIR}/README.txt)
|
set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_BINARY_DIR}/README.txt)
|
||||||
endif()
|
endif()
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_BINARY_DIR}/LICENSE.txt
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/LICENSE
|
||||||
COPYONLY)
|
${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt COPYONLY)
|
||||||
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_BINARY_DIR}/LICENSE.txt)
|
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
@@ -1621,28 +1621,31 @@ downLeftSpine:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the max version among all keys starting with the search path of n +
|
// Return whether or not the max version among all keys starting with the search
|
||||||
// [child], where child in (begin, end). Does not account for the range version
|
// path of n + [child], where child in (begin, end) is <= readVersion. Does not
|
||||||
// of firstGt(searchpath(n) + [end - 1])
|
// account for the range version of firstGt(searchpath(n) + [end - 1])
|
||||||
int64_t maxBetweenExclusive(Node *n, int begin, int end) {
|
bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
||||||
|
int64_t readVersion) {
|
||||||
assume(-1 <= begin);
|
assume(-1 <= begin);
|
||||||
assume(begin <= 256);
|
assume(begin <= 256);
|
||||||
assume(-1 <= end);
|
assume(-1 <= end);
|
||||||
assume(end <= 256);
|
assume(end <= 256);
|
||||||
assume(begin < end);
|
assume(begin < end);
|
||||||
int64_t result = std::numeric_limits<int64_t>::lowest();
|
|
||||||
{
|
{
|
||||||
int c = getChildGeq(n, begin + 1);
|
int c = getChildGeq(n, begin + 1);
|
||||||
if (c >= 0 && c < end) {
|
if (c >= 0 && c < end) {
|
||||||
auto *child = getChildExists(n, c);
|
auto *child = getChildExists(n, c);
|
||||||
if (child->entryPresent) {
|
if (child->entryPresent) {
|
||||||
result = std::max(result, child->entry.rangeVersion);
|
if (!(child->entry.rangeVersion <= readVersion)) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
begin = c;
|
begin = c;
|
||||||
} else {
|
} else {
|
||||||
return result;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (n->getType()) {
|
switch (n->getType()) {
|
||||||
case Type_Node0: // GCOVR_EXCL_LINE
|
case Type_Node0: // GCOVR_EXCL_LINE
|
||||||
// We would have returned above, after not finding a child
|
// We would have returned above, after not finding a child
|
||||||
@@ -1651,7 +1654,9 @@ int64_t maxBetweenExclusive(Node *n, int begin, int end) {
|
|||||||
auto *self = static_cast<Node3 *>(n);
|
auto *self = static_cast<Node3 *>(n);
|
||||||
for (int i = 0; i < self->numChildren && self->index[i] < end; ++i) {
|
for (int i = 0; i < self->numChildren && self->index[i] < end; ++i) {
|
||||||
if (begin <= self->index[i]) {
|
if (begin <= self->index[i]) {
|
||||||
result = std::max(result, self->children[i].childMaxVersion);
|
if (self->children[i].childMaxVersion > readVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -1659,37 +1664,45 @@ int64_t maxBetweenExclusive(Node *n, int begin, int end) {
|
|||||||
auto *self = static_cast<Node16 *>(n);
|
auto *self = static_cast<Node16 *>(n);
|
||||||
for (int i = 0; i < self->numChildren && self->index[i] < end; ++i) {
|
for (int i = 0; i < self->numChildren && self->index[i] < end; ++i) {
|
||||||
if (begin <= self->index[i]) {
|
if (begin <= self->index[i]) {
|
||||||
result = std::max(result, self->children[i].childMaxVersion);
|
if (self->children[i].childMaxVersion > readVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case Type_Node48: {
|
case Type_Node48: {
|
||||||
|
bool conflict[256] = {};
|
||||||
auto *self = static_cast<Node48 *>(n);
|
auto *self = static_cast<Node48 *>(n);
|
||||||
self->bitSet.forEachInRange(
|
self->bitSet.forEachInRange(
|
||||||
[&](int i) {
|
[&](int i) {
|
||||||
result =
|
conflict[i] =
|
||||||
std::max(result, self->children[self->index[i]].childMaxVersion);
|
self->children[self->index[i]].childMaxVersion > readVersion;
|
||||||
},
|
},
|
||||||
begin, end);
|
begin, end);
|
||||||
break;
|
bool result = true;
|
||||||
|
for (auto c : conflict) {
|
||||||
|
result &= !c;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
case Type_Node256: {
|
case Type_Node256: {
|
||||||
|
bool conflict[256] = {};
|
||||||
auto *self = static_cast<Node256 *>(n);
|
auto *self = static_cast<Node256 *>(n);
|
||||||
self->bitSet.forEachInRange(
|
self->bitSet.forEachInRange(
|
||||||
[&](int i) {
|
[&](int i) {
|
||||||
result = std::max(result, self->children[i].childMaxVersion);
|
conflict[i] = self->children[i].childMaxVersion > readVersion;
|
||||||
},
|
},
|
||||||
begin, end);
|
begin, end);
|
||||||
break;
|
bool result = true;
|
||||||
|
for (auto c : conflict) {
|
||||||
|
result &= !c;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
default: // GCOVR_EXCL_LINE
|
default: // GCOVR_EXCL_LINE
|
||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
}
|
}
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
return true;
|
||||||
fprintf(stderr, "At `%s', max version in (%02x, %02x) is %" PRId64 "\n",
|
|
||||||
getSearchPathPrintable(n).c_str(), begin, end, result);
|
|
||||||
#endif
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<uint8_t> getSearchPath(Arena &arena, Node *n) {
|
Vector<uint8_t> getSearchPath(Arena &arena, Node *n) {
|
||||||
@@ -1722,7 +1735,7 @@ bool checkRangeStartsWith(Node *n, std::span<const uint8_t> key, int begin,
|
|||||||
#endif
|
#endif
|
||||||
auto remaining = key;
|
auto remaining = key;
|
||||||
if (remaining.size() == 0) {
|
if (remaining.size() == 0) {
|
||||||
return maxBetweenExclusive(n, begin, end) <= readVersion;
|
return checkMaxBetweenExclusive(n, begin, end, readVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
@@ -1821,7 +1834,7 @@ struct CheckRangeLeftSide {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (searchPathLen >= prefixLen) {
|
if (searchPathLen >= prefixLen) {
|
||||||
if (maxBetweenExclusive(n, remaining[0], 256) > readVersion) {
|
if (!checkMaxBetweenExclusive(n, remaining[0], 256, readVersion)) {
|
||||||
ok = false;
|
ok = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1963,7 +1976,7 @@ struct CheckRangeRightSide {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxBetweenExclusive(n, -1, remaining[0]) > readVersion) {
|
if (!checkMaxBetweenExclusive(n, -1, remaining[0], readVersion)) {
|
||||||
ok = false;
|
ok = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
38
README.md
38
README.md
@@ -58,27 +58,29 @@ Performance counters:
|
|||||||
|
|
||||||
## Skip list
|
## Skip list
|
||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark |
|
| ns/op | op/s | err% | total | benchmark
|
||||||
| -----: | -----------: | ---: | ----: | :---------------------------------- |
|
|--------------------:|--------------------:|--------:|----------:|:----------
|
||||||
| 246.99 | 4,048,700.59 | 0.2% | 0.01 | `point reads` |
|
| 255.20 | 3,918,570.44 | 0.1% | 0.01 | `point reads`
|
||||||
| 260.16 | 3,843,784.65 | 0.1% | 0.01 | `prefix reads` |
|
| 269.35 | 3,712,633.44 | 0.8% | 0.01 | `prefix reads`
|
||||||
| 493.35 | 2,026,953.19 | 0.1% | 0.01 | `range reads` |
|
| 502.72 | 1,989,186.40 | 0.4% | 0.01 | `range reads`
|
||||||
| 462.05 | 2,164,289.23 | 0.6% | 0.01 | `point writes` |
|
| 456.85 | 2,188,902.27 | 0.6% | 0.01 | `point writes`
|
||||||
| 448.19 | 2,231,205.25 | 0.9% | 0.01 | `prefix writes` |
|
| 444.81 | 2,248,148.60 | 0.7% | 0.01 | `prefix writes`
|
||||||
| 255.83 | 3,908,845.72 | 1.5% | 0.02 | `range writes` |
|
| 250.00 | 4,000,000.00 | 1.7% | 0.02 | `range writes`
|
||||||
| 582.63 | 1,716,349.02 | 1.3% | 0.01 | `monotonic increasing point writes` |
|
| 566.51 | 1,765,186.07 | 0.5% | 0.01 | `monotonic increasing point writes`
|
||||||
|
| 226.41 | 4,416,703.74 | 0.5% | 0.01 | `worst case for radix tree`
|
||||||
|
|
||||||
## Radix tree (this implementation)
|
## Radix tree (this implementation)
|
||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark |
|
| ns/op | op/s | err% | total | benchmark
|
||||||
| -----: | ------------: | ---: | ----: | :---------------------------------- |
|
|--------------------:|--------------------:|--------:|----------:|:----------
|
||||||
| 19.42 | 51,483,206.67 | 0.3% | 0.01 | `point reads` |
|
| 19.60 | 51,025,020.51 | 0.1% | 0.01 | `point reads`
|
||||||
| 58.43 | 17,115,612.57 | 0.1% | 0.01 | `prefix reads` |
|
| 55.62 | 17,980,734.93 | 0.7% | 0.01 | `prefix reads`
|
||||||
| 216.09 | 4,627,766.60 | 0.2% | 0.01 | `range reads` |
|
| 174.86 | 5,718,896.02 | 0.4% | 0.01 | `range reads`
|
||||||
| 28.35 | 35,267,567.72 | 0.2% | 0.01 | `point writes` |
|
| 28.27 | 35,372,166.39 | 0.1% | 0.01 | `point writes`
|
||||||
| 43.43 | 23,026,226.17 | 0.2% | 0.01 | `prefix writes` |
|
| 43.85 | 22,804,171.49 | 0.5% | 0.01 | `prefix writes`
|
||||||
| 50.00 | 20,000,000.00 | 0.0% | 0.01 | `range writes` |
|
| 49.59 | 20,165,355.92 | 0.9% | 0.01 | `range writes`
|
||||||
| 92.38 | 10,824,863.69 | 4.1% | 0.01 | `monotonic increasing point writes` |
|
| 92.04 | 10,864,732.33 | 3.6% | 0.01 | `monotonic increasing point writes`
|
||||||
|
| 6,937.00 | 144,154.53 | 0.2% | 0.01 | `worst case for radix tree`
|
||||||
|
|
||||||
# "Real data" test
|
# "Real data" test
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
___stack_chk_fail
|
||||||
|
___stack_chk_guard
|
||||||
__tlv_bootstrap
|
__tlv_bootstrap
|
||||||
_abort
|
_abort
|
||||||
_bzero
|
_bzero
|
||||||
|
@@ -115,7 +115,9 @@ class ConflictSet:
|
|||||||
|
|
||||||
def check(self, *reads: ReadRange) -> list[Result]:
|
def check(self, *reads: ReadRange) -> list[Result]:
|
||||||
r = (ctypes.c_int * len(reads))()
|
r = (ctypes.c_int * len(reads))()
|
||||||
self._lib.ConflictSet_check(self.p, *reads, r, 1)
|
self._lib.ConflictSet_check(
|
||||||
|
self.p, (ReadRange * len(reads))(*reads), r, len(reads)
|
||||||
|
)
|
||||||
return [Result(x) for x in r]
|
return [Result(x) for x in r]
|
||||||
|
|
||||||
def setOldestVersion(self, version: int) -> None:
|
def setOldestVersion(self, version: int) -> None:
|
||||||
|
1
paper/.gitignore
vendored
1
paper/.gitignore
vendored
@@ -10,3 +10,4 @@
|
|||||||
*.pdf
|
*.pdf
|
||||||
*.run.xml
|
*.run.xml
|
||||||
*.synctex.gz
|
*.synctex.gz
|
||||||
|
version.txt
|
||||||
|
@@ -6,4 +6,4 @@ paper.pdf: paper.tex $(wildcard *.tikz)
|
|||||||
latexmk -pdf
|
latexmk -pdf
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
xargs -I '{}' bash -c "rm -f {}" < .gitignore
|
grep -v version.txt .gitignore |xargs -I '{}' bash -c "rm -f {}"
|
||||||
|
@@ -8,9 +8,11 @@
|
|||||||
\usepackage[edges]{forest}
|
\usepackage[edges]{forest}
|
||||||
\usepackage{amsmath}
|
\usepackage{amsmath}
|
||||||
|
|
||||||
|
\input{version.txt}
|
||||||
|
|
||||||
\title{ARTful Concurrency Control à la FoundationDB}
|
\title{ARTful Concurrency Control à la FoundationDB}
|
||||||
\author{Andrew Noyes \thanks{\href{mailto:andrew@weaselab.dev}{andrew@weaselab.dev}}}
|
\author{Andrew Noyes \thanks{\href{mailto:andrew@weaselab.dev}{andrew@weaselab.dev}}}
|
||||||
\date{}
|
\date{Version \versionnumber}
|
||||||
|
|
||||||
\usepackage{biblatex}
|
\usepackage{biblatex}
|
||||||
\bibliography{bibliography}
|
\bibliography{bibliography}
|
||||||
|
@@ -53,7 +53,7 @@ def test_conflict_set():
|
|||||||
assert cs.getBytes() - before > 0
|
assert cs.getBytes() - before > 0
|
||||||
assert cs.check(read(0, key)) == [Result.CONFLICT]
|
assert cs.check(read(0, key)) == [Result.CONFLICT]
|
||||||
cs.setOldestVersion(1)
|
cs.setOldestVersion(1)
|
||||||
assert cs.check(read(0, key)) == [Result.TOO_OLD]
|
assert cs.check(read(0, key), read(1, key)) == [Result.TOO_OLD, Result.COMMIT]
|
||||||
|
|
||||||
|
|
||||||
def test_inner_full_words():
|
def test_inner_full_words():
|
||||||
|
1
version.txt.in
Normal file
1
version.txt.in
Normal file
@@ -0,0 +1 @@
|
|||||||
|
\providecommand{\versionnumber}{@PROJECT_VERSION@}
|
Reference in New Issue
Block a user