Compare commits
167 Commits
a4b03bc216
...
v0.0.1
Author | SHA1 | Date | |
---|---|---|---|
71c39f9955 | |||
8cc17158fd | |||
ab211c646a | |||
7af961f141 | |||
a91df62608 | |||
0a1843a161 | |||
4edf0315d9 | |||
14515e186a | |||
b0085df5ad | |||
76a7e17b29 | |||
5cf43d1bfa | |||
25cc427ec5 | |||
c15c2e7b44 | |||
a4d1f91670 | |||
b7cdecaf71 | |||
cda28643a6 | |||
cdb5360b9a | |||
ef224a60f4 | |||
6222b74787 | |||
19edc6f78f | |||
3f9d01c46a | |||
db03c6f901 | |||
c1698b040b | |||
2e08b54785 | |||
aa6f237d50 | |||
becfd25139 | |||
d78b36821b | |||
ce79b47fbe | |||
727b7e642a | |||
cb4c2b7e1e | |||
ef9b789745 | |||
edd7bcaa1e | |||
be8ac879c5 | |||
83c7f66d67 | |||
a5710b8282 | |||
c31eebd5de | |||
ddeb059968 | |||
5a0bcf9a5a | |||
97717cec86 | |||
6a13c43a78 | |||
c6c438bae2 | |||
7d4f832b43 | |||
5b0c3c2428 | |||
f2b5e9b0bf | |||
8e0e65dac6 | |||
5aab76847a | |||
1a51aa00e5 | |||
3975bada0c | |||
a5330b6e23 | |||
2e246ec6a4 | |||
6d7e3c9849 | |||
671da5d096 | |||
303b368fc5 | |||
9f5a68e2c0 | |||
dfbb3ce5f1 | |||
e7719b6e0b | |||
83fedf1f9e | |||
8556caf360 | |||
9d13ca84f5 | |||
a79436ee9b | |||
e9c8537cf2 | |||
5b988efe6f | |||
e35d698b21 | |||
30496d14e7 | |||
eb93157ddf | |||
9cafef8bbb | |||
6f81580953 | |||
429fe5baed | |||
a0451e4423 | |||
a9b3d3d1c9 | |||
b817e3c749 | |||
ee36bda8f8 | |||
a8f4bd91c8 | |||
35086ee66a | |||
0f795cf163 | |||
a07c93ffff | |||
c68f563017 | |||
6b6a9bace9 | |||
3cb0765fdd | |||
351ff3df3b | |||
e818648cdc | |||
12540b8713 | |||
c2606cd26a | |||
4b72fc0b7b | |||
a9caa0249e | |||
08b2b7f41a | |||
26bd8b94cc | |||
55eaef5b1d | |||
797e6b4a3e | |||
ee86b5289b | |||
b779c0f6f7 | |||
ef802b8acd | |||
5371c2bede | |||
75c304bbe7 | |||
aefb83dbc6 | |||
b0ac7e41b9 | |||
4b6b2747bf | |||
1496aa106b | |||
71e117965e | |||
471b276947 | |||
b721bc80a9 | |||
5e4eab55fb | |||
1dcb380c73 | |||
87d650ff00 | |||
b8f6a8edf2 | |||
01f1d5850f | |||
cd567383c3 | |||
53a442abf9 | |||
6e212847ac | |||
44a023c2f4 | |||
e32bea7b29 | |||
504a93bb10 | |||
b79d8f71d3 | |||
34430dbbe7 | |||
06fcb2531e | |||
bd24a362e3 | |||
1437280ec7 | |||
e5051bac9e | |||
733f32b22e | |||
3fb8bf7c3b | |||
0c8cb8faa5 | |||
93e487c8fb | |||
d91538dcad | |||
43a768d152 | |||
2989866a6d | |||
60df97847c | |||
0038382661 | |||
782abc70d6 | |||
8802d17acd | |||
02afd47d8f | |||
987e93b190 | |||
81263f5abf | |||
2689901637 | |||
87dd70c4b6 | |||
451ac5b2b6 | |||
a8042ab20d | |||
8a36e72640 | |||
1519216d08 | |||
f2cd05c29d | |||
5e1fb1dac5 | |||
d1a6b293e9 | |||
be43143891 | |||
53bc36f628 | |||
0f360fa806 | |||
00389936a8 | |||
04f75d57e9 | |||
6a0344e821 | |||
2fcf3da29f | |||
c8495b1695 | |||
d81d02f11d | |||
be5f1b67c8 | |||
ec3aec4dff | |||
9a4eed9453 | |||
30abf7833d | |||
d9c0d24e58 | |||
3f121dc681 | |||
3ea0f405f2 | |||
1811342cb6 | |||
16aa52c071 | |||
b68e04ba80 | |||
45e8d68234 | |||
760a99098a | |||
e6a88852b3 | |||
b97f611a3c | |||
10436096d1 | |||
ad11782029 | |||
8bf3aa7f56 |
2
.clangd
2
.clangd
@@ -1,2 +1,2 @@
|
||||
CompileFlags:
|
||||
Add: [-DENABLE_MAIN, -UNDEBUG, -DENABLE_FUZZ, -fexceptions]
|
||||
Add: [-DENABLE_MAIN, -UNDEBUG, -DENABLE_FUZZ, -DTHREAD_TEST, -fexceptions]
|
||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"*.tikz": "latex"
|
||||
}
|
||||
},
|
||||
"latex-workshop.view.pdf.invertMode.enabled": "compat",
|
||||
"latex-workshop.view.pdf.invert": 1,
|
||||
"latex-workshop.view.pdf.invertMode.sepia": 1,
|
||||
"latex-workshop.view.pdf.invertMode.grayscale": 0.5
|
||||
}
|
38
Bench.cpp
38
Bench.cpp
@@ -2,7 +2,6 @@
|
||||
#include "Internal.h"
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#if SHOW_MEMORY
|
||||
void showMemory(const ConflictSet &cs);
|
||||
@@ -35,7 +34,7 @@ ConflictSet::ReadRange singleton(Arena &arena, std::span<const uint8_t> key) {
|
||||
std::span<uint8_t>(new (arena) uint8_t[key.size() + 1], key.size() + 1);
|
||||
memcpy(r.data(), key.data(), key.size());
|
||||
r[key.size()] = 0;
|
||||
return {key.data(), int(key.size()), r.data(), int(r.size())};
|
||||
return {{key.data(), int(key.size())}, {r.data(), int(r.size())}, 0};
|
||||
}
|
||||
|
||||
ConflictSet::ReadRange prefixRange(Arena &arena, std::span<const uint8_t> key) {
|
||||
@@ -53,7 +52,7 @@ ConflictSet::ReadRange prefixRange(Arena &arena, std::span<const uint8_t> key) {
|
||||
auto r = std::span<uint8_t>(new (arena) uint8_t[index + 1], index + 1);
|
||||
memcpy(r.data(), key.data(), index + 1);
|
||||
r[r.size() - 1]++;
|
||||
return {key.data(), int(key.size()), r.data(), int(r.size())};
|
||||
return {{key.data(), int(key.size())}, {r.data(), int(r.size())}, 0};
|
||||
}
|
||||
|
||||
void benchConflictSet() {
|
||||
@@ -61,7 +60,6 @@ void benchConflictSet() {
|
||||
ConflictSet cs{0};
|
||||
|
||||
bench.batch(kOpsPerTx);
|
||||
bench.minEpochIterations(10000);
|
||||
|
||||
int64_t version = 0;
|
||||
|
||||
@@ -78,10 +76,9 @@ void benchConflictSet() {
|
||||
w.begin.len = r.begin.len;
|
||||
w.end.p = r.end.p;
|
||||
w.end.len = 0;
|
||||
w.writeVersion = version + 1;
|
||||
writes.push_back(w);
|
||||
}
|
||||
cs.addWrites(writes.data(), writes.size());
|
||||
cs.addWrites(writes.data(), writes.size(), version + 1);
|
||||
++version;
|
||||
}
|
||||
|
||||
@@ -112,11 +109,10 @@ void benchConflictSet() {
|
||||
w.begin.len = begin.size();
|
||||
w.end.p = end.data();
|
||||
w.end.len = end.size();
|
||||
w.writeVersion = version + 1;
|
||||
writes.push_back(w);
|
||||
}
|
||||
cs.addWrites(writes.data(), kOpsPerTx, version + 1);
|
||||
++version;
|
||||
cs.addWrites(writes.data(), kOpsPerTx);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -189,17 +185,12 @@ void benchConflictSet() {
|
||||
|
||||
while (version < kMvccWindow) {
|
||||
auto v = ++version;
|
||||
writes[0].writeVersion = v;
|
||||
cs.addWrites(writes.data(), 1);
|
||||
cs.addWrites(writes.data(), 1, v);
|
||||
}
|
||||
|
||||
bench.run("point writes", [&]() {
|
||||
auto v = ++version;
|
||||
for (auto &w : writes) {
|
||||
w.writeVersion = v;
|
||||
}
|
||||
cs.addWrites(writes.data(), writes.size());
|
||||
cs.setOldestVersion(version - kMvccWindow);
|
||||
cs.addWrites(writes.data(), writes.size(), v);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -219,11 +210,7 @@ void benchConflictSet() {
|
||||
|
||||
bench.run("prefix writes", [&]() {
|
||||
auto v = ++version;
|
||||
for (auto &w : writes) {
|
||||
w.writeVersion = v;
|
||||
}
|
||||
cs.addWrites(writes.data(), writes.size());
|
||||
cs.setOldestVersion(version - kMvccWindow);
|
||||
cs.addWrites(writes.data(), writes.size(), v);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -243,16 +230,14 @@ void benchConflictSet() {
|
||||
|
||||
bench.run("range writes", [&]() {
|
||||
auto v = ++version;
|
||||
for (auto &w : writes) {
|
||||
w.writeVersion = v;
|
||||
}
|
||||
cs.addWrites(writes.data(), writes.size());
|
||||
cs.setOldestVersion(version - kMvccWindow);
|
||||
cs.addWrites(writes.data(), writes.size(), v);
|
||||
});
|
||||
}
|
||||
|
||||
bench.batch(1);
|
||||
|
||||
bench.warmup(10000);
|
||||
|
||||
{
|
||||
bench.run("monotonic increasing point writes", [&]() {
|
||||
auto v = ++version;
|
||||
@@ -263,12 +248,11 @@ void benchConflictSet() {
|
||||
auto x = __builtin_bswap64(version);
|
||||
memcpy(b, &x, 8);
|
||||
|
||||
w.writeVersion = v;
|
||||
w.begin.p = b;
|
||||
w.begin.len = 8;
|
||||
w.end.len = 0;
|
||||
w.end.p = b;
|
||||
cs.addWrites(&w, 1);
|
||||
cs.addWrites(&w, 1, v);
|
||||
cs.setOldestVersion(version - kMvccWindow);
|
||||
});
|
||||
}
|
||||
|
113
CMakeLists.txt
113
CMakeLists.txt
@@ -1,9 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(
|
||||
conflict_set
|
||||
conflict-set
|
||||
VERSION 0.0.1
|
||||
DESCRIPTION
|
||||
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
||||
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
||||
LANGUAGES C CXX)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
@@ -21,7 +22,11 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
"MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
add_compile_options(-fdata-sections -ffunction-sections)
|
||||
add_compile_options(-fdata-sections -ffunction-sections -Wswitch-enum
|
||||
-Werror=switch-enum)
|
||||
|
||||
option(USE_SIMD_FALLBACK
|
||||
"Use fallback implementations of functions that use SIMD" OFF)
|
||||
|
||||
# This is encouraged according to
|
||||
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
||||
@@ -41,18 +46,20 @@ endif()
|
||||
include(CheckIncludeFileCXX)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
cmake_push_check_state()
|
||||
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
||||
check_include_file_cxx("immintrin.h" HAS_AVX)
|
||||
if(HAS_AVX)
|
||||
if(NOT USE_SIMD_FALLBACK)
|
||||
cmake_push_check_state()
|
||||
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
||||
check_include_file_cxx("immintrin.h" HAS_AVX)
|
||||
if(HAS_AVX)
|
||||
add_compile_options(-mavx)
|
||||
add_compile_definitions(HAS_AVX)
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
|
||||
check_include_file_cxx("arm_neon.h" HAS_ARM_NEON)
|
||||
if(HAS_ARM_NEON)
|
||||
check_include_file_cxx("arm_neon.h" HAS_ARM_NEON)
|
||||
if(HAS_ARM_NEON)
|
||||
add_compile_definitions(HAS_ARM_NEON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
|
||||
@@ -76,22 +83,22 @@ if(NOT APPLE)
|
||||
LINKER:--version-script=${CMAKE_SOURCE_DIR}/linker.map)
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME}_static STATIC
|
||||
add_library(${PROJECT_NAME}-static STATIC
|
||||
$<TARGET_OBJECTS:${PROJECT_NAME}_object>)
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set_target_properties(${PROJECT_NAME}_static PROPERTIES LINKER_LANGUAGE C)
|
||||
set_target_properties(${PROJECT_NAME}-static PROPERTIES LINKER_LANGUAGE C)
|
||||
endif()
|
||||
|
||||
if(NOT APPLE AND CMAKE_OBJCOPY)
|
||||
add_custom_command(
|
||||
TARGET conflict_set_static
|
||||
TARGET conflict-set-static
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_OBJCOPY} --keep-global-symbols=${CMAKE_SOURCE_DIR}/symbols.txt
|
||||
$<TARGET_FILE:${PROJECT_NAME}_static>)
|
||||
$<TARGET_FILE:${PROJECT_NAME}-static>)
|
||||
endif()
|
||||
|
||||
set(TEST_FLAGS -Wall -Wextra -Wpedantic -Wunreachable-code -UNDEBUG)
|
||||
set(TEST_FLAGS -Wall -Wextra -Wunreachable-code -Wpedantic -UNDEBUG)
|
||||
|
||||
include(CTest)
|
||||
|
||||
@@ -102,13 +109,25 @@ if(BUILD_TESTING)
|
||||
target_compile_options(skip_list PRIVATE -fPIC -fno-exceptions
|
||||
-fvisibility=hidden)
|
||||
target_include_directories(skip_list PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
set_target_properties(skip_list PROPERTIES LINKER_LANGUAGE C)
|
||||
set_target_properties(skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||
"${CMAKE_BINARY_DIR}/skip_list")
|
||||
set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||
set_target_properties(skip_list PROPERTIES VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||
|
||||
# Shared library version of a std::unordered_map-based conflict set (point
|
||||
# queries only)
|
||||
add_library(hash_table SHARED HashTable.cpp)
|
||||
target_compile_options(hash_table PRIVATE -fPIC -fno-exceptions
|
||||
-fvisibility=hidden)
|
||||
target_include_directories(hash_table PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
set_target_properties(hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||
"${CMAKE_BINARY_DIR}/hash_table")
|
||||
set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||
set_target_properties(
|
||||
hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
||||
${PROJECT_VERSION_MAJOR})
|
||||
|
||||
add_executable(conflict_set_main ConflictSet.cpp)
|
||||
target_include_directories(conflict_set_main
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
@@ -141,6 +160,10 @@ if(BUILD_TESTING)
|
||||
|
||||
add_executable(fuzz_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
||||
target_compile_options(fuzz_driver PRIVATE ${TEST_FLAGS})
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
target_compile_options(fuzz_driver PRIVATE -fsanitize=address,undefined)
|
||||
target_link_options(fuzz_driver PRIVATE -fsanitize=address,undefined)
|
||||
endif()
|
||||
target_compile_definitions(fuzz_driver PRIVATE ENABLE_FUZZ)
|
||||
target_include_directories(fuzz_driver
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
@@ -149,16 +172,41 @@ if(BUILD_TESTING)
|
||||
add_test(NAME conflict_set_fuzz_${hash} COMMAND fuzz_driver ${TEST})
|
||||
endforeach()
|
||||
|
||||
# tsan
|
||||
|
||||
if(NOT CMAKE_CROSSCOMPILING AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
||||
target_compile_options(tsan_driver PRIVATE ${TEST_FLAGS} -fsanitize=thread)
|
||||
target_link_options(tsan_driver PRIVATE -fsanitize=thread)
|
||||
target_compile_definitions(tsan_driver PRIVATE ENABLE_FUZZ THREAD_TEST)
|
||||
target_include_directories(tsan_driver
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
foreach(TEST ${CORPUS_TESTS})
|
||||
get_filename_component(hash ${TEST} NAME)
|
||||
add_test(NAME conflict_set_tsan_${hash} COMMAND tsan_driver ${TEST})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
add_executable(driver TestDriver.cpp)
|
||||
target_compile_options(driver PRIVATE ${TEST_FLAGS})
|
||||
target_link_libraries(driver PRIVATE ${PROJECT_NAME})
|
||||
|
||||
add_executable(script_test ScriptTest.cpp)
|
||||
target_compile_options(script_test PRIVATE ${TEST_FLAGS})
|
||||
target_link_libraries(script_test PRIVATE ${PROJECT_NAME})
|
||||
|
||||
file(GLOB SCRIPT_TESTS ${CMAKE_SOURCE_DIR}/script_tests/*)
|
||||
foreach(TEST ${SCRIPT_TESTS})
|
||||
get_filename_component(name ${TEST} NAME)
|
||||
add_test(NAME conflict_set_script_${name} COMMAND script_test ${TEST})
|
||||
endforeach()
|
||||
|
||||
add_executable(driver_skip_list TestDriver.cpp)
|
||||
target_compile_options(driver_skip_list PRIVATE ${TEST_FLAGS})
|
||||
target_link_libraries(driver_skip_list PRIVATE skip_list)
|
||||
|
||||
find_program(VALGRIND_EXE valgrind)
|
||||
if(VALGRIND_EXE)
|
||||
if(VALGRIND_EXE AND NOT CMAKE_CROSSCOMPILING)
|
||||
add_test(NAME conflict_set_blackbox_valgrind
|
||||
COMMAND ${VALGRIND_EXE} --error-exitcode=99 --
|
||||
$<TARGET_FILE:driver> ${CORPUS_TESTS})
|
||||
@@ -199,7 +247,7 @@ if(BUILD_TESTING)
|
||||
NAME conflict_set_static_symbols
|
||||
COMMAND
|
||||
${CMAKE_SOURCE_DIR}/test_symbols.sh
|
||||
$<TARGET_FILE:${PROJECT_NAME}_static> ${CMAKE_SOURCE_DIR}/symbols.txt)
|
||||
$<TARGET_FILE:${PROJECT_NAME}-static> ${CMAKE_SOURCE_DIR}/symbols.txt)
|
||||
endif()
|
||||
|
||||
# bench
|
||||
@@ -207,10 +255,6 @@ if(BUILD_TESTING)
|
||||
add_executable(conflict_set_bench Bench.cpp)
|
||||
target_link_libraries(conflict_set_bench PRIVATE ${PROJECT_NAME})
|
||||
set_target_properties(conflict_set_bench PROPERTIES SKIP_BUILD_RPATH ON)
|
||||
# target_compile_options(conflict_set_bench PRIVATE
|
||||
# "-fsanitize=address,undefined,fuzzer")
|
||||
# target_link_options(conflict_set_bench PRIVATE
|
||||
# "-fsanitize=address,undefined,fuzzer")
|
||||
|
||||
add_executable(real_data_bench RealDataBench.cpp)
|
||||
target_link_libraries(real_data_bench PRIVATE ${PROJECT_NAME})
|
||||
@@ -219,7 +263,20 @@ endif()
|
||||
# packaging
|
||||
|
||||
set(CPACK_PACKAGE_CONTACT andrew@weaselab.dev)
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
|
||||
set(CPACK_PACKAGE_VENDOR "Weaselab")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
||||
|
||||
# rpm
|
||||
set(CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
|
||||
set(CPACK_RPM_SPEC_INSTALL_POST "/bin/true") # avoid stripping
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "Apache 2.0")
|
||||
set(CPACK_RPM_FILE_NAME RPM-DEFAULT)
|
||||
|
||||
# deb
|
||||
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
|
||||
|
||||
include(CPack)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
@@ -230,7 +287,7 @@ target_include_directories(
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>)
|
||||
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}_static
|
||||
${PROJECT_NAME}-static
|
||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>)
|
||||
|
||||
@@ -239,13 +296,13 @@ set_target_properties(
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_static
|
||||
EXPORT ConflictSetConfig
|
||||
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-static
|
||||
EXPORT conflict-setConfig
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(DIRECTORY include/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
||||
|
||||
install(EXPORT ConflictSetConfig
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/ConflictSet/cmake)
|
||||
install(EXPORT conflict-setConfig
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/conflict-set/cmake)
|
||||
|
1983
ConflictSet.cpp
1983
ConflictSet.cpp
File diff suppressed because it is too large
Load Diff
53
Dockerfile
53
Dockerfile
@@ -15,6 +15,7 @@ RUN TZ=America/Los_Angeles DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
curl \
|
||||
doxygen \
|
||||
file \
|
||||
g++-aarch64-linux-gnu \
|
||||
gcovr \
|
||||
git \
|
||||
gperf \
|
||||
@@ -23,17 +24,20 @@ RUN TZ=America/Los_Angeles DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
ninja-build \
|
||||
pre-commit \
|
||||
python3-requests \
|
||||
qemu-user \
|
||||
rpm \
|
||||
texlive-full \
|
||||
zstd
|
||||
|
||||
# Install recent valgrind from source
|
||||
RUN curl -Ls https://sourceware.org/pub/valgrind/valgrind-3.20.0.tar.bz2 -o valgrind.tar.bz2 && \
|
||||
echo "8536c031dbe078d342f121fa881a9ecd205cb5a78e639005ad570011bdb9f3c6 valgrind.tar.bz2" > valgrind-sha.txt && \
|
||||
RUN curl -Ls https://sourceware.org/pub/valgrind/valgrind-3.22.0.tar.bz2 -o valgrind.tar.bz2 && \
|
||||
echo "c811db5add2c5f729944caf47c4e7a65dcaabb9461e472b578765dd7bf6d2d4c valgrind.tar.bz2" > valgrind-sha.txt && \
|
||||
sha256sum --quiet -c valgrind-sha.txt && \
|
||||
mkdir valgrind && \
|
||||
tar --strip-components 1 --no-same-owner --no-same-permissions --directory valgrind -xjf valgrind.tar.bz2 && \
|
||||
cd valgrind && \
|
||||
./configure --enable-only64bit --enable-lto && \
|
||||
make && \
|
||||
make -j`nproc` && \
|
||||
make install && \
|
||||
cd .. && \
|
||||
rm -rf /tmp/*
|
||||
@@ -42,49 +46,6 @@ RUN curl -Ls https://sourceware.org/pub/valgrind/valgrind-3.20.0.tar.bz2 -o valg
|
||||
ENV CC=clang
|
||||
ENV CXX=clang++
|
||||
|
||||
# Install recent flatbuffers from source
|
||||
RUN curl -Ls https://github.com/google/flatbuffers/archive/refs/tags/v23.3.3.tar.gz -o flatbuffers.tar.gz && \
|
||||
echo "8aff985da30aaab37edf8e5b02fda33ed4cbdd962699a8e2af98fdef306f4e4d flatbuffers.tar.gz" > flatbuffers-sha.txt && \
|
||||
sha256sum --quiet -c flatbuffers-sha.txt && \
|
||||
mkdir flatbuffers && \
|
||||
tar --strip-components 1 --no-same-owner --no-same-permissions --directory flatbuffers -xf flatbuffers.tar.gz && \
|
||||
cd flatbuffers && \
|
||||
cmake -S. -B build -G Ninja -DCMAKE_BUILD_TYPE=Release && \
|
||||
ninja -C build install && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Build msan-instrumented libc++ (llvmorg-16.0.0)
|
||||
RUN curl -Ls https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.0/llvm-project-16.0.0.src.tar.xz -o llvm-project.tar.gz && \
|
||||
echo "9a56d906a2c81f16f06efc493a646d497c53c2f4f28f0cb1f3c8da7f74350254 llvm-project.tar.gz" > llvm-project-sha.txt && \
|
||||
sha256sum --quiet -c llvm-project-sha.txt && \
|
||||
mkdir llvm-project && \
|
||||
tar --strip-components 1 --no-same-owner --no-same-permissions --directory llvm-project -xf llvm-project.tar.gz && \
|
||||
cmake -Sllvm-project/runtimes -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/llvm-msan \
|
||||
-DLLVM_USE_SANITIZER=MemoryWithOrigins && \
|
||||
ninja -C build cxx cxxabi && \
|
||||
ninja -C build install-cxx install-cxxabi && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Install bloaty from source
|
||||
RUN curl -Ls https://github.com/google/bloaty/releases/download/v1.1/bloaty-1.1.tar.bz2 -o bloaty.tar.bz2 && \
|
||||
echo "a308d8369d5812aba45982e55e7c3db2ea4780b7496a5455792fb3dcba9abd6f bloaty.tar.bz2" > bloaty-sha.txt && \
|
||||
sha256sum --quiet -c bloaty-sha.txt && \
|
||||
mkdir bloaty && \
|
||||
tar --strip-components 1 --no-same-owner --no-same-permissions --directory bloaty -xf bloaty.tar.bz2 && \
|
||||
cd bloaty && \
|
||||
cmake -S. -B build -G Ninja && \
|
||||
ninja -C build install && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get upgrade -y
|
||||
RUN TZ=America/Los_Angeles DEBIAN_FRONTEND=noninteractive apt-get install -y texlive-full
|
||||
|
||||
# Try to have all the pre-commit hooks we'll need already initialized
|
||||
COPY .pre-commit-config.yaml /tmp/
|
||||
RUN git init && pre-commit install-hooks
|
||||
|
@@ -2,10 +2,12 @@
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
auto doTest = [&]() {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::ifstream t(argv[i], std::ios::binary);
|
||||
std::stringstream buffer;
|
||||
@@ -13,4 +15,12 @@ int main(int argc, char **argv) {
|
||||
auto str = buffer.str();
|
||||
LLVMFuzzerTestOneInput((const uint8_t *)str.data(), str.size());
|
||||
}
|
||||
};
|
||||
#ifdef THREAD_TEST
|
||||
std::thread thread2{doTest};
|
||||
#endif
|
||||
doTest();
|
||||
#ifdef THREAD_TEST
|
||||
thread2.join();
|
||||
#endif
|
||||
}
|
||||
|
154
HashTable.cpp
Normal file
154
HashTable.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "ConflictSet.h"
|
||||
#include "Internal.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// This implementation isn't correct for range queries :). It's just intended as
|
||||
// a reference for performance comparison with point queries.
|
||||
|
||||
// struct is from "https://www.cppstories.com/2021/heterogeneous-access-cpp20/"
|
||||
struct string_hash {
|
||||
using is_transparent = void;
|
||||
[[nodiscard]] size_t operator()(std::string_view txt) const {
|
||||
return std::hash<std::string_view>{}(txt);
|
||||
}
|
||||
[[nodiscard]] size_t operator()(const std::string &txt) const {
|
||||
return std::hash<std::string>{}(txt);
|
||||
}
|
||||
};
|
||||
|
||||
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) {}
|
||||
void check(const ConflictSet::ReadRange *reads, ConflictSet::Result *results,
|
||||
int count) const {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto key =
|
||||
std::string_view((const char *)reads[i].begin.p, reads[i].begin.len);
|
||||
auto version = reads[i].readVersion;
|
||||
if (version < oldestVersion) {
|
||||
results[i] = TooOld;
|
||||
continue;
|
||||
}
|
||||
auto iter = map.find(key);
|
||||
results[i] =
|
||||
iter == map.end() || iter->second <= version ? Commit : Conflict;
|
||||
}
|
||||
}
|
||||
|
||||
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
||||
int64_t writeVersion) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto &max = map[std::string((const char *)writes[i].begin.p,
|
||||
writes[i].begin.len)];
|
||||
assert(writeVersion >= max);
|
||||
max = writeVersion;
|
||||
keyUpdates += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void setOldestVersion(int64_t oldestVersion) {
|
||||
if (oldestVersion <= this->oldestVersion) {
|
||||
return;
|
||||
}
|
||||
this->oldestVersion = oldestVersion;
|
||||
if (keyUpdates < 100) {
|
||||
return;
|
||||
}
|
||||
auto iter = map.find(removalKey);
|
||||
while (keyUpdates > 0) {
|
||||
if (iter == map.end()) {
|
||||
iter = map.begin();
|
||||
}
|
||||
for (; iter != map.end(); --keyUpdates) {
|
||||
if (iter->second <= oldestVersion) {
|
||||
iter = map.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (iter == map.end()) {
|
||||
removalKey.clear();
|
||||
} else {
|
||||
removalKey = iter->first;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t keyUpdates = 0;
|
||||
int64_t oldestVersion;
|
||||
std::unordered_map<std::string, int64_t, string_hash, std::equal_to<>> map;
|
||||
std::string removalKey;
|
||||
};
|
||||
|
||||
void ConflictSet::check(const ReadRange *reads, Result *results,
|
||||
int count) const {
|
||||
return impl->check(reads, results, count);
|
||||
}
|
||||
|
||||
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
||||
int64_t writeVersion) {
|
||||
return impl->addWrites(writes, count, writeVersion);
|
||||
}
|
||||
|
||||
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
||||
return impl->setOldestVersion(oldestVersion);
|
||||
}
|
||||
|
||||
int64_t ConflictSet::getBytes() const { return -1; }
|
||||
|
||||
ConflictSet::ConflictSet(int64_t oldestVersion)
|
||||
: impl(new (safe_malloc(sizeof(Impl))) Impl{oldestVersion}) {}
|
||||
|
||||
ConflictSet::~ConflictSet() {
|
||||
if (impl) {
|
||||
impl->~Impl();
|
||||
safe_free(impl, sizeof(Impl));
|
||||
}
|
||||
}
|
||||
|
||||
ConflictSet::ConflictSet(ConflictSet &&other) noexcept
|
||||
: impl(std::exchange(other.impl, nullptr)) {}
|
||||
|
||||
ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept {
|
||||
impl = std::exchange(other.impl, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
using ConflictSet_Result = ConflictSet::Result;
|
||||
using ConflictSet_Key = ConflictSet::Key;
|
||||
using ConflictSet_ReadRange = ConflictSet::ReadRange;
|
||||
using ConflictSet_WriteRange = ConflictSet::WriteRange;
|
||||
|
||||
extern "C" {
|
||||
__attribute__((__visibility__("default"))) void
|
||||
ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
||||
ConflictSet_Result *results, int count) {
|
||||
((ConflictSet::Impl *)cs)->check(reads, results, count);
|
||||
}
|
||||
__attribute__((__visibility__("default"))) void
|
||||
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
||||
int64_t writeVersion) {
|
||||
((ConflictSet::Impl *)cs)->addWrites(writes, count, writeVersion);
|
||||
}
|
||||
__attribute__((__visibility__("default"))) void
|
||||
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
||||
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion);
|
||||
}
|
||||
__attribute__((__visibility__("default"))) void *
|
||||
ConflictSet_create(int64_t oldestVersion) {
|
||||
return new (safe_malloc(sizeof(ConflictSet::Impl)))
|
||||
ConflictSet::Impl{oldestVersion};
|
||||
}
|
||||
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||
using Impl = ConflictSet::Impl;
|
||||
((Impl *)cs)->~Impl();
|
||||
safe_free(cs, sizeof(Impl));
|
||||
}
|
||||
__attribute__((__visibility__("default"))) int64_t
|
||||
ConflictSet_getBytes(void *cs) {
|
||||
using Impl = ConflictSet::Impl;
|
||||
return -1;
|
||||
}
|
||||
}
|
173
Internal.h
173
Internal.h
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "ConflictSet.h"
|
||||
|
||||
using namespace weaselab;
|
||||
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <compare>
|
||||
@@ -10,10 +12,12 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <inttypes.h>
|
||||
#include <latch>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -35,16 +39,76 @@ operator<=>(const std::span<const uint8_t> &lhs,
|
||||
return lhs.size() <=> rhs.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto operator<=>(const std::span<const uint8_t> &lhs,
|
||||
const ConflictSet::Key &rhs) noexcept {
|
||||
int cl = std::min<int>(lhs.size(), rhs.len);
|
||||
if (cl > 0) {
|
||||
if (auto c = memcmp(lhs.data(), rhs.p, cl) <=> 0; c != 0) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return lhs.size() <=> size_t(rhs.len);
|
||||
}
|
||||
|
||||
// This header contains code that we want to reuse outside of ConflictSet.cpp or
|
||||
// want to exclude from coverage since it's only testing related.
|
||||
|
||||
// GCOVR_EXCL_START
|
||||
|
||||
#if SHOW_MEMORY
|
||||
inline int64_t mallocBytes = 0;
|
||||
inline int64_t peakMallocBytes = 0;
|
||||
#endif
|
||||
|
||||
inline thread_local int64_t mallocBytesDelta = 0;
|
||||
|
||||
#ifndef NDEBUG
|
||||
constexpr auto kMallocHeaderSize = 16;
|
||||
#endif
|
||||
|
||||
// malloc that aborts on OOM and thus always returns a non-null pointer. Must be
|
||||
// paired with `safe_free`.
|
||||
__attribute__((always_inline)) inline void *safe_malloc(size_t s) {
|
||||
if (void *p = malloc(s)) {
|
||||
return p;
|
||||
mallocBytesDelta += s;
|
||||
#if SHOW_MEMORY
|
||||
mallocBytes += s;
|
||||
if (mallocBytes > peakMallocBytes) {
|
||||
peakMallocBytes = mallocBytes;
|
||||
}
|
||||
#endif
|
||||
void *p = malloc(s
|
||||
#ifndef NDEBUG
|
||||
+ kMallocHeaderSize
|
||||
#endif
|
||||
);
|
||||
if (p == nullptr) {
|
||||
abort();
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
memcpy(p, &s, sizeof(s));
|
||||
(char *&)p += kMallocHeaderSize;
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
// Must be paired with `safe_malloc`.
|
||||
//
|
||||
// There's nothing safer about this than free. Only called safe_free for
|
||||
// symmetry with safe_malloc.
|
||||
__attribute__((always_inline)) inline void safe_free(void *p, size_t s) {
|
||||
mallocBytesDelta -= s;
|
||||
#if SHOW_MEMORY
|
||||
mallocBytes -= s;
|
||||
free(p);
|
||||
#else
|
||||
#ifndef NDEBUG
|
||||
(char *&)p -= kMallocHeaderSize;
|
||||
size_t expected;
|
||||
memcpy(&expected, p, sizeof(expected));
|
||||
assert(s == expected);
|
||||
#endif
|
||||
free(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ==================== BEGIN ARENA IMPL ====================
|
||||
@@ -129,7 +193,7 @@ inline Arena::Arena(int initialSize) : impl(nullptr) {
|
||||
inline void onDestroy(Arena::ArenaImpl *impl) {
|
||||
while (impl) {
|
||||
auto *prev = impl->prev;
|
||||
free(impl);
|
||||
safe_free(impl, sizeof(Arena::ArenaImpl) + impl->capacity);
|
||||
impl = prev;
|
||||
}
|
||||
}
|
||||
@@ -349,34 +413,6 @@ inline uint32_t Arbitrary::bounded(uint32_t s) {
|
||||
|
||||
// ==================== END ARBITRARY IMPL ====================
|
||||
|
||||
// ==================== BEGIN UTILITIES IMPL ====================
|
||||
|
||||
// Call Stepwise::step for each element of remaining until it returns true.
|
||||
// Applies a permutation to `remaining` as a side effect.
|
||||
template <class Stepwise> void runInterleaved(std::span<Stepwise> remaining) {
|
||||
while (remaining.size() > 0) {
|
||||
for (int i = 0; i < int(remaining.size());) {
|
||||
bool done = remaining[i].step();
|
||||
if (done) {
|
||||
if (i != int(remaining.size()) - 1) {
|
||||
using std::swap;
|
||||
swap(remaining[i], remaining.back());
|
||||
}
|
||||
remaining = remaining.subspan(0, remaining.size() - 1);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Stepwise> void runSequential(std::span<Stepwise> remaining) {
|
||||
for (auto &r : remaining) {
|
||||
while (!r.step()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ReferenceImpl {
|
||||
explicit ReferenceImpl(int64_t oldestVersion) : oldestVersion(oldestVersion) {
|
||||
writeVersionMap[""] = oldestVersion;
|
||||
@@ -404,7 +440,8 @@ struct ReferenceImpl {
|
||||
: ConflictSet::Commit;
|
||||
}
|
||||
}
|
||||
void addWrites(const ConflictSet::WriteRange *writes, int count) {
|
||||
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
||||
int64_t writeVersion) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto begin =
|
||||
std::string((const char *)writes[i].begin.p, writes[i].begin.len);
|
||||
@@ -412,7 +449,6 @@ struct ReferenceImpl {
|
||||
writes[i].end.len == 0
|
||||
? begin + std::string("\x00", 1)
|
||||
: std::string((const char *)writes[i].end.p, writes[i].end.len);
|
||||
auto writeVersion = writes[i].writeVersion;
|
||||
auto prevVersion = (--writeVersionMap.upper_bound(end))->second;
|
||||
for (auto iter = writeVersionMap.lower_bound(begin),
|
||||
endIter = writeVersionMap.lower_bound(end);
|
||||
@@ -470,6 +506,18 @@ inline std::string printable(std::span<const uint8_t> key) {
|
||||
return printable(std::string_view((const char *)key.data(), key.size()));
|
||||
}
|
||||
|
||||
inline const char *resultToStr(ConflictSet::Result r) {
|
||||
switch (r) {
|
||||
case ConflictSet::Commit:
|
||||
return "commit";
|
||||
case ConflictSet::Conflict:
|
||||
return "conflict";
|
||||
case ConflictSet::TooOld:
|
||||
return "too old";
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <class ConflictSetImpl> struct TestDriver {
|
||||
@@ -482,22 +530,10 @@ template <class ConflictSetImpl> struct TestDriver {
|
||||
ConflictSetImpl cs{oldestVersion};
|
||||
ReferenceImpl refImpl{oldestVersion};
|
||||
|
||||
constexpr static auto kMaxKeyLen = 32;
|
||||
constexpr static auto kMaxKeyLen = 8;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
static const char *resultToStr(ConflictSet::Result r) {
|
||||
switch (r) {
|
||||
case ConflictSet::Commit:
|
||||
return "commit";
|
||||
case ConflictSet::Conflict:
|
||||
return "conflict";
|
||||
case ConflictSet::TooOld:
|
||||
return "too old";
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
// Call until it returns true, for "done". Check internal invariants etc
|
||||
// between calls to next.
|
||||
bool next() {
|
||||
@@ -547,17 +583,14 @@ template <class ConflictSetImpl> struct TestDriver {
|
||||
++iter;
|
||||
--rangesRemaining;
|
||||
}
|
||||
writes[i].writeVersion = v;
|
||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||
if (writes[i].end.len == 0) {
|
||||
fprintf(stderr, "Write: {%s} -> %d\n",
|
||||
printable(writes[i].begin).c_str(),
|
||||
int(writes[i].writeVersion));
|
||||
fprintf(stderr, "Write: {%s} -> %" PRId64 "\n",
|
||||
printable(writes[i].begin).c_str(), writeVersion);
|
||||
} else {
|
||||
fprintf(stderr, "Write: [%s, %s) -> %d\n",
|
||||
fprintf(stderr, "Write: [%s, %s) -> %" PRId64 "\n",
|
||||
printable(writes[i].begin).c_str(),
|
||||
printable(writes[i].end).c_str(),
|
||||
int(writes[i].writeVersion));
|
||||
printable(writes[i].end).c_str(), writeVersion);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -565,10 +598,10 @@ template <class ConflictSetImpl> struct TestDriver {
|
||||
assert(i == numPointWrites + numRangeWrites);
|
||||
|
||||
CALLGRIND_START_INSTRUMENTATION;
|
||||
cs.addWrites(writes, numPointWrites + numRangeWrites);
|
||||
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||
CALLGRIND_STOP_INSTRUMENTATION;
|
||||
|
||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites);
|
||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||
|
||||
oldestVersion = std::max<int64_t>(writeVersion - arbitrary.bounded(10),
|
||||
oldestVersion);
|
||||
@@ -635,12 +668,26 @@ template <class ConflictSetImpl> struct TestDriver {
|
||||
auto *results2 =
|
||||
new (arena) ConflictSet::Result[numPointReads + numRangeReads];
|
||||
|
||||
#ifdef THREAD_TEST
|
||||
auto *results3 =
|
||||
new (arena) ConflictSet::Result[numPointReads + numRangeReads];
|
||||
std::latch ready{1};
|
||||
std::thread thread2{[&]() {
|
||||
ready.count_down();
|
||||
cs.check(reads, results3, numPointReads + numRangeReads);
|
||||
}};
|
||||
ready.wait();
|
||||
#endif
|
||||
|
||||
CALLGRIND_START_INSTRUMENTATION;
|
||||
cs.check(reads, results1, numPointReads + numRangeReads);
|
||||
CALLGRIND_STOP_INSTRUMENTATION;
|
||||
|
||||
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
||||
for (int i = 0; i < numPointReads + numRangeReads; ++i) {
|
||||
|
||||
auto compareResults = [reads](ConflictSet::Result *results1,
|
||||
ConflictSet::Result *results2, int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (results1[i] != results2[i]) {
|
||||
if (reads[i].end.len == 0) {
|
||||
fprintf(stderr,
|
||||
@@ -657,10 +704,24 @@ template <class ConflictSetImpl> struct TestDriver {
|
||||
printable(reads[i].begin).c_str(),
|
||||
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!compareResults(results1, results2, numPointReads + numRangeReads)) {
|
||||
ok = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef THREAD_TEST
|
||||
thread2.join();
|
||||
if (!compareResults(results3, results2, numPointReads + numRangeReads)) {
|
||||
ok = false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
52
Jenkinsfile
vendored
52
Jenkinsfile
vendored
@@ -13,8 +13,6 @@ def CleanBuildAndTest(String cmakeArgs) {
|
||||
cd build
|
||||
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 -T Test -j `nproc` --timeout 90
|
||||
zstd Testing/*/Test.xml
|
||||
LD_LIBRARY_PATH=skip_list ./conflict_set_bench
|
||||
LD_LIBRARY_PATH=radix_tree ./conflict_set_bench
|
||||
'''
|
||||
}
|
||||
xunit tools: [CTest(pattern: 'build/Testing/*/Test.xml')], reduceLog: false, skipPublishingChecks: false
|
||||
@@ -38,6 +36,29 @@ pipeline {
|
||||
sh 'pre-commit run --all-files --show-diff-on-failure'
|
||||
}
|
||||
}
|
||||
stage('Clang') {
|
||||
agent {
|
||||
dockerfile {
|
||||
args '-v /home/jenkins/ccache:/ccache'
|
||||
reuseNode true
|
||||
}
|
||||
}
|
||||
steps {
|
||||
CleanBuildAndTest("")
|
||||
recordIssues(tools: [clang()])
|
||||
}
|
||||
}
|
||||
stage('SIMD fallback') {
|
||||
agent {
|
||||
dockerfile {
|
||||
args '-v /home/jenkins/ccache:/ccache'
|
||||
reuseNode true
|
||||
}
|
||||
}
|
||||
steps {
|
||||
CleanBuildAndTest("-DUSE_SIMD_FALLBACK=ON")
|
||||
}
|
||||
}
|
||||
stage('Release [gcc]') {
|
||||
agent {
|
||||
dockerfile {
|
||||
@@ -46,18 +67,35 @@ pipeline {
|
||||
}
|
||||
}
|
||||
steps {
|
||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release")
|
||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS=-DNVALGRIND")
|
||||
recordIssues(tools: [gcc()])
|
||||
sh '''
|
||||
cd build
|
||||
cpack -G DEB
|
||||
cpack -G RPM
|
||||
'''
|
||||
minio bucket: 'jenkins', credentialsId: 'jenkins-minio', excludes: '', host: 'minio.weaselab.dev', includes: 'build/*.deb', targetFolder: '${JOB_NAME}/${BUILD_NUMBER}/${STAGE_NAME}/'
|
||||
sh '''
|
||||
cd paper
|
||||
make
|
||||
'''
|
||||
minio bucket: 'jenkins', credentialsId: 'jenkins-minio', excludes: '', host: 'minio.weaselab.dev', includes: 'paper/*.pdf', targetFolder: '${JOB_NAME}/${BUILD_NUMBER}/${STAGE_NAME}/'
|
||||
minio bucket: 'jenkins', credentialsId: 'jenkins-minio', excludes: '', host: 'minio.weaselab.dev', includes: 'build/*.deb,build/*.rpm,paper/*.pdf', targetFolder: '${JOB_NAME}/${BUILD_NUMBER}/${STAGE_NAME}/'
|
||||
}
|
||||
}
|
||||
stage('Release [gcc,aarch64]') {
|
||||
agent {
|
||||
dockerfile {
|
||||
args '-v /home/jenkins/ccache:/ccache'
|
||||
reuseNode true
|
||||
}
|
||||
}
|
||||
steps {
|
||||
CleanBuildAndTest("-DCMAKE_TOOLCHAIN_FILE=../aarch64-toolchain.cmake -DCMAKE_CXX_FLAGS=-DNVALGRIND")
|
||||
sh '''
|
||||
cd build
|
||||
cpack -G DEB
|
||||
cpack -G RPM
|
||||
'''
|
||||
minio bucket: 'jenkins', credentialsId: 'jenkins-minio', excludes: '', host: 'minio.weaselab.dev', includes: 'build/*.deb,build/*.rpm', targetFolder: '${JOB_NAME}/${BUILD_NUMBER}/${STAGE_NAME}'
|
||||
}
|
||||
}
|
||||
stage('Coverage') {
|
||||
@@ -68,9 +106,9 @@ pipeline {
|
||||
}
|
||||
}
|
||||
steps {
|
||||
CleanBuildAndTest("-DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DCMAKE_BUILD_TYPE=Debug")
|
||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DCMAKE_BUILD_TYPE=Debug")
|
||||
sh '''
|
||||
gcovr --gcov-executable "llvm-cov-15 gcov" --exclude '.*third_party.*' --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
|
||||
}
|
||||
|
133
README.md
133
README.md
@@ -2,71 +2,104 @@ A data structure for optimistic concurrency control on ranges of bitwise-lexicog
|
||||
|
||||
Intended to replace FoundationDB's skip list.
|
||||
|
||||
Hardware for all benchmarks is a mac m1 2020.
|
||||
|
||||
# FoundationDB's benchmark
|
||||
|
||||
## Skip list
|
||||
|
||||
```
|
||||
New conflict set: 4.189 sec
|
||||
0.298 Mtransactions/sec
|
||||
1.194 Mkeys/sec
|
||||
Detect only: 3.990 sec
|
||||
0.313 Mtransactions/sec
|
||||
1.253 Mkeys/sec
|
||||
Skiplist only: 2.849 sec
|
||||
0.439 Mtransactions/sec
|
||||
1.755 Mkeys/sec
|
||||
New conflict set: 1.957 sec
|
||||
0.639 Mtransactions/sec
|
||||
2.555 Mkeys/sec
|
||||
Detect only: 1.845 sec
|
||||
0.678 Mtransactions/sec
|
||||
2.710 Mkeys/sec
|
||||
Skiplist only: 1.263 sec
|
||||
0.990 Mtransactions/sec
|
||||
3.960 Mkeys/sec
|
||||
Performance counters:
|
||||
Build: 0.0913
|
||||
Add: 0.0998
|
||||
Detect: 3.99
|
||||
D.Sort: 0.808
|
||||
D.Combine: 0.0309
|
||||
D.CheckRead: 1.67
|
||||
D.CheckIntraBatch: 0.0305
|
||||
D.MergeWrite: 1.18
|
||||
D.RemoveBefore: 0.265
|
||||
Build: 0.0546
|
||||
Add: 0.0563
|
||||
Detect: 1.84
|
||||
D.Sort: 0.412
|
||||
D.Combine: 0.0141
|
||||
D.CheckRead: 0.671
|
||||
D.CheckIntraBatch: 0.0068
|
||||
D.MergeWrite: 0.592
|
||||
D.RemoveBefore: 0.146
|
||||
```
|
||||
|
||||
## Radix tree (this implementation)
|
||||
|
||||
```
|
||||
New conflict set: 2.965 sec
|
||||
0.422 Mtransactions/sec
|
||||
1.686 Mkeys/sec
|
||||
Detect only: 2.761 sec
|
||||
0.453 Mtransactions/sec
|
||||
1.811 Mkeys/sec
|
||||
Skiplist only: 1.580 sec
|
||||
0.791 Mtransactions/sec
|
||||
3.165 Mkeys/sec
|
||||
New conflict set: 1.366 sec
|
||||
0.915 Mtransactions/sec
|
||||
3.660 Mkeys/sec
|
||||
Detect only: 1.248 sec
|
||||
1.002 Mtransactions/sec
|
||||
4.007 Mkeys/sec
|
||||
Skiplist only: 0.573 sec
|
||||
2.182 Mtransactions/sec
|
||||
8.730 Mkeys/sec
|
||||
Performance counters:
|
||||
Build: 0.0902
|
||||
Add: 0.107
|
||||
Detect: 2.76
|
||||
D.Sort: 0.809
|
||||
D.Combine: 0.0309
|
||||
D.CheckRead: 0.658
|
||||
D.CheckIntraBatch: 0.0294
|
||||
D.MergeWrite: 0.921
|
||||
D.RemoveBefore: 0.305
|
||||
Build: 0.0594
|
||||
Add: 0.0572
|
||||
Detect: 1.25
|
||||
D.Sort: 0.418
|
||||
D.Combine: 0.0149
|
||||
D.CheckRead: 0.232
|
||||
D.CheckIntraBatch: 0.0067
|
||||
D.MergeWrite: 0.341
|
||||
D.RemoveBefore: 0.232
|
||||
```
|
||||
|
||||
# Our benchmark
|
||||
|
||||
## Skip list
|
||||
|
||||
| ns/op | op/s | err% | total | benchmark
|
||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
||||
| 325.60 | 3,071,225.77 | 4.8% | 0.77 | `skip list (point reads)`
|
||||
| 297.15 | 3,365,278.10 | 1.7% | 0.72 | `skip list (prefix reads)`
|
||||
| 408.79 | 2,446,222.23 | 1.0% | 1.03 | `skip list (range reads)`
|
||||
| 261.88 | 3,818,471.08 | 1.3% | 0.73 | `skip list (point writes)`
|
||||
| 253.54 | 3,944,191.08 | 0.1% | 0.61 | `skip list (prefix writes)`
|
||||
| 258.73 | 3,865,078.52 | 0.8% | 0.62 | `skip list (range writes)`
|
||||
| 489.56 | 2,042,648.19 | 1.8% | 0.01 | `skip list (monotonic increasing point writes)`
|
||||
| 14.83 | 67,446,579.75 | 0.1% | 0.04 | `radix tree (point reads)`
|
||||
| 59.68 | 16,756,917.37 | 0.1% | 0.14 | `radix tree (prefix reads)`
|
||||
| 287.32 | 3,480,485.22 | 1.2% | 0.69 | `radix tree (range reads)`
|
||||
| 46.59 | 21,461,855.59 | 0.2% | 0.12 | `radix tree (point writes)`
|
||||
| 83.70 | 11,946,755.99 | 0.1% | 0.20 | `radix tree (prefix writes)`
|
||||
| 100.75 | 9,925,723.26 | 0.6% | 0.25 | `radix tree (range writes)`
|
||||
| 118.37 | 8,448,345.29 | 0.6% | 0.01 | `radix tree (monotonic increasing point writes)`
|
||||
| 246.99 | 4,048,700.59 | 0.2% | 0.01 | `point 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`
|
||||
| 462.05 | 2,164,289.23 | 0.6% | 0.01 | `point 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`
|
||||
| 582.63 | 1,716,349.02 | 1.3% | 0.01 | `monotonic increasing point writes`
|
||||
|
||||
## Radix tree (this implementation)
|
||||
|
||||
| ns/op | op/s | err% | total | benchmark
|
||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
||||
| 19.42 | 51,483,206.67 | 0.3% | 0.01 | `point 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`
|
||||
| 28.35 | 35,267,567.72 | 0.2% | 0.01 | `point 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`
|
||||
| 92.38 | 10,824,863.69 | 4.1% | 0.01 | `monotonic increasing point writes`
|
||||
|
||||
# "Real data" test
|
||||
|
||||
Point queries only, best of three runs. Gc ratio is the ratio of time spent doing garbage collection to time spent adding writes or doing garbage collection. Lower is better.
|
||||
|
||||
## skip list
|
||||
|
||||
```
|
||||
Check: 11.3385 seconds, 329.718 MB/s, Add: 5.35612 seconds, 131.072 MB/s, Gc ratio: 45.7173%
|
||||
```
|
||||
|
||||
## radix tree
|
||||
|
||||
```
|
||||
Check: 2.48583 seconds, 1503.93 MB/s, Add: 2.12768 seconds, 329.954 MB/s, Gc ratio: 41.7943%
|
||||
```
|
||||
|
||||
## hash table
|
||||
|
||||
(The hash table implementation doesn't work on range queries, and its purpose is to provide an idea of how fast point queries can be)
|
||||
|
||||
```
|
||||
Check: 1.83386 seconds, 2038.6 MB/s, Add: 0.601411 seconds, 1167.32 MB/s, Gc ratio: 48.9776%
|
||||
```
|
||||
|
@@ -3,12 +3,13 @@
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace weaselab;
|
||||
|
||||
double now() {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
@@ -29,7 +30,7 @@ constexpr inline size_t rightAlign(size_t offset, size_t alignment) {
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
// Use with this dataset https://snap.stanford.edu/data/memetracker9.html
|
||||
// Preprocess the files with `sed -i '' '/^Q/d'`
|
||||
// Preprocess the files with `sed -i'' '/^Q/d'`
|
||||
|
||||
double checkTime = 0;
|
||||
double addTime = 0;
|
||||
@@ -41,6 +42,8 @@ int main(int argc, const char **argv) {
|
||||
int64_t version = 0;
|
||||
double timer = 0;
|
||||
|
||||
int64_t peakMemory = 0;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
int fd = open(argv[i], O_RDONLY);
|
||||
struct stat st;
|
||||
@@ -55,6 +58,8 @@ int main(int argc, const char **argv) {
|
||||
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;
|
||||
|
||||
using StringView = std::basic_string_view<uint8_t>;
|
||||
|
||||
@@ -95,7 +100,6 @@ int main(int argc, const char **argv) {
|
||||
// Add unconditionally so that the load doesn't actually depend on the
|
||||
// conflict rate
|
||||
ConflictSet::WriteRange w;
|
||||
w.writeVersion = ++version;
|
||||
w.begin.p = (const uint8_t *)write.data();
|
||||
w.begin.len = write.size();
|
||||
w.end.len = 0;
|
||||
@@ -103,22 +107,28 @@ int main(int argc, const char **argv) {
|
||||
addBytes += write.size();
|
||||
|
||||
timer = now();
|
||||
cs.addWrites(&w, 1);
|
||||
cs.addWrites(&w, 1, ++version);
|
||||
addTime += now() - timer;
|
||||
|
||||
write = {};
|
||||
reads.clear();
|
||||
|
||||
if (cs.getBytes() > peakMemory) {
|
||||
peakMemory = cs.getBytes();
|
||||
}
|
||||
|
||||
timer = now();
|
||||
cs.setOldestVersion(version - 10000);
|
||||
gcTime += now() - timer;
|
||||
}
|
||||
}
|
||||
munmap((void *)mapOriginal, sizeOriginal);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
printf("Check: %g seconds, %g MB/s, Add: %g seconds, %g MB/s, Gc: %g "
|
||||
"seconds\n",
|
||||
printf("Check: %g seconds, %g MB/s, Add: %g seconds, %g MB/s, Gc ratio: "
|
||||
"%g%%, Peak idle memory: %g\n",
|
||||
checkTime, checkBytes / checkTime * 1e-6, addTime,
|
||||
addBytes / addTime * 1e-6, gcTime);
|
||||
addBytes / addTime * 1e-6, gcTime / (gcTime + addTime) * 1e2,
|
||||
double(peakMemory));
|
||||
}
|
155
ScriptTest.cpp
Normal file
155
ScriptTest.cpp
Normal file
@@ -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);
|
||||
}
|
||||
}
|
74
SkipList.cpp
74
SkipList.cpp
@@ -149,7 +149,7 @@ private:
|
||||
setMaxVersion(level, v);
|
||||
}
|
||||
|
||||
void destroy() { free(this); }
|
||||
void destroy() { safe_free(this, getNodeSize()); }
|
||||
|
||||
private:
|
||||
int getNodeSize() const {
|
||||
@@ -286,7 +286,7 @@ public:
|
||||
void swap(SkipList &other) { std::swap(header, other.header); }
|
||||
|
||||
void addConflictRanges(const Finger *fingers, int rangeCount,
|
||||
Version *version) {
|
||||
Version version) {
|
||||
for (int r = rangeCount - 1; r >= 0; r--) {
|
||||
const Finger &startF = fingers[r * 2];
|
||||
const Finger &endF = fingers[r * 2 + 1];
|
||||
@@ -295,7 +295,7 @@ public:
|
||||
insert(endF, endF.finger[0]->getMaxVersion(0));
|
||||
|
||||
remove(startF, endF);
|
||||
insert(startF, version[r]);
|
||||
insert(startF, version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,7 +588,8 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void addWrites(const ConflictSet::WriteRange *writes, int count) {
|
||||
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
||||
int64_t writeVersion) {
|
||||
Arena arena;
|
||||
const int stringCount = count * 2;
|
||||
|
||||
@@ -606,11 +607,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
values[i * 2 + 1] = w.end.len > 0
|
||||
? StringRef{w.end.p, size_t(w.end.len)}
|
||||
: keyAfter(arena, values[i * 2]);
|
||||
writeVersions[i] = w.writeVersion;
|
||||
keyUpdates += 2;
|
||||
keyUpdates += 3;
|
||||
}
|
||||
skipList.find(values, fingers, temp, ss);
|
||||
skipList.addConflictRanges(fingers, ss / 2, writeVersions);
|
||||
skipList.addConflictRanges(fingers, ss / 2, writeVersion);
|
||||
ss = stripeSize;
|
||||
}
|
||||
}
|
||||
@@ -621,14 +621,16 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
int temp;
|
||||
std::span<const uint8_t> key = removalKey;
|
||||
skipList.find(&key, &finger, &temp, 1);
|
||||
skipList.removeBefore(oldestVersion, finger, std::exchange(keyUpdates, 0));
|
||||
skipList.removeBefore(oldestVersion, finger, std::exchange(keyUpdates, 10));
|
||||
removalArena = Arena();
|
||||
removalKey = copyToArena(
|
||||
removalArena, {finger.getValue().data(), finger.getValue().size()});
|
||||
}
|
||||
|
||||
int64_t totalBytes = 0;
|
||||
|
||||
private:
|
||||
int64_t keyUpdates = 0;
|
||||
int64_t keyUpdates = 10;
|
||||
Arena removalArena;
|
||||
std::span<const uint8_t> removalKey;
|
||||
int64_t oldestVersion;
|
||||
@@ -637,24 +639,44 @@ private:
|
||||
|
||||
void ConflictSet::check(const ReadRange *reads, Result *results,
|
||||
int count) const {
|
||||
return impl->check(reads, results, count);
|
||||
impl->check(reads, results, count);
|
||||
}
|
||||
|
||||
void ConflictSet::addWrites(const WriteRange *writes, int count) {
|
||||
return impl->addWrites(writes, count);
|
||||
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
||||
int64_t writeVersion) {
|
||||
mallocBytesDelta = 0;
|
||||
impl->addWrites(writes, count, writeVersion);
|
||||
impl->totalBytes += mallocBytesDelta;
|
||||
#if SHOW_MEMORY
|
||||
if (impl->totalBytes != mallocBytes) {
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
||||
return impl->setOldestVersion(oldestVersion);
|
||||
mallocBytesDelta = 0;
|
||||
impl->setOldestVersion(oldestVersion);
|
||||
impl->totalBytes += mallocBytesDelta;
|
||||
#if SHOW_MEMORY
|
||||
if (impl->totalBytes != mallocBytes) {
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t ConflictSet::getBytes() const { return impl->totalBytes; }
|
||||
|
||||
ConflictSet::ConflictSet(int64_t oldestVersion)
|
||||
: impl(new (safe_malloc(sizeof(Impl))) Impl{oldestVersion}) {}
|
||||
: impl((mallocBytesDelta = 0,
|
||||
new (safe_malloc(sizeof(Impl))) Impl{oldestVersion})) {
|
||||
impl->totalBytes += mallocBytesDelta;
|
||||
}
|
||||
|
||||
ConflictSet::~ConflictSet() {
|
||||
if (impl) {
|
||||
impl->~Impl();
|
||||
free(impl);
|
||||
safe_free(impl, sizeof(Impl));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,9 +700,9 @@ ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
||||
((ConflictSet::Impl *)cs)->check(reads, results, count);
|
||||
}
|
||||
__attribute__((__visibility__("default"))) void
|
||||
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes,
|
||||
int count) {
|
||||
((ConflictSet::Impl *)cs)->addWrites(writes, count);
|
||||
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
||||
int64_t writeVersion) {
|
||||
((ConflictSet::Impl *)cs)->addWrites(writes, count, writeVersion);
|
||||
}
|
||||
__attribute__((__visibility__("default"))) void
|
||||
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
||||
@@ -694,6 +716,20 @@ ConflictSet_create(int64_t oldestVersion) {
|
||||
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||
using Impl = ConflictSet::Impl;
|
||||
((Impl *)cs)->~Impl();
|
||||
free(cs);
|
||||
safe_free(cs, sizeof(Impl));
|
||||
}
|
||||
__attribute__((__visibility__("default"))) int64_t
|
||||
ConflictSet_getBytes(void *cs) {
|
||||
using Impl = ConflictSet::Impl;
|
||||
return ((Impl *)cs)->totalBytes;
|
||||
}
|
||||
}
|
||||
|
||||
#if SHOW_MEMORY
|
||||
struct __attribute__((visibility("default"))) PeakPrinter {
|
||||
~PeakPrinter() {
|
||||
printf("malloc bytes: %g\n", double(mallocBytes));
|
||||
printf("Peak malloc bytes: %g\n", double(peakMallocBytes));
|
||||
}
|
||||
} peakPrinter;
|
||||
#endif
|
@@ -5,7 +5,6 @@
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
printf("Running: %s\n", argv[i]);
|
||||
std::ifstream t(argv[i], std::ios::binary);
|
||||
std::stringstream buffer;
|
||||
buffer << t.rdbuf();
|
||||
|
7
aarch64-toolchain.cmake
Normal file
7
aarch64-toolchain.cmake
Normal file
@@ -0,0 +1,7 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
set(CMAKE_C_COMPILER "/usr/bin/aarch64-linux-gnu-gcc")
|
||||
set(CMAKE_CXX_COMPILER "/usr/bin/aarch64-linux-gnu-g++")
|
||||
set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)
|
||||
set(CMAKE_CROSSCOMPILING_EMULATOR "qemu-aarch64;-L;/usr/aarch64-linux-gnu/")
|
||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE arm64)
|
@@ -7,17 +7,19 @@ int main(void) {
|
||||
ConflictSet_WriteRange w;
|
||||
ConflictSet_Result result;
|
||||
ConflictSet_ReadRange r;
|
||||
int64_t bytes;
|
||||
w.begin.p = (const uint8_t *)"0000";
|
||||
w.begin.len = 4;
|
||||
w.end.len = 0;
|
||||
w.writeVersion = 1;
|
||||
ConflictSet_addWrites(cs, &w, 1);
|
||||
ConflictSet_addWrites(cs, &w, 1, 1);
|
||||
r.begin.p = (const uint8_t *)"0000";
|
||||
r.begin.len = 4;
|
||||
r.end.len = 0;
|
||||
r.readVersion = 0;
|
||||
ConflictSet_check(cs, &r, &result, 1);
|
||||
assert(result == ConflictSet_Conflict);
|
||||
bytes = ConflictSet_getBytes(cs);
|
||||
assert(bytes > 0);
|
||||
ConflictSet_destroy(cs);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -2,14 +2,15 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace weaselab;
|
||||
|
||||
int main(void) {
|
||||
ConflictSet cs(0);
|
||||
ConflictSet::WriteRange w;
|
||||
w.begin.p = (const uint8_t *)"0000";
|
||||
w.begin.len = 4;
|
||||
w.end.len = 0;
|
||||
w.writeVersion = 1;
|
||||
cs.addWrites(&w, 1);
|
||||
cs.addWrites(&w, 1, 1);
|
||||
ConflictSet::Result result;
|
||||
ConflictSet::ReadRange r;
|
||||
r.begin.p = (const uint8_t *)"0000";
|
||||
@@ -18,4 +19,6 @@ int main(void) {
|
||||
r.readVersion = 0;
|
||||
cs.check(&r, &result, 1);
|
||||
assert(result == ConflictSet::Conflict);
|
||||
int64_t bytes = cs.getBytes();
|
||||
assert(bytes > 0);
|
||||
}
|
||||
|
BIN
corpus/00cc0f31d9e079e5c10967791cbfc214c876da48
Normal file
BIN
corpus/00cc0f31d9e079e5c10967791cbfc214c876da48
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/02057227d6245d09b874d53295f14ddb87ee54ff
Normal file
BIN
corpus/02057227d6245d09b874d53295f14ddb87ee54ff
Normal file
Binary file not shown.
BIN
corpus/0258a68ab599675ffb6639a7e1e3cea2879c79cf
Normal file
BIN
corpus/0258a68ab599675ffb6639a7e1e3cea2879c79cf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0325dc4513367ff3f788d540a3d5037cd8422645
Normal file
BIN
corpus/0325dc4513367ff3f788d540a3d5037cd8422645
Normal file
Binary file not shown.
BIN
corpus/0415011bcf7b79337a7fb013d85f9b840b8d2050
Normal file
BIN
corpus/0415011bcf7b79337a7fb013d85f9b840b8d2050
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0503541cc3e72b44e61518ff3d525f226c64ace7
Normal file
BIN
corpus/0503541cc3e72b44e61518ff3d525f226c64ace7
Normal file
Binary file not shown.
BIN
corpus/05bef24faf30d29da5b7caa52f80302985c00e8c
Normal file
BIN
corpus/05bef24faf30d29da5b7caa52f80302985c00e8c
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/063d680d490143cf5fbcb7414f97affed2f66493
Normal file
BIN
corpus/063d680d490143cf5fbcb7414f97affed2f66493
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/064b20fcb90c53503dbb803dc85ff3c5427fe469
Normal file
BIN
corpus/064b20fcb90c53503dbb803dc85ff3c5427fe469
Normal file
Binary file not shown.
BIN
corpus/072efe2ef3d857c2a7d417e010933d496386ea2b
Normal file
BIN
corpus/072efe2ef3d857c2a7d417e010933d496386ea2b
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/07caf39a29c8f5f703a85dabf1069073f1155f9a
Normal file
BIN
corpus/07caf39a29c8f5f703a85dabf1069073f1155f9a
Normal file
Binary file not shown.
BIN
corpus/080c4a75f5549a222909e354ac0d00c63f11701a
Normal file
BIN
corpus/080c4a75f5549a222909e354ac0d00c63f11701a
Normal file
Binary file not shown.
BIN
corpus/09577892321d14b660c77955dc05ba4cf9bcc05c
Normal file
BIN
corpus/09577892321d14b660c77955dc05ba4cf9bcc05c
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0b2b81913b5ef8e7dc1c0d9486f5dd1df578069c
Normal file
BIN
corpus/0b2b81913b5ef8e7dc1c0d9486f5dd1df578069c
Normal file
Binary file not shown.
BIN
corpus/0cab3671b418e023a9b955a5bcdb339a5d80885d
Normal file
BIN
corpus/0cab3671b418e023a9b955a5bcdb339a5d80885d
Normal file
Binary file not shown.
BIN
corpus/0d668e5a8e3e3fc505d99bb1b0fb2633bc8e616c
Normal file
BIN
corpus/0d668e5a8e3e3fc505d99bb1b0fb2633bc8e616c
Normal file
Binary file not shown.
BIN
corpus/0dd7586cd48024a419c0ab49808b958d6540becc
Normal file
BIN
corpus/0dd7586cd48024a419c0ab49808b958d6540becc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0e8030a76f0434cddf6b81ca053e3cc208c2ea03
Normal file
BIN
corpus/0e8030a76f0434cddf6b81ca053e3cc208c2ea03
Normal file
Binary file not shown.
BIN
corpus/0ebb5811e76d95913a595fdeb53d8d440c84d027
Normal file
BIN
corpus/0ebb5811e76d95913a595fdeb53d8d440c84d027
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0f5349817b85b03ef24015b1cda7aa69c06e53d9
Normal file
BIN
corpus/0f5349817b85b03ef24015b1cda7aa69c06e53d9
Normal file
Binary file not shown.
BIN
corpus/0f8b6a2d4ebe4472b25b81d4e8d35586c75af9f9
Normal file
BIN
corpus/0f8b6a2d4ebe4472b25b81d4e8d35586c75af9f9
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/13f781904a9065579d637731c787998f305cc860
Normal file
BIN
corpus/13f781904a9065579d637731c787998f305cc860
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/15d58ff012753e558bfc8e5ff95e456a24654fe1
Normal file
BIN
corpus/15d58ff012753e558bfc8e5ff95e456a24654fe1
Normal file
Binary file not shown.
BIN
corpus/15ff9acbb757117edbc8fcafa13a902f8dfad08d
Normal file
BIN
corpus/15ff9acbb757117edbc8fcafa13a902f8dfad08d
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/18a37da332a8fc0b894fb32da8aa17e981a20e16
Normal file
BIN
corpus/18a37da332a8fc0b894fb32da8aa17e981a20e16
Normal file
Binary file not shown.
BIN
corpus/18e0a1a32d1dfac596258d76367fe3d072863cab
Normal file
BIN
corpus/18e0a1a32d1dfac596258d76367fe3d072863cab
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/199a623aac6456b63c51775bfc307f92af877ee9
Normal file
BIN
corpus/199a623aac6456b63c51775bfc307f92af877ee9
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1b1ee095f24377da663057a4c055795fac40bfda
Normal file
BIN
corpus/1b1ee095f24377da663057a4c055795fac40bfda
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1ca245aada69d5d286c8e8782f03b2b5503a26f5
Normal file
BIN
corpus/1ca245aada69d5d286c8e8782f03b2b5503a26f5
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1e24a4e9325031dadafecedb99f15b87e97a1812
Normal file
BIN
corpus/1e24a4e9325031dadafecedb99f15b87e97a1812
Normal file
Binary file not shown.
BIN
corpus/1e33bf9cb1d0e84d9c9780608b43444495c4d8bf
Normal file
BIN
corpus/1e33bf9cb1d0e84d9c9780608b43444495c4d8bf
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1fcf4bf1173d1054a5e85375ff7667f392508faa
Normal file
BIN
corpus/1fcf4bf1173d1054a5e85375ff7667f392508faa
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/20a353ee3251c2f27b27794c4118cebb24d16b59
Normal file
BIN
corpus/20a353ee3251c2f27b27794c4118cebb24d16b59
Normal file
Binary file not shown.
BIN
corpus/20df7c1e5e3acf2e9d7b050a7dd591b4ace6819e
Normal file
BIN
corpus/20df7c1e5e3acf2e9d7b050a7dd591b4ace6819e
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/218f8656038aeb0bfd406d7abf7908501243a181
Normal file
BIN
corpus/218f8656038aeb0bfd406d7abf7908501243a181
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/234e5cd85e9ffa7fa5a4ae6491025898ac412ac5
Normal file
BIN
corpus/234e5cd85e9ffa7fa5a4ae6491025898ac412ac5
Normal file
Binary file not shown.
BIN
corpus/23f00c1ae1c35400efbbeb2156f6e9d45f6bfcce
Normal file
BIN
corpus/23f00c1ae1c35400efbbeb2156f6e9d45f6bfcce
Normal file
Binary file not shown.
BIN
corpus/26915f2b14f80fb44d34ef855d6ffeff2926138a
Normal file
BIN
corpus/26915f2b14f80fb44d34ef855d6ffeff2926138a
Normal file
Binary file not shown.
BIN
corpus/26eac0247cc3a68355f59134db85969b76d4e701
Normal file
BIN
corpus/26eac0247cc3a68355f59134db85969b76d4e701
Normal file
Binary file not shown.
BIN
corpus/28f5bafcb2c22604c19e64ecf79ff926dcfb942a
Normal file
BIN
corpus/28f5bafcb2c22604c19e64ecf79ff926dcfb942a
Normal file
Binary file not shown.
BIN
corpus/291bd1320f380ad648f9bd8c2f3c80de3bd2d86d
Normal file
BIN
corpus/291bd1320f380ad648f9bd8c2f3c80de3bd2d86d
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/2a666bfbb2b22b1202d23af4b9b35e56eea5e951
Normal file
BIN
corpus/2a666bfbb2b22b1202d23af4b9b35e56eea5e951
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/2a87229ba138b7bb2bf3beaf0367ceaca58b5076
Normal file
BIN
corpus/2a87229ba138b7bb2bf3beaf0367ceaca58b5076
Normal file
Binary file not shown.
BIN
corpus/2c2b15e9f8fa607cabb128bec2181d235264b617
Normal file
BIN
corpus/2c2b15e9f8fa607cabb128bec2181d235264b617
Normal file
Binary file not shown.
BIN
corpus/2d97d47f77bf81d8e02b14c54151ab2519b2d849
Normal file
BIN
corpus/2d97d47f77bf81d8e02b14c54151ab2519b2d849
Normal file
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