Compare commits
26 Commits
v0.0.2
...
08ed17f47b
Author | SHA1 | Date | |
---|---|---|---|
08ed17f47b | |||
76a45f16ad | |||
c15d296432 | |||
64a98c529c | |||
ed1388ed21 | |||
309d315956 | |||
eab2e46a56 | |||
85db1a8786 | |||
717f9d6829 | |||
fd93300ce8 | |||
b7e16b31ff | |||
a324d31518 | |||
fdb05e0e33 | |||
7c27d4a972 | |||
738de01cb4 | |||
325cab6a95 | |||
0b2821941a | |||
a40b5dcd74 | |||
193b1926ff | |||
1c900c5a8c | |||
90fdcdd51a | |||
eb3f6823eb | |||
1534e10b75 | |||
3c100ccee8 | |||
5cf45d1c35 | |||
4f97932893 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
.cache
|
.cache
|
||||||
|
__pycache__
|
||||||
build
|
build
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
rev: 6d365699efc33b1b432eab5b4ae331a19e1857de # frozen: v18.1.2
|
rev: 6d365699efc33b1b432eab5b4ae331a19e1857de # frozen: v18.1.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: clang-format
|
- id: clang-format
|
||||||
exclude: ".*third_party/.*"
|
exclude: ".*third_party/.*"
|
||||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||||
rev: e2c2116d86a80e72e7146a06e68b7c228afc6319 # frozen: v0.6.13
|
rev: e2c2116d86a80e72e7146a06e68b7c228afc6319 # frozen: v0.6.13
|
||||||
hooks:
|
hooks:
|
||||||
- id: cmake-format
|
- id: cmake-format
|
||||||
- repo: local
|
- repo: local
|
||||||
@@ -13,7 +13,7 @@ repos:
|
|||||||
- id: debug verbose check
|
- id: debug verbose check
|
||||||
name: disallow checking in DEBUG_VERBOSE=1
|
name: disallow checking in DEBUG_VERBOSE=1
|
||||||
description: disallow checking in DEBUG_VERBOSE=1
|
description: disallow checking in DEBUG_VERBOSE=1
|
||||||
entry: '^#define DEBUG_VERBOSE 1$'
|
entry: "^#define DEBUG_VERBOSE 1$"
|
||||||
language: pygrep
|
language: pygrep
|
||||||
types: [c++]
|
types: [c++]
|
||||||
- repo: local
|
- repo: local
|
||||||
@@ -21,10 +21,14 @@ repos:
|
|||||||
- id: debug verbose check
|
- id: debug verbose check
|
||||||
name: disallow checking in SHOW_MEMORY=1
|
name: disallow checking in SHOW_MEMORY=1
|
||||||
description: disallow checking in SHOW_MEMORY=1
|
description: disallow checking in SHOW_MEMORY=1
|
||||||
entry: '^#define SHOW_MEMORY 1$'
|
entry: "^#define SHOW_MEMORY 1$"
|
||||||
language: pygrep
|
language: pygrep
|
||||||
types: [c++]
|
types: [c++]
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||||
rev: a23f6b85d0fdd5bb9d564e2579e678033debbdff # frozen: v0.10.0.1
|
rev: a23f6b85d0fdd5bb9d564e2579e678033debbdff # frozen: v0.10.0.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 552baf822992936134cbd31a38f69c8cfe7c0f05 # frozen: 24.3.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
project(
|
project(
|
||||||
conflict-set
|
conflict-set
|
||||||
VERSION 0.0.2
|
VERSION 0.0.4
|
||||||
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})
|
||||||
|
|
||||||
include(CMakePushCheckState)
|
include(CMakePushCheckState)
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
include(CheckIncludeFileCXX)
|
include(CheckIncludeFileCXX)
|
||||||
@@ -128,7 +130,7 @@ endif()
|
|||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${PROJECT_NAME}-static
|
TARGET ${PROJECT_NAME}-static
|
||||||
PRE_BUILD
|
PRE_LINK
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/privatize_symbols_macos.sh
|
COMMAND ${CMAKE_SOURCE_DIR}/privatize_symbols_macos.sh
|
||||||
$<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
$<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
||||||
else()
|
else()
|
||||||
@@ -259,14 +261,22 @@ if(BUILD_TESTING)
|
|||||||
|
|
||||||
# scripted tests. Written manually to fill in anything libfuzzer couldn't
|
# scripted tests. Written manually to fill in anything libfuzzer couldn't
|
||||||
# find.
|
# find.
|
||||||
add_executable(script_test ScriptTest.cpp)
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
target_compile_options(script_test PRIVATE ${TEST_FLAGS})
|
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||||
target_link_libraries(script_test PRIVATE ${PROJECT_NAME})
|
set_property(
|
||||||
file(GLOB SCRIPT_TESTS ${CMAKE_SOURCE_DIR}/script_tests/*)
|
DIRECTORY
|
||||||
foreach(TEST ${SCRIPT_TESTS})
|
APPEND
|
||||||
get_filename_component(name ${TEST} NAME)
|
PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/test_conflict_set.py)
|
||||||
add_test(NAME conflict_set_script_${name} COMMAND script_test ${TEST})
|
execute_process(
|
||||||
endforeach()
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/test_conflict_set.py
|
||||||
|
list OUTPUT_VARIABLE SCRIPT_TESTS)
|
||||||
|
foreach(TEST ${SCRIPT_TESTS})
|
||||||
|
add_test(
|
||||||
|
NAME script_test_${TEST}
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/test_conflict_set.py
|
||||||
|
test ${TEST} --build-dir ${CMAKE_BINARY_DIR})
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
find_program(VALGRIND_EXE valgrind)
|
find_program(VALGRIND_EXE valgrind)
|
||||||
if(VALGRIND_EXE AND NOT CMAKE_CROSSCOMPILING)
|
if(VALGRIND_EXE AND NOT CMAKE_CROSSCOMPILING)
|
||||||
@@ -333,6 +343,7 @@ endif()
|
|||||||
# packaging
|
# packaging
|
||||||
|
|
||||||
set(CPACK_PACKAGE_CONTACT andrew@weaselab.dev)
|
set(CPACK_PACKAGE_CONTACT andrew@weaselab.dev)
|
||||||
|
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME all)
|
||||||
|
|
||||||
set(CPACK_PACKAGE_VENDOR "Weaselab")
|
set(CPACK_PACKAGE_VENDOR "Weaselab")
|
||||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||||
@@ -353,6 +364,20 @@ else()
|
|||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.14)")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.14)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# macos
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
|
||||||
|
if(APPLE)
|
||||||
|
find_program(PANDOC_EXE pandoc)
|
||||||
|
if(PANDOC_EXE)
|
||||||
|
execute_process(COMMAND ${PANDOC_EXE} ${CMAKE_SOURCE_DIR}/README.md -o
|
||||||
|
${CMAKE_BINARY_DIR}/README.txt)
|
||||||
|
set(CPACK_RESOURCE_FILE_README ${CMAKE_BINARY_DIR}/README.txt)
|
||||||
|
endif()
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_BINARY_DIR}/LICENSE.txt
|
||||||
|
COPYONLY)
|
||||||
|
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_BINARY_DIR}/LICENSE.txt)
|
||||||
|
endif()
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
@@ -377,8 +402,11 @@ install(
|
|||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
install(DIRECTORY include/
|
install(DIRECTORY include/
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
||||||
|
|
||||||
install(EXPORT ${PROJECT_NAME}Config
|
install(EXPORT ${PROJECT_NAME}Config
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
|
||||||
|
|
||||||
|
cpack_add_component(all)
|
||||||
|
@@ -770,7 +770,7 @@ template <class NodeT> int getChildGeqSimd(NodeT *self, int child) {
|
|||||||
|
|
||||||
// cachegrind says the plain loop is fewer instructions and more mis-predicted
|
// cachegrind says the plain loop is fewer instructions and more mis-predicted
|
||||||
// branches. Microbenchmark says plain loop is faster. It's written in this
|
// branches. Microbenchmark says plain loop is faster. It's written in this
|
||||||
// weird "generic" way though in case someday we can use the simd
|
// weird "generic" way though so that someday we can use the simd
|
||||||
// implementation easily if we want.
|
// implementation easily if we want.
|
||||||
if constexpr (std::is_same_v<NodeT, Node3>) {
|
if constexpr (std::is_same_v<NodeT, Node3>) {
|
||||||
Node3 *n = (Node3 *)self;
|
Node3 *n = (Node3 *)self;
|
||||||
@@ -1146,8 +1146,14 @@ void freeAndMakeCapacityAtLeast(Node *&self, int capacity,
|
|||||||
// capacity.
|
// capacity.
|
||||||
void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
|
void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
|
||||||
ConflictSet::Impl *impl) {
|
ConflictSet::Impl *impl) {
|
||||||
|
|
||||||
const int maxCapacity =
|
const int maxCapacity =
|
||||||
(self->numChildren + int(self->entryPresent)) * (self->partialKeyLen + 1);
|
(self->numChildren + int(self->entryPresent)) * (self->partialKeyLen + 1);
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr, "maybeDecreaseCapacity: current: %d, max: %d, key: %s\n",
|
||||||
|
self->getCapacity(), maxCapacity,
|
||||||
|
getSearchPathPrintable(self).c_str());
|
||||||
|
#endif
|
||||||
if (self->getCapacity() <= maxCapacity) {
|
if (self->getCapacity() <= maxCapacity) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2510,6 +2516,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
explicit Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) {
|
explicit Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) {
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
fprintf(stderr, "radix_tree: create\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Insert ""
|
// Insert ""
|
||||||
root = allocators.node0.allocate(0);
|
root = allocators.node0.allocate(0);
|
||||||
root->numChildren = 0;
|
root->numChildren = 0;
|
||||||
@@ -2524,7 +2534,12 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
root->entry.pointVersion = oldestVersion;
|
root->entry.pointVersion = oldestVersion;
|
||||||
root->entry.rangeVersion = oldestVersion;
|
root->entry.rangeVersion = oldestVersion;
|
||||||
}
|
}
|
||||||
~Impl() { destroyTree(root); }
|
~Impl() {
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
fprintf(stderr, "radix_tree: destroy\n");
|
||||||
|
#endif
|
||||||
|
destroyTree(root);
|
||||||
|
}
|
||||||
|
|
||||||
NodeAllocators allocators;
|
NodeAllocators allocators;
|
||||||
|
|
||||||
@@ -2647,17 +2662,27 @@ ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
|||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
||||||
int64_t writeVersion) {
|
int64_t writeVersion) {
|
||||||
auto *impl = ((ConflictSet::Impl *)cs);
|
auto *impl = (ConflictSet::Impl *)cs;
|
||||||
mallocBytesDelta = 0;
|
mallocBytesDelta = 0;
|
||||||
impl->addWrites(writes, count, writeVersion);
|
impl->addWrites(writes, count, writeVersion);
|
||||||
impl->totalBytes += mallocBytesDelta;
|
impl->totalBytes += mallocBytesDelta;
|
||||||
|
#if SHOW_MEMORY
|
||||||
|
if (impl->totalBytes != mallocBytes) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
||||||
auto *impl = ((ConflictSet::Impl *)cs);
|
auto *impl = (ConflictSet::Impl *)cs;
|
||||||
mallocBytesDelta = 0;
|
mallocBytesDelta = 0;
|
||||||
impl->setOldestVersion(oldestVersion);
|
impl->setOldestVersion(oldestVersion);
|
||||||
impl->totalBytes += mallocBytesDelta;
|
impl->totalBytes += mallocBytesDelta;
|
||||||
|
#if SHOW_MEMORY
|
||||||
|
if (impl->totalBytes != mallocBytes) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void *
|
__attribute__((__visibility__("default"))) void *
|
||||||
ConflictSet_create(int64_t oldestVersion) {
|
ConflictSet_create(int64_t oldestVersion) {
|
||||||
@@ -2995,6 +3020,7 @@ void removeKey(Node *n) {
|
|||||||
|
|
||||||
struct __attribute__((visibility("default"))) PeakPrinter {
|
struct __attribute__((visibility("default"))) PeakPrinter {
|
||||||
~PeakPrinter() {
|
~PeakPrinter() {
|
||||||
|
printf("--- radix_tree ---\n");
|
||||||
printf("malloc bytes: %g\n", double(mallocBytes));
|
printf("malloc bytes: %g\n", double(mallocBytes));
|
||||||
printf("Peak malloc bytes: %g\n", double(peakMallocBytes));
|
printf("Peak malloc bytes: %g\n", double(peakMallocBytes));
|
||||||
printf("Node bytes: %g\n", double(nodeBytes));
|
printf("Node bytes: %g\n", double(nodeBytes));
|
||||||
|
3
Jenkinsfile
vendored
3
Jenkinsfile
vendored
@@ -111,6 +111,9 @@ pipeline {
|
|||||||
gcovr -f ConflictSet.cpp --cobertura > build/coverage.xml
|
gcovr -f ConflictSet.cpp --cobertura > build/coverage.xml
|
||||||
'''
|
'''
|
||||||
cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: 'build/coverage.xml', conditionalCoverageTargets: '70, 0, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '80, 0, 0', maxNumberOfBuilds: 0, methodCoverageTargets: '80, 0, 0', onlyStable: false, sourceEncoding: 'ASCII', zoomCoverageChart: false
|
cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: 'build/coverage.xml', conditionalCoverageTargets: '70, 0, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '80, 0, 0', maxNumberOfBuilds: 0, methodCoverageTargets: '80, 0, 0', onlyStable: false, sourceEncoding: 'ASCII', zoomCoverageChart: false
|
||||||
|
sh '''
|
||||||
|
gcovr -f ConflictSet.cpp --fail-under-line 100 > /dev/null
|
||||||
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
README.md
36
README.md
@@ -58,27 +58,27 @@ 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`
|
| 246.99 | 4,048,700.59 | 0.2% | 0.01 | `point reads` |
|
||||||
| 260.16 | 3,843,784.65 | 0.1% | 0.01 | `prefix reads`
|
| 260.16 | 3,843,784.65 | 0.1% | 0.01 | `prefix reads` |
|
||||||
| 493.35 | 2,026,953.19 | 0.1% | 0.01 | `range reads`
|
| 493.35 | 2,026,953.19 | 0.1% | 0.01 | `range reads` |
|
||||||
| 462.05 | 2,164,289.23 | 0.6% | 0.01 | `point writes`
|
| 462.05 | 2,164,289.23 | 0.6% | 0.01 | `point writes` |
|
||||||
| 448.19 | 2,231,205.25 | 0.9% | 0.01 | `prefix writes`
|
| 448.19 | 2,231,205.25 | 0.9% | 0.01 | `prefix writes` |
|
||||||
| 255.83 | 3,908,845.72 | 1.5% | 0.02 | `range writes`
|
| 255.83 | 3,908,845.72 | 1.5% | 0.02 | `range writes` |
|
||||||
| 582.63 | 1,716,349.02 | 1.3% | 0.01 | `monotonic increasing point writes`
|
| 582.63 | 1,716,349.02 | 1.3% | 0.01 | `monotonic increasing point writes` |
|
||||||
|
|
||||||
## 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.42 | 51,483,206.67 | 0.3% | 0.01 | `point reads` |
|
||||||
| 58.43 | 17,115,612.57 | 0.1% | 0.01 | `prefix reads`
|
| 58.43 | 17,115,612.57 | 0.1% | 0.01 | `prefix reads` |
|
||||||
| 216.09 | 4,627,766.60 | 0.2% | 0.01 | `range reads`
|
| 216.09 | 4,627,766.60 | 0.2% | 0.01 | `range reads` |
|
||||||
| 28.35 | 35,267,567.72 | 0.2% | 0.01 | `point writes`
|
| 28.35 | 35,267,567.72 | 0.2% | 0.01 | `point writes` |
|
||||||
| 43.43 | 23,026,226.17 | 0.2% | 0.01 | `prefix writes`
|
| 43.43 | 23,026,226.17 | 0.2% | 0.01 | `prefix writes` |
|
||||||
| 50.00 | 20,000,000.00 | 0.0% | 0.01 | `range writes`
|
| 50.00 | 20,000,000.00 | 0.0% | 0.01 | `range writes` |
|
||||||
| 92.38 | 10,824,863.69 | 4.1% | 0.01 | `monotonic increasing point writes`
|
| 92.38 | 10,824,863.69 | 4.1% | 0.01 | `monotonic increasing point writes` |
|
||||||
|
|
||||||
# "Real data" test
|
# "Real data" test
|
||||||
|
|
||||||
|
155
ScriptTest.cpp
155
ScriptTest.cpp
@@ -1,155 +0,0 @@
|
|||||||
#include "Internal.h"
|
|
||||||
#include <ConflictSet.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstring>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
inline size_t getPageSize() {
|
|
||||||
static size_t kPageSize = sysconf(_SC_PAGESIZE);
|
|
||||||
return kPageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper for rounding up to page size (or some other alignment)
|
|
||||||
constexpr inline size_t rightAlign(size_t offset, size_t alignment) {
|
|
||||||
return offset % alignment == 0 ? offset
|
|
||||||
: ((offset / alignment) + 1) * alignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
using StringView = std::basic_string_view<uint8_t>;
|
|
||||||
|
|
||||||
inline StringView operator"" _v(const char *str, size_t size) {
|
|
||||||
return {reinterpret_cast<const uint8_t *>(str), size};
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
|
||||||
|
|
||||||
ConflictSet cs{0};
|
|
||||||
ReferenceImpl ref{0};
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
|
||||||
int fd = open(argv[i], O_RDONLY);
|
|
||||||
struct stat st;
|
|
||||||
if (fstat(fd, &st) == -1) {
|
|
||||||
int err = errno;
|
|
||||||
fprintf(stderr, "stat error %s - %s\n", argv[i], strerror(err));
|
|
||||||
fflush(stderr);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t size = rightAlign(st.st_size, getPageSize());
|
|
||||||
const uint8_t *begin =
|
|
||||||
(uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
||||||
madvise((void *)begin, size, MADV_SEQUENTIAL);
|
|
||||||
auto *const mapOriginal = begin;
|
|
||||||
const auto sizeOriginal = size;
|
|
||||||
|
|
||||||
StringView b;
|
|
||||||
StringView e;
|
|
||||||
int64_t v = 0;
|
|
||||||
int64_t lastWriteVersion = 0;
|
|
||||||
int64_t lastOldestVersion = 0;
|
|
||||||
std::vector<ConflictSet::WriteRange> writeRanges;
|
|
||||||
std::vector<ConflictSet::ReadRange> readRanges;
|
|
||||||
std::vector<ConflictSet::Result> results;
|
|
||||||
|
|
||||||
for (uint8_t *end = (uint8_t *)memchr(begin, '\n', size); end != nullptr;) {
|
|
||||||
StringView line{begin, static_cast<size_t>(end - begin)};
|
|
||||||
size -= end - begin + 1;
|
|
||||||
begin = end + 1;
|
|
||||||
end = (uint8_t *)memchr(begin, '\n', size);
|
|
||||||
|
|
||||||
if (line.starts_with("begin"_v)) {
|
|
||||||
b = line.substr("begin "_v.size(), line.size());
|
|
||||||
printf("b <- %.*s\n", int(b.size()), b.data());
|
|
||||||
} else if (line.starts_with("end"_v)) {
|
|
||||||
e = line.substr("end "_v.size(), line.size());
|
|
||||||
printf("e <- %.*s\n", int(e.size()), e.data());
|
|
||||||
} else if (line.starts_with("version"_v)) {
|
|
||||||
line = line.substr("version "_v.size(), line.size());
|
|
||||||
v = 0;
|
|
||||||
for (auto c : line) {
|
|
||||||
v = v * 10 + int(c) - int('0');
|
|
||||||
}
|
|
||||||
printf("v <- %" PRId64 "\n", v);
|
|
||||||
} else if (line.starts_with("pointread"_v)) {
|
|
||||||
printf("pointread\n");
|
|
||||||
ConflictSet::ReadRange r;
|
|
||||||
r.begin.p = b.data();
|
|
||||||
r.begin.len = b.size();
|
|
||||||
r.end.len = 0;
|
|
||||||
r.readVersion = v;
|
|
||||||
readRanges.push_back(r);
|
|
||||||
} else if (line.starts_with("pointwrite"_v)) {
|
|
||||||
printf("pointwrite\n");
|
|
||||||
assert(writeRanges.empty() ||
|
|
||||||
(writeRanges.back().end.len == 0 ? writeRanges.back().begin
|
|
||||||
: writeRanges.back().end) < b);
|
|
||||||
ConflictSet::WriteRange w;
|
|
||||||
w.begin.p = b.data();
|
|
||||||
w.begin.len = b.size();
|
|
||||||
w.end.len = 0;
|
|
||||||
writeRanges.push_back(w);
|
|
||||||
} else if (line.starts_with("rangeread"_v)) {
|
|
||||||
printf("rangeread\n");
|
|
||||||
ConflictSet::ReadRange r;
|
|
||||||
r.begin.p = b.data();
|
|
||||||
r.begin.len = b.size();
|
|
||||||
r.end.p = e.data();
|
|
||||||
r.end.len = e.size();
|
|
||||||
r.readVersion = v;
|
|
||||||
readRanges.push_back(r);
|
|
||||||
} else if (line.starts_with("rangewrite"_v)) {
|
|
||||||
printf("rangewrite\n");
|
|
||||||
assert(b < e);
|
|
||||||
assert(writeRanges.empty() ||
|
|
||||||
(writeRanges.back().end.len == 0 ? writeRanges.back().begin
|
|
||||||
: writeRanges.back().end) < b);
|
|
||||||
ConflictSet::WriteRange w;
|
|
||||||
w.begin.p = b.data();
|
|
||||||
w.begin.len = b.size();
|
|
||||||
w.end.p = e.data();
|
|
||||||
w.end.len = e.size();
|
|
||||||
writeRanges.push_back(w);
|
|
||||||
} else if (line.starts_with("check"_v)) {
|
|
||||||
printf("check\n");
|
|
||||||
Arena arena;
|
|
||||||
auto *expected = new (arena) ConflictSet::Result[readRanges.size()];
|
|
||||||
auto *actual = new (arena) ConflictSet::Result[readRanges.size()];
|
|
||||||
ref.check(readRanges.data(), expected, readRanges.size());
|
|
||||||
cs.check(readRanges.data(), actual, readRanges.size());
|
|
||||||
for (int i = 0; i < int(readRanges.size()); ++i) {
|
|
||||||
if (expected[i] != actual[i]) {
|
|
||||||
fprintf(stderr, "Expected %s, got %s at index %d\n",
|
|
||||||
resultToStr(expected[i]), resultToStr(actual[i]), i);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readRanges = {};
|
|
||||||
} else if (line.starts_with("addwrites"_v)) {
|
|
||||||
printf("addwrites\n");
|
|
||||||
assert(v > lastWriteVersion);
|
|
||||||
lastWriteVersion = v;
|
|
||||||
cs.addWrites(writeRanges.data(), writeRanges.size(), v);
|
|
||||||
ref.addWrites(writeRanges.data(), writeRanges.size(), v);
|
|
||||||
writeRanges = {};
|
|
||||||
} else if (line.starts_with("setoldest"_v)) {
|
|
||||||
printf("setoldest\n");
|
|
||||||
assert(v > lastOldestVersion);
|
|
||||||
lastOldestVersion = v;
|
|
||||||
cs.setOldestVersion(v);
|
|
||||||
ref.setOldestVersion(v);
|
|
||||||
} else if (line.empty() || line.starts_with(";"_v)) {
|
|
||||||
// skip
|
|
||||||
} else {
|
|
||||||
printf("Unrecognized line: %.*s\n", int(line.size()), line.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
munmap((void *)mapOriginal, sizeOriginal);
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
}
|
|
42
SkipList.cpp
42
SkipList.cpp
@@ -16,6 +16,8 @@
|
|||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* This source code is modified to compile outside of FoundationDB
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ConflictSet.h"
|
#include "ConflictSet.h"
|
||||||
@@ -268,13 +270,21 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
explicit SkipList(Version version = 0) {
|
explicit SkipList(Version version = 0) {
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
fprintf(stderr, "skip_list: create\n");
|
||||||
|
#endif
|
||||||
header = Node::create(StringRef(), MaxLevels - 1);
|
header = Node::create(StringRef(), MaxLevels - 1);
|
||||||
for (int l = 0; l < MaxLevels; l++) {
|
for (int l = 0; l < MaxLevels; l++) {
|
||||||
header->setNext(l, nullptr);
|
header->setNext(l, nullptr);
|
||||||
header->setMaxVersion(l, version);
|
header->setMaxVersion(l, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~SkipList() { destroy(); }
|
~SkipList() {
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
fprintf(stderr, "skip_list: destroy\n");
|
||||||
|
#endif
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
SkipList(SkipList &&other) noexcept : header(other.header) {
|
SkipList(SkipList &&other) noexcept : header(other.header) {
|
||||||
other.header = nullptr;
|
other.header = nullptr;
|
||||||
}
|
}
|
||||||
@@ -603,6 +613,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
for (int s = stripes - 1; s >= 0; s--) {
|
for (int s = stripes - 1; s >= 0; s--) {
|
||||||
for (int i = 0; i * 2 < ss; ++i) {
|
for (int i = 0; i * 2 < ss; ++i) {
|
||||||
const auto &w = writes[s * stripeSize / 2 + i];
|
const auto &w = writes[s * stripeSize / 2 + i];
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
printf("Write begin: %s\n", printable(w.begin).c_str());
|
||||||
|
fflush(stdout);
|
||||||
|
#endif
|
||||||
values[i * 2] = {w.begin.p, size_t(w.begin.len)};
|
values[i * 2] = {w.begin.p, size_t(w.begin.len)};
|
||||||
values[i * 2 + 1] = w.end.len > 0
|
values[i * 2 + 1] = w.end.len > 0
|
||||||
? StringRef{w.end.p, size_t(w.end.len)}
|
? StringRef{w.end.p, size_t(w.end.len)}
|
||||||
@@ -702,16 +716,35 @@ ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
|||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
||||||
int64_t writeVersion) {
|
int64_t writeVersion) {
|
||||||
((ConflictSet::Impl *)cs)->addWrites(writes, count, writeVersion);
|
auto *impl = (ConflictSet::Impl *)cs;
|
||||||
|
mallocBytesDelta = 0;
|
||||||
|
impl->addWrites(writes, count, writeVersion);
|
||||||
|
impl->totalBytes += mallocBytesDelta;
|
||||||
|
#if SHOW_MEMORY
|
||||||
|
if (impl->totalBytes != mallocBytes) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
||||||
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion);
|
auto *impl = (ConflictSet::Impl *)cs;
|
||||||
|
mallocBytesDelta = 0;
|
||||||
|
impl->setOldestVersion(oldestVersion);
|
||||||
|
impl->totalBytes += mallocBytesDelta;
|
||||||
|
#if SHOW_MEMORY
|
||||||
|
if (impl->totalBytes != mallocBytes) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void *
|
__attribute__((__visibility__("default"))) void *
|
||||||
ConflictSet_create(int64_t oldestVersion) {
|
ConflictSet_create(int64_t oldestVersion) {
|
||||||
return new (safe_malloc(sizeof(ConflictSet::Impl)))
|
mallocBytesDelta = 0;
|
||||||
|
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
|
||||||
ConflictSet::Impl{oldestVersion};
|
ConflictSet::Impl{oldestVersion};
|
||||||
|
result->totalBytes += mallocBytesDelta;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||||
using Impl = ConflictSet::Impl;
|
using Impl = ConflictSet::Impl;
|
||||||
@@ -728,6 +761,7 @@ ConflictSet_getBytes(void *cs) {
|
|||||||
#if SHOW_MEMORY
|
#if SHOW_MEMORY
|
||||||
struct __attribute__((visibility("default"))) PeakPrinter {
|
struct __attribute__((visibility("default"))) PeakPrinter {
|
||||||
~PeakPrinter() {
|
~PeakPrinter() {
|
||||||
|
printf("--- skip_list ---\n");
|
||||||
printf("malloc bytes: %g\n", double(mallocBytes));
|
printf("malloc bytes: %g\n", double(mallocBytes));
|
||||||
printf("Peak malloc bytes: %g\n", double(peakMallocBytes));
|
printf("Peak malloc bytes: %g\n", double(peakMallocBytes));
|
||||||
}
|
}
|
||||||
|
@@ -4,4 +4,5 @@ _bzero
|
|||||||
_free
|
_free
|
||||||
_malloc
|
_malloc
|
||||||
_memcpy
|
_memcpy
|
||||||
_memmove
|
_memmove
|
||||||
|
dyld_stub_binder
|
136
conflict_set.py
Normal file
136
conflict_set.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import ctypes
|
||||||
|
import enum
|
||||||
|
import os
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class _Key(ctypes.Structure):
|
||||||
|
_fields_ = [("p", ctypes.POINTER(ctypes.c_ubyte)), ("len", ctypes.c_int)]
|
||||||
|
|
||||||
|
|
||||||
|
class ReadRange(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("begin", _Key),
|
||||||
|
("end", _Key),
|
||||||
|
("readVersion", ctypes.c_int64),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class WriteRange(ctypes.Structure):
|
||||||
|
_fields_ = [("begin", _Key), ("end", _Key)]
|
||||||
|
|
||||||
|
|
||||||
|
class Result(enum.Enum):
|
||||||
|
COMMIT = 0
|
||||||
|
CONFLICT = 1
|
||||||
|
TOO_OLD = 2
|
||||||
|
|
||||||
|
|
||||||
|
def write(begin: bytes, end: Optional[bytes] = None) -> WriteRange:
|
||||||
|
b = (ctypes.c_ubyte * len(begin)).from_buffer(bytearray(begin))
|
||||||
|
|
||||||
|
if end is None:
|
||||||
|
e = (ctypes.c_ubyte * 0)()
|
||||||
|
else:
|
||||||
|
e = (ctypes.c_ubyte * len(end)).from_buffer(bytearray(end))
|
||||||
|
return WriteRange(_Key(b, len(b)), _Key(e, len(e)))
|
||||||
|
|
||||||
|
|
||||||
|
def read(version: int, begin: bytes, end: Optional[bytes] = None) -> ReadRange:
|
||||||
|
b = (ctypes.c_ubyte * len(begin)).from_buffer(bytearray(begin))
|
||||||
|
if end is None:
|
||||||
|
e = (ctypes.c_ubyte * 0)()
|
||||||
|
else:
|
||||||
|
e = (ctypes.c_ubyte * len(end)).from_buffer(bytearray(end))
|
||||||
|
return ReadRange(_Key(b, len(b)), _Key(e, len(e)), version)
|
||||||
|
|
||||||
|
|
||||||
|
class ConflictSet:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
version: int = 0,
|
||||||
|
build_dir: Optional[str] = None,
|
||||||
|
implementation: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
self._lib = None
|
||||||
|
if build_dir is None:
|
||||||
|
build_dir = os.path.dirname(__file__) + "/build"
|
||||||
|
if implementation is None:
|
||||||
|
implementation = "radix_tree"
|
||||||
|
for f in (
|
||||||
|
build_dir + "/" + implementation + "/libconflict-set.so.0",
|
||||||
|
os.path.dirname(__file__)
|
||||||
|
+ "/build/"
|
||||||
|
+ implementation
|
||||||
|
+ "/libconflict-set.0.dylib",
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
self._lib = ctypes.cdll.LoadLibrary(f)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self._lib is None:
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print(
|
||||||
|
"Could not find libconflict-set implementation " + implementation,
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_create.argtypes = (ctypes.c_int64,)
|
||||||
|
self._lib.ConflictSet_create.restype = ctypes.c_void_p
|
||||||
|
|
||||||
|
self._lib.ConflictSet_check.argtypes = (
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.POINTER(ReadRange),
|
||||||
|
ctypes.POINTER(ctypes.c_int),
|
||||||
|
ctypes.c_int,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_addWrites.argtypes = (
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.POINTER(WriteRange),
|
||||||
|
ctypes.c_int,
|
||||||
|
ctypes.c_int64,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_setOldestVersion.argtypes = (
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.c_int64,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_destroy.argtypes = (ctypes.c_void_p,)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_getBytes.argtypes = (ctypes.c_void_p,)
|
||||||
|
self._lib.ConflictSet_getBytes.restype = ctypes.c_int64
|
||||||
|
|
||||||
|
self.p = self._lib.ConflictSet_create(version)
|
||||||
|
|
||||||
|
def addWrites(self, version: int, *writes: WriteRange):
|
||||||
|
self._lib.ConflictSet_addWrites(
|
||||||
|
self.p, (WriteRange * len(writes))(*writes), len(writes), version
|
||||||
|
)
|
||||||
|
|
||||||
|
def check(self, *reads: ReadRange) -> list[Result]:
|
||||||
|
r = (ctypes.c_int * len(reads))()
|
||||||
|
self._lib.ConflictSet_check(self.p, *reads, r, 1)
|
||||||
|
return [Result(x) for x in r]
|
||||||
|
|
||||||
|
def setOldestVersion(self, version: int) -> None:
|
||||||
|
self._lib.ConflictSet_setOldestVersion(self.p, version)
|
||||||
|
|
||||||
|
def getBytes(self) -> int:
|
||||||
|
return self._lib.ConflictSet_getBytes(self.p)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
if self.p is not None:
|
||||||
|
self._lib.ConflictSet_destroy(self.p)
|
||||||
|
self.p = None
|
||||||
|
|
||||||
|
def __exit__(self, exception_type, exception_value, exception_traceback):
|
||||||
|
self.close()
|
33
package_macos.sh
Executable file
33
package_macos.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
umask 022
|
||||||
|
|
||||||
|
SRC_DIR="${0%/*}"
|
||||||
|
BUILD_ARM="$(mktemp -d -t conflict-set-arm)"
|
||||||
|
BUILD_X86="$(mktemp -d -t conflict-set-x86)"
|
||||||
|
|
||||||
|
cmake_args=(-DCMAKE_CXX_FLAGS=-DNVALGRIND -DCPACK_PACKAGING_INSTALL_PREFIX=/usr/local)
|
||||||
|
|
||||||
|
cmake -S"$SRC_DIR" -B"$BUILD_ARM" -DCMAKE_OSX_ARCHITECTURES=arm64 "${cmake_args[@]}"
|
||||||
|
cmake --build "$BUILD_ARM" --target conflict-set --target conflict-set-static
|
||||||
|
|
||||||
|
cmake -S"$SRC_DIR" -B"$BUILD_X86" -DCMAKE_OSX_ARCHITECTURES=x86_64 "${cmake_args[@]}"
|
||||||
|
cmake --build "$BUILD_X86" --target conflict-set --target conflict-set-static
|
||||||
|
|
||||||
|
VERSION="$(cat "$BUILD_ARM/version.txt")"
|
||||||
|
|
||||||
|
lipo -create "$BUILD_ARM/radix_tree/libconflict-set.$VERSION.dylib" "$BUILD_X86/radix_tree/libconflict-set.$VERSION.dylib" -output "libconflict-set.$VERSION.dylib.tmp"
|
||||||
|
lipo -create "$BUILD_ARM"/libconflict-set-static.a "$BUILD_X86"/libconflict-set-static.a -output libconflict-set-static.a.tmp
|
||||||
|
|
||||||
|
mv "libconflict-set.$VERSION.dylib.tmp" "$BUILD_ARM/radix_tree/libconflict-set.$VERSION.dylib"
|
||||||
|
mv libconflict-set-static.a.tmp "$BUILD_ARM/libconflict-set-static.a"
|
||||||
|
|
||||||
|
pushd "$BUILD_ARM"
|
||||||
|
cpack -G productbuild
|
||||||
|
popd
|
||||||
|
|
||||||
|
mv "$BUILD_ARM/conflict-set-$VERSION-Darwin.pkg" .
|
||||||
|
|
||||||
|
rm -rf "$BUILD_ARM" "$BUILD_X86"
|
@@ -155,3 +155,16 @@ keywords = {data structures, searching, trees}
|
|||||||
year={1981},
|
year={1981},
|
||||||
publisher={ACM New York, NY, USA}
|
publisher={ACM New York, NY, USA}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@article{Lemire_2018,
|
||||||
|
title={Roaring bitmaps: Implementation of an optimized software library},
|
||||||
|
volume={48},
|
||||||
|
ISSN={1097-024X},
|
||||||
|
url={http://dx.doi.org/10.1002/spe.2560},
|
||||||
|
DOI={10.1002/spe.2560},
|
||||||
|
number={4},
|
||||||
|
journal={Software: Practice and Experience},
|
||||||
|
publisher={Wiley},
|
||||||
|
author={Lemire, Daniel and Kaser, Owen and Kurz, Nathan and Deri, Luca and O’Hara, Chris and Saint‐Jacques, François and Ssi‐Yan‐Kai, Gregory},
|
||||||
|
year={2018},
|
||||||
|
month=jan, pages={867–895} }
|
||||||
|
120
paper/paper.tex
120
paper/paper.tex
@@ -6,6 +6,7 @@
|
|||||||
\usepackage{tikz}
|
\usepackage{tikz}
|
||||||
\usepackage{tikzscale}
|
\usepackage{tikzscale}
|
||||||
\usepackage[edges]{forest}
|
\usepackage[edges]{forest}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
|
||||||
\title{ARTful Conflict Checking for FoundationDB}
|
\title{ARTful Conflict Checking for 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}}}
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
|
|
||||||
\section*{Abstract}
|
\section*{Abstract}
|
||||||
|
|
||||||
FoundationDB \cite{DBLP:conf/sigmod/ZhouXSNMTABSLRD21} provides serializability using a specialized data structure called \textit{lastCommit} \footnote{See Algorithm 1 referenced in \cite{DBLP:conf/sigmod/ZhouXSNMTABSLRD21}} to implement optimistic concurrency control \cite{kung1981optimistic}.
|
FoundationDB \cite{DBLP:conf/sigmod/ZhouXSNMTABSLRD21} provides serializability using a specialized data structure called \textit{lastCommit} \footnote{See Algorithm 1 referenced in \cite{DBLP:conf/sigmod/ZhouXSNMTABSLRD21}.} to implement optimistic concurrency control \cite{kung1981optimistic}.
|
||||||
This data structure encodes the write sets for recent transactions as a map from key ranges (represented as bitwise-lexicographically-ordered half-open intervals) to most recent write versions.
|
This data structure encodes the write sets for recent transactions as a map from key ranges (represented as bitwise-lexicographically-ordered half-open intervals) to most recent write versions.
|
||||||
FoundationDB implements \textit{lastCommit} as a version-augmented probabilistic skip list \cite{10.1145/78973.78977}.
|
FoundationDB implements \textit{lastCommit} as a version-augmented probabilistic skip list \cite{10.1145/78973.78977}.
|
||||||
In this paper, we propose an alternative implementation of \textit{lastCommit} as a version-augmented Adaptive Radix Tree (ART) \cite{DBLP:conf/icde/LeisK013}, and evaluate its performance.
|
In this paper, we propose an alternative implementation of \textit{lastCommit} as a version-augmented Adaptive Radix Tree (ART) \cite{DBLP:conf/icde/LeisK013}, and evaluate its performance.
|
||||||
@@ -68,9 +69,16 @@ See figure \ref{fig:tree} for an example tree after inserting
|
|||||||
$\{ANY\} \rightarrow 2$,
|
$\{ANY\} \rightarrow 2$,
|
||||||
$\{ARE\} \rightarrow 3$, and
|
$\{ARE\} \rightarrow 3$, and
|
||||||
$\{ART\} \rightarrow 4$.
|
$\{ART\} \rightarrow 4$.
|
||||||
Each node shows its partial prefix annotated with $(max,point,range)$.
|
Each node shows its partial prefix annotated with $max$ or $max,point,range$.
|
||||||
|
|
||||||
\subsection{Checking point reads}
|
\begin{figure}
|
||||||
|
\caption{}
|
||||||
|
\label{fig:tree}
|
||||||
|
\centering
|
||||||
|
\includegraphics{tree.tikz}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsection{Checking point reads} \label{Checking point reads}
|
||||||
|
|
||||||
The algorithm for checking point reads follows directly from the definitions of the \emph{point} and \emph{range} fields.
|
The algorithm for checking point reads follows directly from the definitions of the \emph{point} and \emph{range} fields.
|
||||||
Our input is a key $k$ and a read version $r$, and we must report whether or not the write version $v_{k}$ of $k$ is less than or equal to $r$.
|
Our input is a key $k$ and a read version $r$, and we must report whether or not the write version $v_{k}$ of $k$ is less than or equal to $r$.
|
||||||
@@ -84,16 +92,106 @@ As an optimization, during the search phase for a point read we can inspect the
|
|||||||
If the max version among all keys starting with a prefix of $k$ is less than or equal to $r$, then $v_{k} \leq r$.
|
If the max version among all keys starting with a prefix of $k$ is less than or equal to $r$, then $v_{k} \leq r$.
|
||||||
|
|
||||||
\subsection{Checking range reads}
|
\subsection{Checking range reads}
|
||||||
\subsection{Adding point writes}
|
|
||||||
\subsection{Adding range writes}
|
Checking range reads is more involved. Logically the idea is to partition the range read so that each subrange in the partition is a single point or coincides with the set of keys beginning with a prefix (a \emph{prefix range}).
|
||||||
|
The max version of a single point is $v$ as described in \ref{Checking point reads}.
|
||||||
|
The max version of a prefix range is the $max$ of the node associated with the prefix if such a node exists, and $range$ of the next node with a $range$ field otherwise.
|
||||||
|
If there is no next node with a range field, then we ignore that subrange in our max version calculation.
|
||||||
|
The max version among all max versions of subranges in this partition is the max version of the whole range, which we compare to $r$.
|
||||||
|
|
||||||
|
Let's start with partitioning the range in the case where the beginning of the range is a prefix of the end of the range.
|
||||||
|
We'll be able to use this as a subroutine in the general case.
|
||||||
|
Suppose our range is $[a_{0}\dots a_{k}, a_{0}\dots a_{n})$ where $k < n$, and $a_{i} \in [0, 256)$.
|
||||||
|
The partition starts with the singleton set containing the first key in the range.
|
||||||
|
\[
|
||||||
|
\{a_{0}\dots a_{k}\}
|
||||||
|
\]
|
||||||
|
or equivalently
|
||||||
|
\[
|
||||||
|
[a_{0}\dots a_{k}, a_{0}\dots a_{k} 0)
|
||||||
|
\]
|
||||||
|
and continues with a sequence of prefix ranges ending in each digit up until $a_{k+1}$.
|
||||||
|
Recall that the range $[a_{0}\dots a_{k} 0, a_{0}\dots a_{k} 1)$ is equivalent to the set of keys starting with $a_{0}\dots a_{k} 0$.
|
||||||
|
|
||||||
|
\begin{align*}
|
||||||
|
\dots \quad \cup \quad & [a_{0}\dots a_{k} 0, a_{0}\dots a_{k} 1) \quad \cup \\
|
||||||
|
& [a_{0}\dots a_{k} 1, a_{0}\dots a_{k} 2) \quad \cup \\
|
||||||
|
& \dots \quad \cup \\
|
||||||
|
& [a_{0}\dots a_{k} (a_{k+1}-1), a_{0}\dots a_{k+1})
|
||||||
|
\end{align*}
|
||||||
|
|
||||||
|
The remainder of the partition begins with the singleton set
|
||||||
|
\[
|
||||||
|
\dots \quad \cup \quad [a_{0}\dots a_{k + 1}, a_{0}\dots a_{k + 1} 0) \quad \cup\ \quad \dots
|
||||||
|
\]
|
||||||
|
and proceeds as above until a range ending at $a_{0}\dots a_{n}$.
|
||||||
|
|
||||||
|
Let's now consider a range where begin is not a prefix of end.
|
||||||
|
|
||||||
|
\[
|
||||||
|
[a_{0}\dots a_{m}, b_{0}\dots b_{n})
|
||||||
|
\]
|
||||||
|
|
||||||
|
Let $i$ be the lowest index such that $a_{i} \neq b_{i}$.
|
||||||
|
For brevity we will elide the common prefix up until $i$ in the following discussion so that our range is denoted as $[a_{i}\dots a_{m}, b_{i}\dots b_{n})$.
|
||||||
|
We'll start with partitioning this range coarsely:
|
||||||
|
|
||||||
|
\begin{align*}
|
||||||
|
& [a_{i}\dots a_{m}, a_{i} + 1) \quad \cup \\
|
||||||
|
& [a_{i} + 1, a_{i} + 2) \quad \cup \\
|
||||||
|
& \dots \\
|
||||||
|
& [b_{i} - 1, b_{i}) \quad \cup \\
|
||||||
|
& [b_{i}, b_{i}\dots b_{n})
|
||||||
|
\end{align*}
|
||||||
|
|
||||||
|
The last range has a begin that's a prefix of end, and so we'll partition that as before.
|
||||||
|
The inner ranges are already prefix ranges.
|
||||||
|
This leaves only $[a_{i}\dots a_{m}, a_{i} + 1)$.
|
||||||
|
|
||||||
|
If $m = i$, then this range is adjacent to the first inner range above, and we're done.
|
||||||
|
Otherwise we'll partition this into
|
||||||
|
|
||||||
|
\begin{align*}
|
||||||
|
& [a_{i}\dots a_{m}, a_{i}\dots (a_{m} + 1)) \quad \cup \\
|
||||||
|
& [a_{i}\dots (a_{m} + 1), a_{i}\dots (a_{m} + 2)) \quad \cup \\
|
||||||
|
& \dots \\
|
||||||
|
& [a_{i}\dots 254, a_{i}\dots 255) \quad \cup \\
|
||||||
|
& [a_{i}\dots 255, a_{i}\dots (a_{m-1} + 1) )
|
||||||
|
\end{align*}
|
||||||
|
|
||||||
|
and repeat \footnote{This doesn't explicitly describe how to handle the case where $a_{m-1} = 255$. In this case we would skip to the largest $j < m$ such that $a_{j} \neq 255$. We know $j \geq i$ since if $a_{i} = 255$ then the range is inverted.} starting at
|
||||||
|
\[
|
||||||
|
\dots \quad \cup \quad [a_{i}\dots (a_{m-1} + 1), a_{i}\dots (a_{m-1} + 2))
|
||||||
|
\]
|
||||||
|
until we end at $a_{i} + 1$, adjacent to the first inner range.
|
||||||
|
|
||||||
|
A few notes on implementation:
|
||||||
|
\begin{itemize}
|
||||||
|
\item{For clarity, the above algorithm decouples the logical partitioning from the physical structure of the tree. An optimized implementation would merge adjacent prefix ranges that don't correspond to nodes in the tree as it scans, so that it only calculates the version of such merged ranges once. Additionally, our implementation stores an index of which child pointers are valid as a bitset for Node48 and Node256 to speed up this scan using techniques inspired by \cite{Lemire_2018}.}
|
||||||
|
\item{In order to avoid many costly pointer indirections, we can store the max version not in each node itself but next to each node's parent pointer. Without this, the range read performance is not competetive with the skip list.}
|
||||||
|
\item{An optimized implementation would construct the partition of $[a_{i}\dots a_{m}, a_{i} + 1)$ in reverse order, as it descends along the search path to $a_{i}\dots a_{m}$}
|
||||||
|
\item{An optimized implementation would search for the common prefix first, and return early if any prefix of the common prefix has a $max \leq r$.}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
\subsection{Reclaiming old entries}
|
\subsection{Reclaiming old entries}
|
||||||
|
|
||||||
\begin{figure}
|
In order to bound memory usage, we track an \emph{oldest version}, reject transactions with read versions before \emph{oldest version}, and reclaim nodes made redundant by \emph{oldest version}.
|
||||||
\caption{}
|
We track the rate of insertions of new nodes and make sure that our incremental reclaiming of old nodes according to \emph{oldest version} outpaces inserts.
|
||||||
\label{fig:tree}
|
|
||||||
\centering
|
\subsection{Adding point writes}
|
||||||
\includegraphics{tree.tikz}
|
|
||||||
\end{figure}
|
A point write of $k$ at version $v$ simply sets $max \gets v$ \footnote{Recall that write versions are non-decreasing.} for every node along $k$'s search path, and sets $range$ for $k$'s node to the $range$ of the first node greater than $k$, or the \emph{oldest version} if none exists.
|
||||||
|
|
||||||
|
\subsection{Adding range writes}
|
||||||
|
|
||||||
|
A range write of $[b, e)$ at version $v$ performs a point write of $b$ at $v$, and then inserts a node at $e$ with $range$ set to $v$, and $point$ set such that the result of checking a read of $e$ is unaffected.
|
||||||
|
Nodes along the search path to $e$ that are a strict prefix of $e$ get $max$ set to $v$, and all nodes between $b$ and $e$ are removed.
|
||||||
|
|
||||||
|
\section{Evaluation}
|
||||||
|
|
||||||
|
\section{Testing}
|
||||||
|
|
||||||
|
\section{Conclusion}
|
||||||
|
|
||||||
\printbibliography
|
\printbibliography
|
||||||
|
|
||||||
|
@@ -2,5 +2,7 @@
|
|||||||
|
|
||||||
# This has the effect of making visibility=hidden symbols private in object files
|
# This has the effect of making visibility=hidden symbols private in object files
|
||||||
for obj in "$@" ; do
|
for obj in "$@" ; do
|
||||||
ld -r "$obj" -o "$obj.tmp" && mv "$obj.tmp" "$obj"
|
ld -r "$obj" -o "$obj.tmp"
|
||||||
|
touch -r "$obj" "$obj.tmp"
|
||||||
|
mv "$obj.tmp" "$obj"
|
||||||
done
|
done
|
||||||
|
@@ -1,262 +0,0 @@
|
|||||||
|
|
||||||
; Create a node with a large partial key capacity
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
; Make it a Node256
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaA
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaC
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaD
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaE
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaF
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaG
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaH
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaI
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaJ
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaK
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaL
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaM
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaN
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaO
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaP
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaQ
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaR
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaS
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaT
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaU
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaV
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaW
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaY
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZ
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaae
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaf
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaai
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaj
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaak
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaal
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaam
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaao
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaap
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaq
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaav
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaw
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz
|
|
||||||
pointwrite
|
|
||||||
version 1
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
; Lower its partial key length
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
version 2
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
; Create work for setoldest
|
|
||||||
|
|
||||||
begin x
|
|
||||||
end y
|
|
||||||
rangewrite
|
|
||||||
version 3
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 4
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 5
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 6
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 7
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 8
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 9
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 10
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 11
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 12
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 13
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 14
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 15
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 16
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 17
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 18
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 19
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 20
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 21
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 22
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 23
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
version 1
|
|
||||||
setoldest
|
|
@@ -1,147 +0,0 @@
|
|||||||
; Create a node with a large partial key capacity
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
; Make it a Node48
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaae
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaf
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaai
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaj
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaak
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaal
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaam
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaao
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaap
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaq
|
|
||||||
pointwrite
|
|
||||||
version 2
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
; Lower its partial key length
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
version 3
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
; Create work for setoldest
|
|
||||||
|
|
||||||
begin x
|
|
||||||
end y
|
|
||||||
rangewrite
|
|
||||||
version 4
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 5
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 6
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 7
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 8
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 9
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 10
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 11
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 12
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 13
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 14
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 15
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 16
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 17
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 18
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 19
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 20
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 21
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
rangewrite
|
|
||||||
version 22
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
version 1
|
|
||||||
setoldest
|
|
@@ -1,124 +0,0 @@
|
|||||||
begin
|
|
||||||
pointwrite
|
|
||||||
begin 0
|
|
||||||
pointwrite
|
|
||||||
begin 00
|
|
||||||
pointwrite
|
|
||||||
begin 00A
|
|
||||||
pointwrite
|
|
||||||
begin 00B
|
|
||||||
pointwrite
|
|
||||||
begin 00C
|
|
||||||
pointwrite
|
|
||||||
begin 00D
|
|
||||||
pointwrite
|
|
||||||
begin 00E
|
|
||||||
pointwrite
|
|
||||||
begin 00F
|
|
||||||
pointwrite
|
|
||||||
begin 00G
|
|
||||||
pointwrite
|
|
||||||
begin 00H
|
|
||||||
pointwrite
|
|
||||||
begin 00I
|
|
||||||
pointwrite
|
|
||||||
begin 00J
|
|
||||||
pointwrite
|
|
||||||
begin 00K
|
|
||||||
pointwrite
|
|
||||||
begin 00L
|
|
||||||
pointwrite
|
|
||||||
begin 00M
|
|
||||||
pointwrite
|
|
||||||
begin 00N
|
|
||||||
pointwrite
|
|
||||||
begin 00O
|
|
||||||
pointwrite
|
|
||||||
begin 00P
|
|
||||||
pointwrite
|
|
||||||
begin 00Q
|
|
||||||
pointwrite
|
|
||||||
begin 00R
|
|
||||||
pointwrite
|
|
||||||
begin 00S
|
|
||||||
pointwrite
|
|
||||||
begin 00T
|
|
||||||
pointwrite
|
|
||||||
begin 00U
|
|
||||||
pointwrite
|
|
||||||
begin 00V
|
|
||||||
pointwrite
|
|
||||||
begin 00W
|
|
||||||
pointwrite
|
|
||||||
begin 00X
|
|
||||||
pointwrite
|
|
||||||
begin 00Y
|
|
||||||
pointwrite
|
|
||||||
begin 00Z
|
|
||||||
pointwrite
|
|
||||||
begin 00a
|
|
||||||
pointwrite
|
|
||||||
begin 00b
|
|
||||||
pointwrite
|
|
||||||
begin 00c
|
|
||||||
pointwrite
|
|
||||||
begin 00d
|
|
||||||
pointwrite
|
|
||||||
begin 00e
|
|
||||||
pointwrite
|
|
||||||
begin 00f
|
|
||||||
pointwrite
|
|
||||||
begin 00g
|
|
||||||
pointwrite
|
|
||||||
begin 00h
|
|
||||||
pointwrite
|
|
||||||
begin 00i
|
|
||||||
pointwrite
|
|
||||||
begin 00j
|
|
||||||
pointwrite
|
|
||||||
begin 00k
|
|
||||||
pointwrite
|
|
||||||
begin 00l
|
|
||||||
pointwrite
|
|
||||||
begin 00m
|
|
||||||
pointwrite
|
|
||||||
begin 00n
|
|
||||||
pointwrite
|
|
||||||
begin 00o
|
|
||||||
pointwrite
|
|
||||||
begin 00p
|
|
||||||
pointwrite
|
|
||||||
begin 00q
|
|
||||||
pointwrite
|
|
||||||
begin 00r
|
|
||||||
pointwrite
|
|
||||||
begin 00s
|
|
||||||
pointwrite
|
|
||||||
begin 00t
|
|
||||||
pointwrite
|
|
||||||
begin 00u
|
|
||||||
pointwrite
|
|
||||||
begin 00v
|
|
||||||
pointwrite
|
|
||||||
begin 00w
|
|
||||||
pointwrite
|
|
||||||
begin 00x
|
|
||||||
pointwrite
|
|
||||||
begin 00y
|
|
||||||
pointwrite
|
|
||||||
begin 00z
|
|
||||||
pointwrite
|
|
||||||
version 1
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
begin 0
|
|
||||||
end 000
|
|
||||||
rangewrite
|
|
||||||
version 2
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
begin
|
|
||||||
end 00
|
|
||||||
rangewrite
|
|
||||||
version 3
|
|
||||||
addwrites
|
|
@@ -1,72 +0,0 @@
|
|||||||
begin
|
|
||||||
pointwrite
|
|
||||||
begin 0
|
|
||||||
pointwrite
|
|
||||||
begin 00
|
|
||||||
pointwrite
|
|
||||||
begin 00a
|
|
||||||
pointwrite
|
|
||||||
begin 00b
|
|
||||||
pointwrite
|
|
||||||
begin 00c
|
|
||||||
pointwrite
|
|
||||||
begin 00d
|
|
||||||
pointwrite
|
|
||||||
begin 00e
|
|
||||||
pointwrite
|
|
||||||
begin 00f
|
|
||||||
pointwrite
|
|
||||||
begin 00g
|
|
||||||
pointwrite
|
|
||||||
begin 00h
|
|
||||||
pointwrite
|
|
||||||
begin 00i
|
|
||||||
pointwrite
|
|
||||||
begin 00j
|
|
||||||
pointwrite
|
|
||||||
begin 00k
|
|
||||||
pointwrite
|
|
||||||
begin 00l
|
|
||||||
pointwrite
|
|
||||||
begin 00m
|
|
||||||
pointwrite
|
|
||||||
begin 00n
|
|
||||||
pointwrite
|
|
||||||
begin 00o
|
|
||||||
pointwrite
|
|
||||||
begin 00p
|
|
||||||
pointwrite
|
|
||||||
begin 00q
|
|
||||||
pointwrite
|
|
||||||
begin 00r
|
|
||||||
pointwrite
|
|
||||||
begin 00s
|
|
||||||
pointwrite
|
|
||||||
begin 00t
|
|
||||||
pointwrite
|
|
||||||
begin 00u
|
|
||||||
pointwrite
|
|
||||||
begin 00v
|
|
||||||
pointwrite
|
|
||||||
begin 00w
|
|
||||||
pointwrite
|
|
||||||
begin 00x
|
|
||||||
pointwrite
|
|
||||||
begin 00y
|
|
||||||
pointwrite
|
|
||||||
begin 00z
|
|
||||||
pointwrite
|
|
||||||
version 1
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
begin 0
|
|
||||||
end 000
|
|
||||||
rangewrite
|
|
||||||
version 2
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
begin
|
|
||||||
end 00
|
|
||||||
rangewrite
|
|
||||||
version 3
|
|
||||||
addwrites
|
|
@@ -1,9 +0,0 @@
|
|||||||
begin aa
|
|
||||||
pointwrite
|
|
||||||
version 1
|
|
||||||
addwrites
|
|
||||||
begin aa
|
|
||||||
end bb
|
|
||||||
version 0
|
|
||||||
rangeread
|
|
||||||
check
|
|
@@ -1,212 +0,0 @@
|
|||||||
; insert \x3fa at version 1
|
|
||||||
begin ?a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
; insert \x81a at version 1
|
|
||||||
begin a
|
|
||||||
pointwrite
|
|
||||||
version 1
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
; insert [\x40-\x80]a at version 2
|
|
||||||
begin @a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Aa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ba
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ca
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Da
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ea
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Fa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ga
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ha
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ia
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ja
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ka
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin La
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ma
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Na
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Oa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Pa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Qa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ra
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Sa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ta
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ua
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Va
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Wa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Xa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Ya
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin Za
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin [a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin \a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ]a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ^a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin _a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin `a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin aa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ba
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ca
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin da
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ea
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin fa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ga
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ha
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ia
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ja
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ka
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin la
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ma
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin na
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin oa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin pa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin qa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ra
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin sa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ta
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ua
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin va
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin wa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin xa
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ya
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin za
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin {a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin |a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin }a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin ~a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
begin a
|
|
||||||
pointwrite
|
|
||||||
|
|
||||||
version 2
|
|
||||||
addwrites
|
|
||||||
|
|
||||||
; readrange a superset at version 1
|
|
||||||
begin !
|
|
||||||
end
|
|
||||||
version 1
|
|
||||||
rangeread
|
|
||||||
check
|
|
@@ -1,19 +0,0 @@
|
|||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
end aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
|
|
||||||
rangeread
|
|
||||||
check
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
end aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaaab
|
|
||||||
rangeread
|
|
||||||
check
|
|
||||||
|
|
||||||
begin aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
end aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaa
|
|
||||||
rangeread
|
|
||||||
check
|
|
||||||
|
|
||||||
begin aaaaaaaaaa
|
|
||||||
end aaaaaaabaa
|
|
||||||
rangeread
|
|
||||||
check
|
|
122
test_conflict_set.py
Normal file
122
test_conflict_set.py
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
from conflict_set import *
|
||||||
|
|
||||||
|
|
||||||
|
build_dir = None
|
||||||
|
|
||||||
|
|
||||||
|
class DebugConflictSet:
|
||||||
|
"""
|
||||||
|
Bisimulates the skip list and radix tree conflict sets for testing purposes
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, version: int = 0) -> None:
|
||||||
|
self.skip_list = ConflictSet(
|
||||||
|
version, build_dir=build_dir, implementation="skip_list"
|
||||||
|
)
|
||||||
|
self.radix_tree = ConflictSet(
|
||||||
|
version, build_dir=build_dir, implementation="radix_tree"
|
||||||
|
)
|
||||||
|
|
||||||
|
def addWrites(self, version: int, *writes: WriteRange):
|
||||||
|
self.skip_list.addWrites(version, *writes)
|
||||||
|
self.radix_tree.addWrites(version, *writes)
|
||||||
|
|
||||||
|
def check(self, *reads: ReadRange) -> list[Result]:
|
||||||
|
expected = self.skip_list.check(*reads)
|
||||||
|
actual = self.radix_tree.check(*reads)
|
||||||
|
assert expected == actual
|
||||||
|
return actual
|
||||||
|
|
||||||
|
def setOldestVersion(self, version: int) -> None:
|
||||||
|
self.skip_list.setOldestVersion(version)
|
||||||
|
self.radix_tree.setOldestVersion(version)
|
||||||
|
|
||||||
|
def getBytes(self) -> int:
|
||||||
|
return self.radix_tree.getBytes()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
self.skip_list.close()
|
||||||
|
self.radix_tree.close()
|
||||||
|
|
||||||
|
def __exit__(self, exception_type, exception_value, exception_traceback):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_conflict_set():
|
||||||
|
with DebugConflictSet() as cs:
|
||||||
|
before = cs.getBytes()
|
||||||
|
key = b"a key"
|
||||||
|
cs.addWrites(1, write(key))
|
||||||
|
assert cs.getBytes() - before > 0
|
||||||
|
assert cs.check(read(0, key)) == [Result.CONFLICT]
|
||||||
|
cs.setOldestVersion(1)
|
||||||
|
assert cs.check(read(0, key)) == [Result.TOO_OLD]
|
||||||
|
|
||||||
|
|
||||||
|
def test_inner_full_words():
|
||||||
|
with DebugConflictSet() as cs:
|
||||||
|
cs.addWrites(1, write(b"\x3f\x61"), write(b"\x81\x61"))
|
||||||
|
writes = []
|
||||||
|
for i in range(0x40, 0x81):
|
||||||
|
writes.append(write(bytes([i, 0x61])))
|
||||||
|
cs.addWrites(2, *writes)
|
||||||
|
cs.check(read(1, b"\x21", b"\xc2"))
|
||||||
|
|
||||||
|
|
||||||
|
def test_decrease_capacity():
|
||||||
|
# make a Node48, then a Node256
|
||||||
|
for count in (17, 49):
|
||||||
|
with DebugConflictSet() as cs:
|
||||||
|
for i in range(count):
|
||||||
|
cs.addWrites(1, write(bytes(([0] * 99) + [i])))
|
||||||
|
# lower its partial key length
|
||||||
|
cs.addWrites(2, write(bytes([0] * 98)))
|
||||||
|
# create work for setOldestVersion
|
||||||
|
for i in range(3, 1000):
|
||||||
|
cs.addWrites(i)
|
||||||
|
# setOldestVersion should decrease the capacity
|
||||||
|
cs.setOldestVersion(1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_large():
|
||||||
|
with DebugConflictSet() as cs:
|
||||||
|
end = 100000
|
||||||
|
for i in range(end):
|
||||||
|
cs.addWrites(1, write(i.to_bytes(8, byteorder="big")))
|
||||||
|
cs.addWrites(
|
||||||
|
2,
|
||||||
|
write((0).to_bytes(8, byteorder="big"), (end).to_bytes(8, byteorder="big")),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# budget "pytest" for ctest integration without pulling in a dependency. You can of course still use pytest in local development.
|
||||||
|
import argparse
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
subparsers = parser.add_subparsers(dest="command")
|
||||||
|
|
||||||
|
list_parser = subparsers.add_parser("list")
|
||||||
|
test_parser = subparsers.add_parser("test")
|
||||||
|
test_parser.add_argument("test")
|
||||||
|
test_parser.add_argument("--build-dir")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.command == "list":
|
||||||
|
sys.stdout.write(
|
||||||
|
";".join(
|
||||||
|
name[5:]
|
||||||
|
for name in dir()
|
||||||
|
if name.startswith("test_")
|
||||||
|
and inspect.isfunction(getattr(sys.modules[__name__], name))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif args.command == "test":
|
||||||
|
build_dir = args.build_dir
|
||||||
|
globals()["test_" + args.test]()
|
Reference in New Issue
Block a user