Compare commits
3 Commits
v0.0.5
..
baddea7f57
| Author | SHA1 | Date | |
|---|---|---|---|
| baddea7f57 | |||
| 2d3e7b9004 | |||
| c4862fee9b |
@@ -1,3 +1,2 @@
|
|||||||
.cache
|
.cache
|
||||||
__pycache__
|
|
||||||
build
|
build
|
||||||
|
|||||||
+8
-11
@@ -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,14 +21,11 @@ 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/koalaman/shellcheck-precommit
|
||||||
rev: a23f6b85d0fdd5bb9d564e2579e678033debbdff # frozen: v0.10.0.1
|
rev: 2491238703a5d3415bb2b7ff11388bf775372f29 # frozen: v0.10.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
- repo: https://github.com/psf/black
|
# args: ["--severity=warning"] # Optionally only show errors and warnings
|
||||||
rev: 552baf822992936134cbd31a38f69c8cfe7c0f05 # frozen: 24.3.0
|
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
+12
-51
@@ -1,15 +1,13 @@
|
|||||||
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.2
|
||||||
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)
|
||||||
@@ -130,7 +128,7 @@ endif()
|
|||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${PROJECT_NAME}-static
|
TARGET ${PROJECT_NAME}-static
|
||||||
PRE_LINK
|
PRE_BUILD
|
||||||
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()
|
||||||
@@ -138,8 +136,7 @@ else()
|
|||||||
TARGET ${PROJECT_NAME}-static
|
TARGET ${PROJECT_NAME}-static
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_OBJCOPY}
|
${CMAKE_OBJCOPY} --keep-global-symbols=${CMAKE_SOURCE_DIR}/symbols.txt
|
||||||
--keep-global-symbols=${CMAKE_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()
|
||||||
@@ -261,22 +258,14 @@ 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.
|
||||||
if(NOT CMAKE_CROSSCOMPILING)
|
add_executable(script_test ScriptTest.cpp)
|
||||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
target_compile_options(script_test PRIVATE ${TEST_FLAGS})
|
||||||
set_property(
|
target_link_libraries(script_test PRIVATE ${PROJECT_NAME})
|
||||||
DIRECTORY
|
file(GLOB SCRIPT_TESTS ${CMAKE_SOURCE_DIR}/script_tests/*)
|
||||||
APPEND
|
foreach(TEST ${SCRIPT_TESTS})
|
||||||
PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/test_conflict_set.py)
|
get_filename_component(name ${TEST} NAME)
|
||||||
execute_process(
|
add_test(NAME conflict_set_script_${name} COMMAND script_test ${TEST})
|
||||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/test_conflict_set.py
|
endforeach()
|
||||||
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)
|
||||||
@@ -312,11 +301,7 @@ if(BUILD_TESTING)
|
|||||||
set(symbol_imports ${CMAKE_SOURCE_DIR}/apple-symbol-imports.txt)
|
set(symbol_imports ${CMAKE_SOURCE_DIR}/apple-symbol-imports.txt)
|
||||||
else()
|
else()
|
||||||
set(symbol_exports ${CMAKE_SOURCE_DIR}/symbol-exports.txt)
|
set(symbol_exports ${CMAKE_SOURCE_DIR}/symbol-exports.txt)
|
||||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
set(symbol_imports ${CMAKE_SOURCE_DIR}/symbol-imports.txt)
|
||||||
set(symbol_imports ${CMAKE_SOURCE_DIR}/aarch64-symbol-imports.txt)
|
|
||||||
else()
|
|
||||||
set(symbol_imports ${CMAKE_SOURCE_DIR}/symbol-imports.txt)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
add_test(
|
add_test(
|
||||||
NAME conflict_set_shared_symbols
|
NAME conflict_set_shared_symbols
|
||||||
@@ -343,7 +328,6 @@ 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")
|
||||||
@@ -357,26 +341,6 @@ set(CPACK_RPM_FILE_NAME RPM-DEFAULT)
|
|||||||
|
|
||||||
# deb
|
# deb
|
||||||
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
|
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
|
||||||
# see *-imports.txt - dependency versions need to be synced with symbol versions
|
|
||||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17)")
|
|
||||||
else()
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.14)")
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -402,11 +366,8 @@ 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)
|
|
||||||
|
|||||||
+43
-73
@@ -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 so that someday we can use the simd
|
// weird "generic" way though in case 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,14 +1146,8 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -2395,11 +2389,7 @@ Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
|
|||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
if (n == nullptr) {
|
if (n == nullptr) {
|
||||||
// This line is genuinely unreachable from any entry point of the
|
return {nullptr, 1};
|
||||||
// final library, since we can't remove a key without introducing a
|
|
||||||
// key after it, and the only production caller of firstGeq is for
|
|
||||||
// resuming the setOldestVersion scan.
|
|
||||||
return {nullptr, 1}; // GCOVR_EXCL_LINE
|
|
||||||
}
|
}
|
||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
}
|
}
|
||||||
@@ -2520,10 +2510,6 @@ 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;
|
||||||
@@ -2538,12 +2524,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
root->entry.pointVersion = oldestVersion;
|
root->entry.pointVersion = oldestVersion;
|
||||||
root->entry.rangeVersion = oldestVersion;
|
root->entry.rangeVersion = oldestVersion;
|
||||||
}
|
}
|
||||||
~Impl() {
|
~Impl() { destroyTree(root); }
|
||||||
#if DEBUG_VERBOSE
|
|
||||||
fprintf(stderr, "radix_tree: destroy\n");
|
|
||||||
#endif
|
|
||||||
destroyTree(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeAllocators allocators;
|
NodeAllocators allocators;
|
||||||
|
|
||||||
@@ -2597,16 +2578,17 @@ Node *&getInTree(Node *n, ConflictSet::Impl *impl) {
|
|||||||
: getChildExists(n->parent, n->parentsIndex);
|
: getChildExists(n->parent, n->parentsIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal entry points. Public entry points should just delegate to these
|
// ==================== END IMPLEMENTATION ====================
|
||||||
|
|
||||||
void internal_check(ConflictSet::Impl *impl,
|
// GCOVR_EXCL_START
|
||||||
const ConflictSet::ReadRange *reads,
|
|
||||||
ConflictSet::Result *results, int count) {
|
void ConflictSet::check(const ReadRange *reads, Result *results,
|
||||||
impl->check(reads, results, count);
|
int count) const {
|
||||||
|
return impl->check(reads, results, count);
|
||||||
}
|
}
|
||||||
void internal_addWrites(ConflictSet::Impl *impl,
|
|
||||||
const ConflictSet::WriteRange *writes, int count,
|
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
||||||
int64_t writeVersion) {
|
int64_t writeVersion) {
|
||||||
mallocBytesDelta = 0;
|
mallocBytesDelta = 0;
|
||||||
impl->addWrites(writes, count, writeVersion);
|
impl->addWrites(writes, count, writeVersion);
|
||||||
impl->totalBytes += mallocBytesDelta;
|
impl->totalBytes += mallocBytesDelta;
|
||||||
@@ -2617,7 +2599,7 @@ void internal_addWrites(ConflictSet::Impl *impl,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void internal_setOldestVersion(ConflictSet::Impl *impl, int64_t oldestVersion) {
|
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
||||||
mallocBytesDelta = 0;
|
mallocBytesDelta = 0;
|
||||||
impl->setOldestVersion(oldestVersion);
|
impl->setOldestVersion(oldestVersion);
|
||||||
impl->totalBytes += mallocBytesDelta;
|
impl->totalBytes += mallocBytesDelta;
|
||||||
@@ -2627,47 +2609,19 @@ void internal_setOldestVersion(ConflictSet::Impl *impl, int64_t oldestVersion) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
ConflictSet::Impl *internal_create(int64_t oldestVersion) {
|
|
||||||
mallocBytesDelta = 0;
|
|
||||||
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
|
|
||||||
ConflictSet::Impl{oldestVersion};
|
|
||||||
result->totalBytes += mallocBytesDelta;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void internal_destroy(ConflictSet::Impl *impl) {
|
int64_t ConflictSet::getBytes() const { return impl->totalBytes; }
|
||||||
impl->~Impl();
|
|
||||||
safe_free(impl, sizeof(ConflictSet::Impl));
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; }
|
|
||||||
|
|
||||||
// ==================== END IMPLEMENTATION ====================
|
|
||||||
|
|
||||||
// GCOVR_EXCL_START
|
|
||||||
|
|
||||||
void ConflictSet::check(const ReadRange *reads, Result *results,
|
|
||||||
int count) const {
|
|
||||||
internal_check(impl, reads, results, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
|
||||||
int64_t writeVersion) {
|
|
||||||
internal_addWrites(impl, writes, count, writeVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
|
||||||
internal_setOldestVersion(impl, oldestVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t ConflictSet::getBytes() const { return internal_getBytes(impl); }
|
|
||||||
|
|
||||||
ConflictSet::ConflictSet(int64_t oldestVersion)
|
ConflictSet::ConflictSet(int64_t oldestVersion)
|
||||||
: impl(internal_create(oldestVersion)) {}
|
: impl((mallocBytesDelta = 0,
|
||||||
|
new(safe_malloc(sizeof(Impl))) Impl{oldestVersion})) {
|
||||||
|
impl->totalBytes += mallocBytesDelta;
|
||||||
|
}
|
||||||
|
|
||||||
ConflictSet::~ConflictSet() {
|
ConflictSet::~ConflictSet() {
|
||||||
if (impl) {
|
if (impl) {
|
||||||
internal_destroy(impl);
|
impl->~Impl();
|
||||||
|
safe_free(impl, sizeof(*impl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2688,27 +2642,40 @@ extern "C" {
|
|||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
||||||
ConflictSet_Result *results, int count) {
|
ConflictSet_Result *results, int count) {
|
||||||
internal_check((ConflictSet::Impl *)cs, reads, results, count);
|
((ConflictSet::Impl *)cs)->check(reads, results, count);
|
||||||
}
|
}
|
||||||
__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) {
|
||||||
internal_addWrites((ConflictSet::Impl *)cs, writes, count, writeVersion);
|
auto *impl = ((ConflictSet::Impl *)cs);
|
||||||
|
mallocBytesDelta = 0;
|
||||||
|
impl->addWrites(writes, count, writeVersion);
|
||||||
|
impl->totalBytes += mallocBytesDelta;
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
||||||
internal_setOldestVersion((ConflictSet::Impl *)cs, oldestVersion);
|
auto *impl = ((ConflictSet::Impl *)cs);
|
||||||
|
mallocBytesDelta = 0;
|
||||||
|
impl->setOldestVersion(oldestVersion);
|
||||||
|
impl->totalBytes += mallocBytesDelta;
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void *
|
__attribute__((__visibility__("default"))) void *
|
||||||
ConflictSet_create(int64_t oldestVersion) {
|
ConflictSet_create(int64_t oldestVersion) {
|
||||||
return internal_create(oldestVersion);
|
mallocBytesDelta = 0;
|
||||||
|
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
|
||||||
|
ConflictSet::Impl{oldestVersion};
|
||||||
|
result->totalBytes += mallocBytesDelta;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||||
internal_destroy((ConflictSet::Impl *)cs);
|
using Impl = ConflictSet::Impl;
|
||||||
|
((Impl *)cs)->~Impl();
|
||||||
|
safe_free(cs, sizeof(Impl));
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) int64_t
|
__attribute__((__visibility__("default"))) int64_t
|
||||||
ConflictSet_getBytes(void *cs) {
|
ConflictSet_getBytes(void *cs) {
|
||||||
return internal_getBytes((ConflictSet::Impl *)cs);
|
using Impl = ConflictSet::Impl;
|
||||||
|
return ((Impl *)cs)->totalBytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2944,6 +2911,10 @@ Iterator firstGeq(Node *n, std::string_view key) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
void __throw_length_error(const char *) { __builtin_unreachable(); }
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
#if SHOW_MEMORY
|
#if SHOW_MEMORY
|
||||||
|
|
||||||
int64_t nodeBytes = 0;
|
int64_t nodeBytes = 0;
|
||||||
@@ -3024,7 +2995,6 @@ 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));
|
||||||
|
|||||||
+16
-74
@@ -99,7 +99,8 @@ __attribute__((always_inline)) inline void safe_free(void *p, size_t s) {
|
|||||||
mallocBytesDelta -= s;
|
mallocBytesDelta -= s;
|
||||||
#if SHOW_MEMORY
|
#if SHOW_MEMORY
|
||||||
mallocBytes -= s;
|
mallocBytes -= s;
|
||||||
#endif
|
free(p);
|
||||||
|
#else
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
(char *&)p -= kMallocHeaderSize;
|
(char *&)p -= kMallocHeaderSize;
|
||||||
size_t expected;
|
size_t expected;
|
||||||
@@ -107,6 +108,7 @@ __attribute__((always_inline)) inline void safe_free(void *p, size_t s) {
|
|||||||
assert(s == expected);
|
assert(s == expected);
|
||||||
#endif
|
#endif
|
||||||
free(p);
|
free(p);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== BEGIN ARENA IMPL ====================
|
// ==================== BEGIN ARENA IMPL ====================
|
||||||
@@ -255,65 +257,10 @@ template <class T> struct ArenaAlloc {
|
|||||||
void deallocate(T *, size_t) noexcept {}
|
void deallocate(T *, size_t) noexcept {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T> struct Vector {
|
template <class T> using Vector = std::vector<T, ArenaAlloc<T>>;
|
||||||
static_assert(std::is_trivially_destructible_v<T>);
|
template <class T> auto vector(Arena &arena) {
|
||||||
static_assert(std::is_trivially_copyable_v<T>);
|
return Vector<T>(ArenaAlloc<T>(&arena));
|
||||||
|
}
|
||||||
explicit Vector(Arena *arena)
|
|
||||||
: arena(arena), t(nullptr), size_(0), capacity(0) {}
|
|
||||||
|
|
||||||
void append(std::span<const T> slice) {
|
|
||||||
if (size_ + int(slice.size()) > capacity) {
|
|
||||||
grow(std::max<int>(size_ + slice.size(), capacity * 2));
|
|
||||||
}
|
|
||||||
if (slice.size() > 0) {
|
|
||||||
memcpy(const_cast<std::remove_const_t<T> *>(t) + size_, slice.data(),
|
|
||||||
slice.size() * sizeof(T));
|
|
||||||
}
|
|
||||||
size_ += slice.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_back(const T &t) { append(std::span<const T>(&t, 1)); }
|
|
||||||
|
|
||||||
T *begin() { return t; }
|
|
||||||
T *end() { return t + size_; }
|
|
||||||
T *data() { return t; }
|
|
||||||
T &back() {
|
|
||||||
assert(size_ > 0);
|
|
||||||
return t[size_ - 1];
|
|
||||||
}
|
|
||||||
T &operator[](int i) {
|
|
||||||
assert(i >= 0 && i < size_);
|
|
||||||
return t[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_back() {
|
|
||||||
assert(size_ > 0);
|
|
||||||
--size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size() const { return size_; }
|
|
||||||
|
|
||||||
operator std::span<const T>() const { return std::span(t, size_); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void grow(int newCapacity) {
|
|
||||||
capacity = newCapacity;
|
|
||||||
auto old = std::span<const T>(*this);
|
|
||||||
t = (T *)new (std::align_val_t(alignof(T)), *arena)
|
|
||||||
uint8_t[capacity * sizeof(T)];
|
|
||||||
size_ = 0;
|
|
||||||
append(old);
|
|
||||||
}
|
|
||||||
|
|
||||||
Arena *arena;
|
|
||||||
T *t;
|
|
||||||
int size_;
|
|
||||||
int capacity;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T> auto vector(Arena &arena) { return Vector<T>(&arena); }
|
|
||||||
|
|
||||||
template <class T, class C> using Set = std::set<T, C, ArenaAlloc<T>>;
|
template <class T, class C> using Set = std::set<T, C, ArenaAlloc<T>>;
|
||||||
template <class T, class C = std::less<T>> auto set(Arena &arena) {
|
template <class T, class C = std::less<T>> auto set(Arena &arena) {
|
||||||
return Set<T, C>(ArenaAlloc<T>(&arena));
|
return Set<T, C>(ArenaAlloc<T>(&arena));
|
||||||
@@ -578,18 +525,15 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
explicit TestDriver(const uint8_t *data, size_t size)
|
explicit TestDriver(const uint8_t *data, size_t size)
|
||||||
: arbitrary({data, size}) {}
|
: arbitrary({data, size}) {}
|
||||||
|
|
||||||
int64_t writeVersion = 100;
|
int64_t writeVersion = 0;
|
||||||
int64_t oldestVersion = 0;
|
int64_t oldestVersion = 0;
|
||||||
ConflictSetImpl cs{oldestVersion};
|
ConflictSetImpl cs{oldestVersion};
|
||||||
ReferenceImpl refImpl{oldestVersion};
|
ReferenceImpl refImpl{oldestVersion};
|
||||||
|
|
||||||
constexpr static auto kMaxKeySuffixLen = 8;
|
constexpr static auto kMaxKeyLen = 8;
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
||||||
const int prefixLen = arbitrary.bounded(512);
|
|
||||||
const int prefixByte = arbitrary.randT<uint8_t>();
|
|
||||||
|
|
||||||
// Call until it returns true, for "done". Check internal invariants etc
|
// Call until it returns true, for "done". Check internal invariants etc
|
||||||
// between calls to next.
|
// between calls to next.
|
||||||
bool next() {
|
bool next() {
|
||||||
@@ -600,7 +544,7 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
{
|
{
|
||||||
int numPointWrites = arbitrary.bounded(100);
|
int numPointWrites = arbitrary.bounded(100);
|
||||||
int numRangeWrites = arbitrary.bounded(100);
|
int numRangeWrites = arbitrary.bounded(100);
|
||||||
int64_t v = (writeVersion += arbitrary.bounded(10));
|
int64_t v = ++writeVersion;
|
||||||
auto *writes =
|
auto *writes =
|
||||||
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
||||||
auto keys = set<std::string_view>(arena);
|
auto keys = set<std::string_view>(arena);
|
||||||
@@ -608,10 +552,9 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary.hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = arbitrary.bounded(kMaxKeyLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
arbitrary.randomBytes(begin, keyLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
|
||||||
keys.insert(std::string_view((const char *)begin, keyLen));
|
keys.insert(std::string_view((const char *)begin, keyLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,8 +603,8 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
|
|
||||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
|
|
||||||
oldestVersion =
|
oldestVersion = std::max<int64_t>(writeVersion - arbitrary.bounded(10),
|
||||||
std::min(writeVersion - 10, oldestVersion + arbitrary.bounded(10));
|
oldestVersion);
|
||||||
cs.setOldestVersion(oldestVersion);
|
cs.setOldestVersion(oldestVersion);
|
||||||
refImpl.setOldestVersion(oldestVersion);
|
refImpl.setOldestVersion(oldestVersion);
|
||||||
}
|
}
|
||||||
@@ -676,10 +619,9 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary.hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = arbitrary.bounded(kMaxKeyLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
arbitrary.randomBytes(begin, keyLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
|
||||||
keys.insert(std::string_view((const char *)begin, keyLen));
|
keys.insert(std::string_view((const char *)begin, keyLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
-3
@@ -111,9 +111,6 @@ 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
|
|
||||||
'''
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -0,0 +1,155 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-62
@@ -16,8 +16,6 @@
|
|||||||
* 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"
|
||||||
@@ -270,21 +268,13 @@ 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() {
|
~SkipList() { destroy(); }
|
||||||
#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;
|
||||||
}
|
}
|
||||||
@@ -613,10 +603,6 @@ 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)}
|
||||||
@@ -651,16 +637,13 @@ private:
|
|||||||
SkipList skipList;
|
SkipList skipList;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Internal entry points. Public entry points should just delegate to these
|
void ConflictSet::check(const ReadRange *reads, Result *results,
|
||||||
|
int count) const {
|
||||||
void internal_check(ConflictSet::Impl *impl,
|
|
||||||
const ConflictSet::ReadRange *reads,
|
|
||||||
ConflictSet::Result *results, int count) {
|
|
||||||
impl->check(reads, results, count);
|
impl->check(reads, results, count);
|
||||||
}
|
}
|
||||||
void internal_addWrites(ConflictSet::Impl *impl,
|
|
||||||
const ConflictSet::WriteRange *writes, int count,
|
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
||||||
int64_t writeVersion) {
|
int64_t writeVersion) {
|
||||||
mallocBytesDelta = 0;
|
mallocBytesDelta = 0;
|
||||||
impl->addWrites(writes, count, writeVersion);
|
impl->addWrites(writes, count, writeVersion);
|
||||||
impl->totalBytes += mallocBytesDelta;
|
impl->totalBytes += mallocBytesDelta;
|
||||||
@@ -671,7 +654,7 @@ void internal_addWrites(ConflictSet::Impl *impl,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void internal_setOldestVersion(ConflictSet::Impl *impl, int64_t oldestVersion) {
|
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
||||||
mallocBytesDelta = 0;
|
mallocBytesDelta = 0;
|
||||||
impl->setOldestVersion(oldestVersion);
|
impl->setOldestVersion(oldestVersion);
|
||||||
impl->totalBytes += mallocBytesDelta;
|
impl->totalBytes += mallocBytesDelta;
|
||||||
@@ -681,43 +664,19 @@ void internal_setOldestVersion(ConflictSet::Impl *impl, int64_t oldestVersion) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
ConflictSet::Impl *internal_create(int64_t oldestVersion) {
|
|
||||||
mallocBytesDelta = 0;
|
|
||||||
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
|
|
||||||
ConflictSet::Impl{oldestVersion};
|
|
||||||
result->totalBytes += mallocBytesDelta;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void internal_destroy(ConflictSet::Impl *impl) {
|
int64_t ConflictSet::getBytes() const { return impl->totalBytes; }
|
||||||
impl->~Impl();
|
|
||||||
safe_free(impl, sizeof(ConflictSet::Impl));
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; }
|
|
||||||
|
|
||||||
void ConflictSet::check(const ReadRange *reads, Result *results,
|
|
||||||
int count) const {
|
|
||||||
internal_check(impl, reads, results, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
|
||||||
int64_t writeVersion) {
|
|
||||||
internal_addWrites(impl, writes, count, writeVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
|
||||||
internal_setOldestVersion(impl, oldestVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t ConflictSet::getBytes() const { return internal_getBytes(impl); }
|
|
||||||
|
|
||||||
ConflictSet::ConflictSet(int64_t oldestVersion)
|
ConflictSet::ConflictSet(int64_t oldestVersion)
|
||||||
: impl(internal_create(oldestVersion)) {}
|
: impl((mallocBytesDelta = 0,
|
||||||
|
new(safe_malloc(sizeof(Impl))) Impl{oldestVersion})) {
|
||||||
|
impl->totalBytes += mallocBytesDelta;
|
||||||
|
}
|
||||||
|
|
||||||
ConflictSet::~ConflictSet() {
|
ConflictSet::~ConflictSet() {
|
||||||
if (impl) {
|
if (impl) {
|
||||||
internal_destroy(impl);
|
impl->~Impl();
|
||||||
|
safe_free(impl, sizeof(Impl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,34 +697,37 @@ extern "C" {
|
|||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
||||||
ConflictSet_Result *results, int count) {
|
ConflictSet_Result *results, int count) {
|
||||||
internal_check((ConflictSet::Impl *)cs, reads, results, count);
|
((ConflictSet::Impl *)cs)->check(reads, results, count);
|
||||||
}
|
}
|
||||||
__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) {
|
||||||
internal_addWrites((ConflictSet::Impl *)cs, writes, count, writeVersion);
|
((ConflictSet::Impl *)cs)->addWrites(writes, count, writeVersion);
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
||||||
internal_setOldestVersion((ConflictSet::Impl *)cs, oldestVersion);
|
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion);
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void *
|
__attribute__((__visibility__("default"))) void *
|
||||||
ConflictSet_create(int64_t oldestVersion) {
|
ConflictSet_create(int64_t oldestVersion) {
|
||||||
return internal_create(oldestVersion);
|
return new (safe_malloc(sizeof(ConflictSet::Impl)))
|
||||||
|
ConflictSet::Impl{oldestVersion};
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||||
internal_destroy((ConflictSet::Impl *)cs);
|
using Impl = ConflictSet::Impl;
|
||||||
|
((Impl *)cs)->~Impl();
|
||||||
|
safe_free(cs, sizeof(Impl));
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) int64_t
|
__attribute__((__visibility__("default"))) int64_t
|
||||||
ConflictSet_getBytes(void *cs) {
|
ConflictSet_getBytes(void *cs) {
|
||||||
return internal_getBytes((ConflictSet::Impl *)cs);
|
using Impl = ConflictSet::Impl;
|
||||||
|
return ((Impl *)cs)->totalBytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
__stack_chk_fail@GLIBC_2.17
|
|
||||||
__stack_chk_guard@GLIBC_2.17
|
|
||||||
abort@GLIBC_2.17
|
|
||||||
free@GLIBC_2.17
|
|
||||||
malloc@GLIBC_2.17
|
|
||||||
memcpy@GLIBC_2.17
|
|
||||||
memmove@GLIBC_2.17
|
|
||||||
memset@GLIBC_2.17
|
|
||||||
@@ -5,4 +5,3 @@ _free
|
|||||||
_malloc
|
_malloc
|
||||||
_memcpy
|
_memcpy
|
||||||
_memmove
|
_memmove
|
||||||
dyld_stub_binder
|
|
||||||
-136
@@ -1,136 +0,0 @@
|
|||||||
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()
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user