Compare commits
209 Commits
b97f611a3c
...
v0.0.4
Author | SHA1 | Date | |
---|---|---|---|
d2e1863593 | |||
bf91bca16d | |||
08ed17f47b | |||
76a45f16ad | |||
c15d296432 | |||
64a98c529c | |||
ed1388ed21 | |||
309d315956 | |||
eab2e46a56 | |||
85db1a8786 | |||
717f9d6829 | |||
fd93300ce8 | |||
b7e16b31ff | |||
a324d31518 | |||
fdb05e0e33 | |||
7c27d4a972 | |||
738de01cb4 | |||
325cab6a95 | |||
0b2821941a | |||
a40b5dcd74 | |||
193b1926ff | |||
1c900c5a8c | |||
90fdcdd51a | |||
eb3f6823eb | |||
1534e10b75 | |||
3c100ccee8 | |||
5cf45d1c35 | |||
4f97932893 | |||
24b0f6b7e4 | |||
e77c3fdee6 | |||
383b956bc0 | |||
5fad15305a | |||
ad91fb36a5 | |||
38c1481432 | |||
771ae896e7 | |||
5bf72bda61 | |||
a534f3b758 | |||
ae4fa889c7 | |||
e3d3b0ec0d | |||
dea8d6ae01 | |||
dbc6f2313a | |||
4f51878642 | |||
215865a462 | |||
348ebf016a | |||
377259ffa0 | |||
70220d95e7 | |||
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 |
2
.clangd
2
.clangd
@@ -1,2 +1,2 @@
|
|||||||
CompileFlags:
|
CompileFlags:
|
||||||
Add: [-DENABLE_MAIN, -UNDEBUG, -DENABLE_FUZZ, -fexceptions]
|
Add: [-DENABLE_MAIN, -UNDEBUG, -DENABLE_FUZZ, -DTHREAD_TEST, -fexceptions]
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
.cache
|
.cache
|
||||||
|
__pycache__
|
||||||
build
|
build
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
rev: b111689e7b5cba60be3c62d5db2bd1357f4d36ca
|
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
|
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,6 +21,14 @@ repos:
|
|||||||
- id: debug verbose check
|
- id: debug verbose check
|
||||||
name: disallow checking in SHOW_MEMORY=1
|
name: disallow checking in SHOW_MEMORY=1
|
||||||
description: disallow checking in SHOW_MEMORY=1
|
description: disallow checking in SHOW_MEMORY=1
|
||||||
entry: '^#define SHOW_MEMORY 1$'
|
entry: "^#define SHOW_MEMORY 1$"
|
||||||
language: pygrep
|
language: pygrep
|
||||||
types: [c++]
|
types: [c++]
|
||||||
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||||
|
rev: a23f6b85d0fdd5bb9d564e2579e678033debbdff # frozen: v0.10.0.1
|
||||||
|
hooks:
|
||||||
|
- id: shellcheck
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 552baf822992936134cbd31a38f69c8cfe7c0f05 # frozen: 24.3.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.tikz": "latex"
|
"*.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
|
||||||
|
}
|
||||||
|
40
Bench.cpp
40
Bench.cpp
@@ -2,7 +2,6 @@
|
|||||||
#include "Internal.h"
|
#include "Internal.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#if SHOW_MEMORY
|
#if SHOW_MEMORY
|
||||||
void showMemory(const ConflictSet &cs);
|
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);
|
std::span<uint8_t>(new (arena) uint8_t[key.size() + 1], key.size() + 1);
|
||||||
memcpy(r.data(), key.data(), key.size());
|
memcpy(r.data(), key.data(), key.size());
|
||||||
r[key.size()] = 0;
|
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) {
|
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);
|
auto r = std::span<uint8_t>(new (arena) uint8_t[index + 1], index + 1);
|
||||||
memcpy(r.data(), key.data(), index + 1);
|
memcpy(r.data(), key.data(), index + 1);
|
||||||
r[r.size() - 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() {
|
void benchConflictSet() {
|
||||||
@@ -61,7 +60,6 @@ void benchConflictSet() {
|
|||||||
ConflictSet cs{0};
|
ConflictSet cs{0};
|
||||||
|
|
||||||
bench.batch(kOpsPerTx);
|
bench.batch(kOpsPerTx);
|
||||||
bench.minEpochIterations(10000);
|
|
||||||
|
|
||||||
int64_t version = 0;
|
int64_t version = 0;
|
||||||
|
|
||||||
@@ -78,10 +76,9 @@ void benchConflictSet() {
|
|||||||
w.begin.len = r.begin.len;
|
w.begin.len = r.begin.len;
|
||||||
w.end.p = r.end.p;
|
w.end.p = r.end.p;
|
||||||
w.end.len = 0;
|
w.end.len = 0;
|
||||||
w.writeVersion = version + 1;
|
|
||||||
writes.push_back(w);
|
writes.push_back(w);
|
||||||
}
|
}
|
||||||
cs.addWrites(writes.data(), writes.size());
|
cs.addWrites(writes.data(), writes.size(), version + 1);
|
||||||
++version;
|
++version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,11 +109,10 @@ void benchConflictSet() {
|
|||||||
w.begin.len = begin.size();
|
w.begin.len = begin.size();
|
||||||
w.end.p = end.data();
|
w.end.p = end.data();
|
||||||
w.end.len = end.size();
|
w.end.len = end.size();
|
||||||
w.writeVersion = version + 1;
|
|
||||||
writes.push_back(w);
|
writes.push_back(w);
|
||||||
}
|
}
|
||||||
|
cs.addWrites(writes.data(), kOpsPerTx, version + 1);
|
||||||
++version;
|
++version;
|
||||||
cs.addWrites(writes.data(), kOpsPerTx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -189,17 +185,12 @@ void benchConflictSet() {
|
|||||||
|
|
||||||
while (version < kMvccWindow) {
|
while (version < kMvccWindow) {
|
||||||
auto v = ++version;
|
auto v = ++version;
|
||||||
writes[0].writeVersion = v;
|
cs.addWrites(writes.data(), 1, v);
|
||||||
cs.addWrites(writes.data(), 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bench.run("point writes", [&]() {
|
bench.run("point writes", [&]() {
|
||||||
auto v = ++version;
|
auto v = ++version;
|
||||||
for (auto &w : writes) {
|
cs.addWrites(writes.data(), writes.size(), v);
|
||||||
w.writeVersion = v;
|
|
||||||
}
|
|
||||||
cs.addWrites(writes.data(), writes.size());
|
|
||||||
cs.setOldestVersion(version - kMvccWindow);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,11 +210,7 @@ void benchConflictSet() {
|
|||||||
|
|
||||||
bench.run("prefix writes", [&]() {
|
bench.run("prefix writes", [&]() {
|
||||||
auto v = ++version;
|
auto v = ++version;
|
||||||
for (auto &w : writes) {
|
cs.addWrites(writes.data(), writes.size(), v);
|
||||||
w.writeVersion = v;
|
|
||||||
}
|
|
||||||
cs.addWrites(writes.data(), writes.size());
|
|
||||||
cs.setOldestVersion(version - kMvccWindow);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,16 +230,14 @@ void benchConflictSet() {
|
|||||||
|
|
||||||
bench.run("range writes", [&]() {
|
bench.run("range writes", [&]() {
|
||||||
auto v = ++version;
|
auto v = ++version;
|
||||||
for (auto &w : writes) {
|
cs.addWrites(writes.data(), writes.size(), v);
|
||||||
w.writeVersion = v;
|
|
||||||
}
|
|
||||||
cs.addWrites(writes.data(), writes.size());
|
|
||||||
cs.setOldestVersion(version - kMvccWindow);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bench.batch(1);
|
bench.batch(1);
|
||||||
|
|
||||||
|
bench.warmup(10000);
|
||||||
|
|
||||||
{
|
{
|
||||||
bench.run("monotonic increasing point writes", [&]() {
|
bench.run("monotonic increasing point writes", [&]() {
|
||||||
auto v = ++version;
|
auto v = ++version;
|
||||||
@@ -263,15 +248,14 @@ void benchConflictSet() {
|
|||||||
auto x = __builtin_bswap64(version);
|
auto x = __builtin_bswap64(version);
|
||||||
memcpy(b, &x, 8);
|
memcpy(b, &x, 8);
|
||||||
|
|
||||||
w.writeVersion = v;
|
|
||||||
w.begin.p = b;
|
w.begin.p = b;
|
||||||
w.begin.len = 8;
|
w.begin.len = 8;
|
||||||
w.end.len = 0;
|
w.end.len = 0;
|
||||||
w.end.p = b;
|
w.end.p = b;
|
||||||
cs.addWrites(&w, 1);
|
cs.addWrites(&w, 1, v);
|
||||||
cs.setOldestVersion(version - kMvccWindow);
|
cs.setOldestVersion(version - kMvccWindow);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) { benchConflictSet(); }
|
int main(void) { benchConflictSet(); }
|
||||||
|
320
CMakeLists.txt
320
CMakeLists.txt
@@ -1,14 +1,28 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
project(
|
project(
|
||||||
conflict_set
|
conflict-set
|
||||||
VERSION 0.0.1
|
VERSION 0.0.4
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
||||||
|
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
||||||
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(CheckCXXCompilerFlag)
|
||||||
|
include(CheckIncludeFileCXX)
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
|
||||||
set(DEFAULT_BUILD_TYPE "Release")
|
set(DEFAULT_BUILD_TYPE "Release")
|
||||||
|
|
||||||
|
if(EMSCRIPTEN OR CMAKE_SYSTEM_NAME STREQUAL WASI)
|
||||||
|
set(WASM ON)
|
||||||
|
else()
|
||||||
|
set(WASM OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
message(
|
message(
|
||||||
STATUS
|
STATUS
|
||||||
@@ -21,16 +35,33 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|||||||
"MinSizeRel" "RelWithDebInfo")
|
"MinSizeRel" "RelWithDebInfo")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_compile_options(-fdata-sections -ffunction-sections)
|
add_compile_options(-fdata-sections -ffunction-sections -Wswitch-enum
|
||||||
|
-Werror=switch-enum -fPIC)
|
||||||
|
|
||||||
|
set(full_relro_flags "-pie;LINKER:-z,relro,-z,now,-z,noexecstack")
|
||||||
|
cmake_push_check_state()
|
||||||
|
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${full_relro_flags})
|
||||||
|
check_cxx_source_compiles("int main(){}" HAS_FULL_RELRO FAIL_REGEX "warning:")
|
||||||
|
if(HAS_FULL_RELRO)
|
||||||
|
add_link_options(${full_relro_flags})
|
||||||
|
endif()
|
||||||
|
cmake_pop_check_state()
|
||||||
|
|
||||||
|
set(version_script_flags LINKER:--version-script=${CMAKE_SOURCE_DIR}/linker.map)
|
||||||
|
cmake_push_check_state()
|
||||||
|
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${version_script_flags})
|
||||||
|
check_cxx_source_compiles("int main(){}" HAS_VERSION_SCRIPT FAIL_REGEX
|
||||||
|
"warning:")
|
||||||
|
cmake_pop_check_state()
|
||||||
|
|
||||||
|
option(USE_SIMD_FALLBACK
|
||||||
|
"Use fallback implementations of functions that use SIMD" OFF)
|
||||||
|
|
||||||
# This is encouraged according to
|
# This is encouraged according to
|
||||||
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
||||||
include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/third_party/valgrind)
|
include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/third_party/valgrind)
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
|
||||||
add_compile_options(-Wno-maybe-uninitialized
|
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_link_options(-Wl,-dead_strip)
|
add_link_options(-Wl,-dead_strip)
|
||||||
@@ -38,32 +69,46 @@ else()
|
|||||||
add_link_options(-Wl,--gc-sections)
|
add_link_options(-Wl,--gc-sections)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(CheckIncludeFileCXX)
|
if(EMSCRIPTEN)
|
||||||
include(CMakePushCheckState)
|
# https://github.com/emscripten-core/emscripten/issues/15377#issuecomment-1285167486
|
||||||
|
add_link_options(-lnodefs.js -lnoderawfs.js)
|
||||||
cmake_push_check_state()
|
add_link_options(-s ALLOW_MEMORY_GROWTH)
|
||||||
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()
|
endif()
|
||||||
cmake_pop_check_state()
|
|
||||||
|
|
||||||
check_include_file_cxx("arm_neon.h" HAS_ARM_NEON)
|
if(NOT USE_SIMD_FALLBACK)
|
||||||
if(HAS_ARM_NEON)
|
cmake_push_check_state()
|
||||||
add_compile_definitions(HAS_ARM_NEON)
|
list(APPEND CMAKE_REQUIRED_FLAGS -msimd128)
|
||||||
|
check_include_file_cxx("wasm_simd128.h" HAS_WASM_SIMD)
|
||||||
|
if(HAS_WASM_SIMD)
|
||||||
|
add_compile_options(-msimd128)
|
||||||
|
add_compile_definitions(HAS_WASM_SIMD)
|
||||||
|
endif()
|
||||||
|
cmake_pop_check_state()
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
check_include_file_cxx("arm_neon.h" HAS_ARM_NEON)
|
||||||
|
if(HAS_ARM_NEON)
|
||||||
|
add_compile_definitions(HAS_ARM_NEON)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
|
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
|
||||||
|
|
||||||
add_library(${PROJECT_NAME}_object OBJECT ConflictSet.cpp)
|
add_library(${PROJECT_NAME}-object OBJECT ConflictSet.cpp)
|
||||||
target_compile_options(${PROJECT_NAME}_object PRIVATE -fPIC -fno-exceptions
|
target_compile_options(${PROJECT_NAME}-object PRIVATE -fno-exceptions
|
||||||
-fvisibility=hidden)
|
-fvisibility=hidden)
|
||||||
target_include_directories(${PROJECT_NAME}_object
|
target_include_directories(${PROJECT_NAME}-object
|
||||||
PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
|
add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||||
"${CMAKE_BINARY_DIR}/radix_tree")
|
"${CMAKE_BINARY_DIR}/radix_tree")
|
||||||
@@ -71,57 +116,85 @@ if(NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
|||||||
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C)
|
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT APPLE)
|
if(HAS_VERSION_SCRIPT)
|
||||||
target_link_options(${PROJECT_NAME} PRIVATE
|
target_link_options(${PROJECT_NAME} PRIVATE
|
||||||
LINKER:--version-script=${CMAKE_SOURCE_DIR}/linker.map)
|
LINKER:--version-script=${CMAKE_SOURCE_DIR}/linker.map)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(${PROJECT_NAME}_static STATIC
|
add_library(${PROJECT_NAME}-static STATIC
|
||||||
$<TARGET_OBJECTS:${PROJECT_NAME}_object>)
|
$<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
||||||
if(NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
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()
|
endif()
|
||||||
|
|
||||||
if(NOT APPLE AND CMAKE_OBJCOPY)
|
if(APPLE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET conflict_set_static
|
TARGET ${PROJECT_NAME}-static
|
||||||
|
PRE_LINK
|
||||||
|
COMMAND ${CMAKE_SOURCE_DIR}/privatize_symbols_macos.sh
|
||||||
|
$<TARGET_OBJECTS:${PROJECT_NAME}-object>)
|
||||||
|
else()
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT_NAME}-static
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_OBJCOPY} --keep-global-symbols=${CMAKE_SOURCE_DIR}/symbols.txt
|
${CMAKE_OBJCOPY}
|
||||||
$<TARGET_FILE:${PROJECT_NAME}_static>)
|
--keep-global-symbols=${CMAKE_SOURCE_DIR}/symbol-exports.txt
|
||||||
|
$<TARGET_FILE:${PROJECT_NAME}-static> || echo
|
||||||
|
"Proceeding with all symbols global in static library")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(TEST_FLAGS -Wall -Wextra -Wpedantic -Wunreachable-code -UNDEBUG)
|
set(TEST_FLAGS -Wall -Wextra -Wunreachable-code -Wpedantic -UNDEBUG)
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
|
|
||||||
# Shared library version of FoundationDB's skip list implementation
|
# corpus tests, which are tests curated by libfuzzer. The goal is to get broad
|
||||||
add_library(skip_list SHARED SkipList.cpp)
|
# coverage with a small number of tests.
|
||||||
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
|
file(GLOB CORPUS_TESTS ${CMAKE_SOURCE_DIR}/corpus/*)
|
||||||
# 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})
|
|
||||||
|
|
||||||
|
# extra testing that relies on shared libraries, which aren't available with
|
||||||
|
# wasm
|
||||||
|
if(NOT WASM)
|
||||||
|
# Shared library version of FoundationDB's skip list implementation
|
||||||
|
add_library(skip_list SHARED SkipList.cpp)
|
||||||
|
target_compile_options(skip_list PRIVATE -fno-exceptions
|
||||||
|
-fvisibility=hidden)
|
||||||
|
target_include_directories(skip_list PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||||
|
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 -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(driver_skip_list TestDriver.cpp)
|
||||||
|
target_compile_options(driver_skip_list PRIVATE ${TEST_FLAGS})
|
||||||
|
target_link_libraries(driver_skip_list PRIVATE skip_list)
|
||||||
|
|
||||||
|
foreach(TEST ${CORPUS_TESTS})
|
||||||
|
get_filename_component(hash ${TEST} NAME)
|
||||||
|
add_test(NAME skip_list_${hash} COMMAND driver_skip_list ${TEST})
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ad hoc testing
|
||||||
add_executable(conflict_set_main ConflictSet.cpp)
|
add_executable(conflict_set_main ConflictSet.cpp)
|
||||||
target_include_directories(conflict_set_main
|
target_include_directories(conflict_set_main
|
||||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
@@ -148,12 +221,13 @@ if(BUILD_TESTING)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# corpus tests
|
# whitebox tests
|
||||||
|
|
||||||
file(GLOB CORPUS_TESTS ${CMAKE_SOURCE_DIR}/corpus/*)
|
|
||||||
|
|
||||||
add_executable(fuzz_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
add_executable(fuzz_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
||||||
target_compile_options(fuzz_driver PRIVATE ${TEST_FLAGS})
|
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_compile_definitions(fuzz_driver PRIVATE ENABLE_FUZZ)
|
||||||
target_include_directories(fuzz_driver
|
target_include_directories(fuzz_driver
|
||||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
@@ -162,27 +236,55 @@ if(BUILD_TESTING)
|
|||||||
add_test(NAME conflict_set_fuzz_${hash} COMMAND fuzz_driver ${TEST})
|
add_test(NAME conflict_set_fuzz_${hash} COMMAND fuzz_driver ${TEST})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
# tsan tests
|
||||||
|
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()
|
||||||
|
|
||||||
|
# blackbox tests
|
||||||
add_executable(driver TestDriver.cpp)
|
add_executable(driver TestDriver.cpp)
|
||||||
target_compile_options(driver PRIVATE ${TEST_FLAGS})
|
target_compile_options(driver PRIVATE ${TEST_FLAGS})
|
||||||
target_link_libraries(driver PRIVATE ${PROJECT_NAME})
|
target_link_libraries(driver PRIVATE ${PROJECT_NAME})
|
||||||
|
foreach(TEST ${CORPUS_TESTS})
|
||||||
|
get_filename_component(hash ${TEST} NAME)
|
||||||
|
add_test(NAME conflict_set_blackbox_${hash} COMMAND driver ${TEST})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
add_executable(driver_skip_list TestDriver.cpp)
|
# scripted tests. Written manually to fill in anything libfuzzer couldn't
|
||||||
target_compile_options(driver_skip_list PRIVATE ${TEST_FLAGS})
|
# find.
|
||||||
target_link_libraries(driver_skip_list PRIVATE skip_list)
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||||
|
set_property(
|
||||||
|
DIRECTORY
|
||||||
|
APPEND
|
||||||
|
PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/test_conflict_set.py)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/test_conflict_set.py
|
||||||
|
list OUTPUT_VARIABLE SCRIPT_TESTS)
|
||||||
|
foreach(TEST ${SCRIPT_TESTS})
|
||||||
|
add_test(
|
||||||
|
NAME script_test_${TEST}
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/test_conflict_set.py
|
||||||
|
test ${TEST} --build-dir ${CMAKE_BINARY_DIR})
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
find_program(VALGRIND_EXE valgrind)
|
find_program(VALGRIND_EXE valgrind)
|
||||||
if(VALGRIND_EXE)
|
if(VALGRIND_EXE AND NOT CMAKE_CROSSCOMPILING)
|
||||||
add_test(NAME conflict_set_blackbox_valgrind
|
add_test(NAME conflict_set_blackbox_valgrind
|
||||||
COMMAND ${VALGRIND_EXE} --error-exitcode=99 --
|
COMMAND ${VALGRIND_EXE} --error-exitcode=99 --
|
||||||
$<TARGET_FILE:driver> ${CORPUS_TESTS})
|
$<TARGET_FILE:driver> ${CORPUS_TESTS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
foreach(TEST ${CORPUS_TESTS})
|
|
||||||
get_filename_component(hash ${TEST} NAME)
|
|
||||||
add_test(NAME conflict_set_blackbox_${hash} COMMAND driver ${TEST})
|
|
||||||
add_test(NAME skip_list_${hash} COMMAND driver_skip_list ${TEST})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# api smoke tests
|
# api smoke tests
|
||||||
|
|
||||||
# c90
|
# c90
|
||||||
@@ -203,36 +305,79 @@ if(BUILD_TESTING)
|
|||||||
PROPERTIES CXX_STANDARD_REQUIRED ON)
|
PROPERTIES CXX_STANDARD_REQUIRED ON)
|
||||||
add_test(NAME conflict_set_cxx_api_test COMMAND conflict_set_cxx_api_test)
|
add_test(NAME conflict_set_cxx_api_test COMMAND conflict_set_cxx_api_test)
|
||||||
|
|
||||||
if(NOT APPLE AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
# symbol visibility tests
|
||||||
|
if(NOT WASM AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
|
if(APPLE)
|
||||||
|
set(symbol_exports ${CMAKE_SOURCE_DIR}/apple-symbol-exports.txt)
|
||||||
|
set(symbol_imports ${CMAKE_SOURCE_DIR}/apple-symbol-imports.txt)
|
||||||
|
else()
|
||||||
|
set(symbol_exports ${CMAKE_SOURCE_DIR}/symbol-exports.txt)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
||||||
|
set(symbol_imports ${CMAKE_SOURCE_DIR}/aarch64-symbol-imports.txt)
|
||||||
|
else()
|
||||||
|
set(symbol_imports ${CMAKE_SOURCE_DIR}/symbol-imports.txt)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
add_test(
|
add_test(
|
||||||
NAME conflict_set_shared_symbols
|
NAME conflict_set_shared_symbols
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/test_symbols.sh
|
COMMAND
|
||||||
$<TARGET_FILE:${PROJECT_NAME}> ${CMAKE_SOURCE_DIR}/symbols.txt)
|
${CMAKE_SOURCE_DIR}/test_symbols.sh $<TARGET_FILE:${PROJECT_NAME}>
|
||||||
|
${symbol_exports} ${symbol_imports})
|
||||||
add_test(
|
add_test(
|
||||||
NAME conflict_set_static_symbols
|
NAME conflict_set_static_symbols
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_SOURCE_DIR}/test_symbols.sh
|
${CMAKE_SOURCE_DIR}/test_symbols.sh
|
||||||
$<TARGET_FILE:${PROJECT_NAME}_static> ${CMAKE_SOURCE_DIR}/symbols.txt)
|
$<TARGET_FILE:${PROJECT_NAME}-static> ${symbol_exports}
|
||||||
|
${symbol_imports})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# bench
|
# bench
|
||||||
|
|
||||||
add_executable(conflict_set_bench Bench.cpp)
|
add_executable(conflict_set_bench Bench.cpp)
|
||||||
target_link_libraries(conflict_set_bench PRIVATE ${PROJECT_NAME})
|
target_link_libraries(conflict_set_bench PRIVATE ${PROJECT_NAME})
|
||||||
set_target_properties(conflict_set_bench PROPERTIES SKIP_BUILD_RPATH ON)
|
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)
|
add_executable(real_data_bench RealDataBench.cpp)
|
||||||
target_link_libraries(real_data_bench PRIVATE ${PROJECT_NAME})
|
target_link_libraries(real_data_bench PRIVATE ${PROJECT_NAME})
|
||||||
set_target_properties(real_data_bench PROPERTIES SKIP_BUILD_RPATH ON)
|
set_target_properties(real_data_bench PROPERTIES SKIP_BUILD_RPATH ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# packaging
|
# packaging
|
||||||
|
|
||||||
set(CPACK_PACKAGE_CONTACT andrew@weaselab.dev)
|
set(CPACK_PACKAGE_CONTACT andrew@weaselab.dev)
|
||||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME all)
|
||||||
|
|
||||||
|
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)
|
||||||
|
# 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)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
@@ -243,7 +388,7 @@ target_include_directories(
|
|||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>)
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>)
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
${PROJECT_NAME}_static
|
${PROJECT_NAME}-static
|
||||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>)
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>)
|
||||||
|
|
||||||
@@ -252,13 +397,16 @@ set_target_properties(
|
|||||||
SOVERSION ${PROJECT_VERSION_MAJOR})
|
SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||||
|
|
||||||
install(
|
install(
|
||||||
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_static
|
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-static
|
||||||
EXPORT ConflictSetConfig
|
EXPORT ${PROJECT_NAME}Config
|
||||||
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 ConflictSetConfig
|
install(EXPORT ${PROJECT_NAME}Config
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/ConflictSet/cmake)
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
|
||||||
|
|
||||||
|
cpack_add_component(all)
|
||||||
|
2219
ConflictSet.cpp
2219
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 \
|
curl \
|
||||||
doxygen \
|
doxygen \
|
||||||
file \
|
file \
|
||||||
|
g++-aarch64-linux-gnu \
|
||||||
gcovr \
|
gcovr \
|
||||||
git \
|
git \
|
||||||
gperf \
|
gperf \
|
||||||
@@ -23,17 +24,20 @@ RUN TZ=America/Los_Angeles DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|||||||
ninja-build \
|
ninja-build \
|
||||||
pre-commit \
|
pre-commit \
|
||||||
python3-requests \
|
python3-requests \
|
||||||
|
qemu-user \
|
||||||
|
rpm \
|
||||||
|
texlive-full \
|
||||||
zstd
|
zstd
|
||||||
|
|
||||||
# Install recent valgrind from source
|
# Install recent valgrind from source
|
||||||
RUN curl -Ls https://sourceware.org/pub/valgrind/valgrind-3.20.0.tar.bz2 -o valgrind.tar.bz2 && \
|
RUN curl -Ls https://sourceware.org/pub/valgrind/valgrind-3.22.0.tar.bz2 -o valgrind.tar.bz2 && \
|
||||||
echo "8536c031dbe078d342f121fa881a9ecd205cb5a78e639005ad570011bdb9f3c6 valgrind.tar.bz2" > valgrind-sha.txt && \
|
echo "c811db5add2c5f729944caf47c4e7a65dcaabb9461e472b578765dd7bf6d2d4c valgrind.tar.bz2" > valgrind-sha.txt && \
|
||||||
sha256sum --quiet -c valgrind-sha.txt && \
|
sha256sum --quiet -c valgrind-sha.txt && \
|
||||||
mkdir valgrind && \
|
mkdir valgrind && \
|
||||||
tar --strip-components 1 --no-same-owner --no-same-permissions --directory valgrind -xjf valgrind.tar.bz2 && \
|
tar --strip-components 1 --no-same-owner --no-same-permissions --directory valgrind -xjf valgrind.tar.bz2 && \
|
||||||
cd valgrind && \
|
cd valgrind && \
|
||||||
./configure --enable-only64bit --enable-lto && \
|
./configure --enable-only64bit --enable-lto && \
|
||||||
make && \
|
make -j`nproc` && \
|
||||||
make install && \
|
make install && \
|
||||||
cd .. && \
|
cd .. && \
|
||||||
rm -rf /tmp/*
|
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 CC=clang
|
||||||
ENV CXX=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
|
# Try to have all the pre-commit hooks we'll need already initialized
|
||||||
COPY .pre-commit-config.yaml /tmp/
|
COPY .pre-commit-config.yaml /tmp/
|
||||||
RUN git init && pre-commit install-hooks
|
RUN git init && pre-commit install-hooks
|
||||||
|
@@ -2,15 +2,25 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
for (int i = 1; i < argc; ++i) {
|
auto doTest = [&]() {
|
||||||
std::ifstream t(argv[i], std::ios::binary);
|
for (int i = 1; i < argc; ++i) {
|
||||||
std::stringstream buffer;
|
std::ifstream t(argv[i], std::ios::binary);
|
||||||
buffer << t.rdbuf();
|
std::stringstream buffer;
|
||||||
auto str = buffer.str();
|
buffer << t.rdbuf();
|
||||||
LLVMFuzzerTestOneInput((const uint8_t *)str.data(), str.size());
|
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
|
||||||
}
|
}
|
||||||
|
@@ -36,11 +36,13 @@ 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) {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
auto &max = map[std::string((const char *)writes[i].begin.p,
|
auto &max = map[std::string((const char *)writes[i].begin.p,
|
||||||
writes[i].begin.len)];
|
writes[i].begin.len)];
|
||||||
max = std::max(max, writes[i].writeVersion);
|
assert(writeVersion >= max);
|
||||||
|
max = writeVersion;
|
||||||
keyUpdates += 2;
|
keyUpdates += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,14 +56,16 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto iter = map.find(removalKey);
|
auto iter = map.find(removalKey);
|
||||||
if (iter == map.end()) {
|
while (keyUpdates > 0) {
|
||||||
iter = map.begin();
|
if (iter == map.end()) {
|
||||||
}
|
iter = map.begin();
|
||||||
for (; keyUpdates > 0 && iter != map.end(); --keyUpdates) {
|
}
|
||||||
if (iter->second <= oldestVersion) {
|
for (; iter != map.end(); --keyUpdates) {
|
||||||
iter = map.erase(iter);
|
if (iter->second <= oldestVersion) {
|
||||||
} else {
|
iter = map.erase(iter);
|
||||||
++iter;
|
} else {
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (iter == map.end()) {
|
if (iter == map.end()) {
|
||||||
@@ -83,21 +87,24 @@ void ConflictSet::check(const ReadRange *reads, Result *results,
|
|||||||
return impl->check(reads, results, count);
|
return impl->check(reads, results, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConflictSet::addWrites(const WriteRange *writes, int count) {
|
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
||||||
return impl->addWrites(writes, count);
|
int64_t writeVersion) {
|
||||||
|
return impl->addWrites(writes, count, writeVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
||||||
return impl->setOldestVersion(oldestVersion);
|
return impl->setOldestVersion(oldestVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t ConflictSet::getBytes() const { return -1; }
|
||||||
|
|
||||||
ConflictSet::ConflictSet(int64_t oldestVersion)
|
ConflictSet::ConflictSet(int64_t oldestVersion)
|
||||||
: impl(new (safe_malloc(sizeof(Impl))) Impl{oldestVersion}) {}
|
: impl(new(safe_malloc(sizeof(Impl))) Impl{oldestVersion}) {}
|
||||||
|
|
||||||
ConflictSet::~ConflictSet() {
|
ConflictSet::~ConflictSet() {
|
||||||
if (impl) {
|
if (impl) {
|
||||||
impl->~Impl();
|
impl->~Impl();
|
||||||
free(impl);
|
safe_free(impl, sizeof(Impl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,9 +128,9 @@ ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
|||||||
((ConflictSet::Impl *)cs)->check(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,
|
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
||||||
int count) {
|
int64_t writeVersion) {
|
||||||
((ConflictSet::Impl *)cs)->addWrites(writes, count);
|
((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) {
|
||||||
@@ -137,6 +144,11 @@ ConflictSet_create(int64_t oldestVersion) {
|
|||||||
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||||
using Impl = ConflictSet::Impl;
|
using Impl = ConflictSet::Impl;
|
||||||
((Impl *)cs)->~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 -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
230
Internal.h
230
Internal.h
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "ConflictSet.h"
|
#include "ConflictSet.h"
|
||||||
|
|
||||||
|
using namespace weaselab;
|
||||||
|
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <compare>
|
#include <compare>
|
||||||
@@ -10,10 +12,12 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <latch>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -35,16 +39,76 @@ operator<=>(const std::span<const uint8_t> &lhs,
|
|||||||
return lhs.size() <=> rhs.size();
|
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
|
// 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.
|
// want to exclude from coverage since it's only testing related.
|
||||||
|
|
||||||
// GCOVR_EXCL_START
|
// 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) {
|
__attribute__((always_inline)) inline void *safe_malloc(size_t s) {
|
||||||
if (void *p = malloc(s)) {
|
mallocBytesDelta += s;
|
||||||
return p;
|
#if SHOW_MEMORY
|
||||||
|
mallocBytes += s;
|
||||||
|
if (mallocBytes > peakMallocBytes) {
|
||||||
|
peakMallocBytes = mallocBytes;
|
||||||
}
|
}
|
||||||
abort();
|
#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 ====================
|
// ==================== BEGIN ARENA IMPL ====================
|
||||||
@@ -90,6 +154,7 @@ inline void *operator new[](size_t size, std::align_val_t align,
|
|||||||
|
|
||||||
/// align must be a power of two
|
/// align must be a power of two
|
||||||
template <class T> T *align_up(T *t, size_t align) {
|
template <class T> T *align_up(T *t, size_t align) {
|
||||||
|
assert(std::popcount(align) == 1);
|
||||||
auto unaligned = uintptr_t(t);
|
auto unaligned = uintptr_t(t);
|
||||||
auto aligned = (unaligned + align - 1) & ~(align - 1);
|
auto aligned = (unaligned + align - 1) & ~(align - 1);
|
||||||
return reinterpret_cast<T *>(reinterpret_cast<char *>(t) + aligned -
|
return reinterpret_cast<T *>(reinterpret_cast<char *>(t) + aligned -
|
||||||
@@ -98,6 +163,7 @@ template <class T> T *align_up(T *t, size_t align) {
|
|||||||
|
|
||||||
/// align must be a power of two
|
/// align must be a power of two
|
||||||
constexpr inline int align_up(uint32_t unaligned, uint32_t align) {
|
constexpr inline int align_up(uint32_t unaligned, uint32_t align) {
|
||||||
|
assert(std::popcount(align) == 1);
|
||||||
return (unaligned + align - 1) & ~(align - 1);
|
return (unaligned + align - 1) & ~(align - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,12 +179,10 @@ struct Arena::ArenaImpl {
|
|||||||
uint8_t *begin() { return reinterpret_cast<uint8_t *>(this + 1); }
|
uint8_t *begin() { return reinterpret_cast<uint8_t *>(this + 1); }
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(Arena::ArenaImpl) == 16);
|
|
||||||
static_assert(alignof(Arena::ArenaImpl) == 8);
|
|
||||||
|
|
||||||
inline Arena::Arena(int initialSize) : impl(nullptr) {
|
inline Arena::Arena(int initialSize) : impl(nullptr) {
|
||||||
if (initialSize > 0) {
|
if (initialSize > 0) {
|
||||||
auto allocationSize = align_up(initialSize + sizeof(ArenaImpl), 16);
|
auto allocationSize =
|
||||||
|
align_up(initialSize + sizeof(ArenaImpl), alignof(ArenaImpl));
|
||||||
impl = (Arena::ArenaImpl *)safe_malloc(allocationSize);
|
impl = (Arena::ArenaImpl *)safe_malloc(allocationSize);
|
||||||
impl->prev = nullptr;
|
impl->prev = nullptr;
|
||||||
impl->capacity = allocationSize - sizeof(ArenaImpl);
|
impl->capacity = allocationSize - sizeof(ArenaImpl);
|
||||||
@@ -129,7 +193,7 @@ inline Arena::Arena(int initialSize) : impl(nullptr) {
|
|||||||
inline void onDestroy(Arena::ArenaImpl *impl) {
|
inline void onDestroy(Arena::ArenaImpl *impl) {
|
||||||
while (impl) {
|
while (impl) {
|
||||||
auto *prev = impl->prev;
|
auto *prev = impl->prev;
|
||||||
free(impl);
|
safe_free(impl, sizeof(Arena::ArenaImpl) + impl->capacity);
|
||||||
impl = prev;
|
impl = prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +218,7 @@ inline void *operator new(size_t size, std::align_val_t align, Arena &arena) {
|
|||||||
(arena.impl ? std::max<int>(sizeof(Arena::ArenaImpl),
|
(arena.impl ? std::max<int>(sizeof(Arena::ArenaImpl),
|
||||||
arena.impl->capacity * 2)
|
arena.impl->capacity * 2)
|
||||||
: 0)),
|
: 0)),
|
||||||
16);
|
alignof(Arena::ArenaImpl));
|
||||||
auto *impl = (Arena::ArenaImpl *)safe_malloc(allocationSize);
|
auto *impl = (Arena::ArenaImpl *)safe_malloc(allocationSize);
|
||||||
impl->prev = arena.impl;
|
impl->prev = arena.impl;
|
||||||
impl->capacity = allocationSize - sizeof(Arena::ArenaImpl);
|
impl->capacity = allocationSize - sizeof(Arena::ArenaImpl);
|
||||||
@@ -349,34 +413,6 @@ inline uint32_t Arbitrary::bounded(uint32_t s) {
|
|||||||
|
|
||||||
// ==================== END ARBITRARY IMPL ====================
|
// ==================== 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 {
|
struct ReferenceImpl {
|
||||||
explicit ReferenceImpl(int64_t oldestVersion) : oldestVersion(oldestVersion) {
|
explicit ReferenceImpl(int64_t oldestVersion) : oldestVersion(oldestVersion) {
|
||||||
writeVersionMap[""] = oldestVersion;
|
writeVersionMap[""] = oldestVersion;
|
||||||
@@ -404,7 +440,8 @@ struct ReferenceImpl {
|
|||||||
: ConflictSet::Commit;
|
: 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) {
|
for (int i = 0; i < count; ++i) {
|
||||||
auto begin =
|
auto begin =
|
||||||
std::string((const char *)writes[i].begin.p, writes[i].begin.len);
|
std::string((const char *)writes[i].begin.p, writes[i].begin.len);
|
||||||
@@ -412,7 +449,6 @@ struct ReferenceImpl {
|
|||||||
writes[i].end.len == 0
|
writes[i].end.len == 0
|
||||||
? begin + std::string("\x00", 1)
|
? begin + std::string("\x00", 1)
|
||||||
: std::string((const char *)writes[i].end.p, writes[i].end.len);
|
: std::string((const char *)writes[i].end.p, writes[i].end.len);
|
||||||
auto writeVersion = writes[i].writeVersion;
|
|
||||||
auto prevVersion = (--writeVersionMap.upper_bound(end))->second;
|
auto prevVersion = (--writeVersionMap.upper_bound(end))->second;
|
||||||
for (auto iter = writeVersionMap.lower_bound(begin),
|
for (auto iter = writeVersionMap.lower_bound(begin),
|
||||||
endIter = writeVersionMap.lower_bound(end);
|
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()));
|
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 {
|
namespace {
|
||||||
|
|
||||||
template <class ConflictSetImpl> struct TestDriver {
|
template <class ConflictSetImpl> struct TestDriver {
|
||||||
@@ -482,21 +530,12 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
ConflictSetImpl cs{oldestVersion};
|
ConflictSetImpl cs{oldestVersion};
|
||||||
ReferenceImpl refImpl{oldestVersion};
|
ReferenceImpl refImpl{oldestVersion};
|
||||||
|
|
||||||
constexpr static auto kMaxKeyLen = 32;
|
constexpr static auto kMaxKeySuffixLen = 8;
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
||||||
static const char *resultToStr(ConflictSet::Result r) {
|
const int prefixLen = arbitrary.bounded(512);
|
||||||
switch (r) {
|
const int prefixByte = arbitrary.randT<uint8_t>();
|
||||||
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
|
// Call until it returns true, for "done". Check internal invariants etc
|
||||||
// between calls to next.
|
// between calls to next.
|
||||||
@@ -516,9 +555,10 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary.hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = arbitrary.bounded(kMaxKeyLen);
|
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
arbitrary.randomBytes(begin, keyLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
|
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
keys.insert(std::string_view((const char *)begin, keyLen));
|
keys.insert(std::string_view((const char *)begin, keyLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,17 +587,14 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
++iter;
|
++iter;
|
||||||
--rangesRemaining;
|
--rangesRemaining;
|
||||||
}
|
}
|
||||||
writes[i].writeVersion = v;
|
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
if (writes[i].end.len == 0) {
|
if (writes[i].end.len == 0) {
|
||||||
fprintf(stderr, "Write: {%s} -> %d\n",
|
fprintf(stderr, "Write: {%s} -> %" PRId64 "\n",
|
||||||
printable(writes[i].begin).c_str(),
|
printable(writes[i].begin).c_str(), writeVersion);
|
||||||
int(writes[i].writeVersion));
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Write: [%s, %s) -> %d\n",
|
fprintf(stderr, "Write: [%s, %s) -> %" PRId64 "\n",
|
||||||
printable(writes[i].begin).c_str(),
|
printable(writes[i].begin).c_str(),
|
||||||
printable(writes[i].end).c_str(),
|
printable(writes[i].end).c_str(), writeVersion);
|
||||||
int(writes[i].writeVersion));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -565,10 +602,10 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
assert(i == numPointWrites + numRangeWrites);
|
assert(i == numPointWrites + numRangeWrites);
|
||||||
|
|
||||||
CALLGRIND_START_INSTRUMENTATION;
|
CALLGRIND_START_INSTRUMENTATION;
|
||||||
cs.addWrites(writes, numPointWrites + numRangeWrites);
|
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
|
|
||||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites);
|
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
|
|
||||||
oldestVersion = std::max<int64_t>(writeVersion - arbitrary.bounded(10),
|
oldestVersion = std::max<int64_t>(writeVersion - arbitrary.bounded(10),
|
||||||
oldestVersion);
|
oldestVersion);
|
||||||
@@ -586,9 +623,10 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary.hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = arbitrary.bounded(kMaxKeyLen);
|
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
arbitrary.randomBytes(begin, keyLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
|
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
keys.insert(std::string_view((const char *)begin, keyLen));
|
keys.insert(std::string_view((const char *)begin, keyLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,32 +673,60 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
auto *results2 =
|
auto *results2 =
|
||||||
new (arena) ConflictSet::Result[numPointReads + numRangeReads];
|
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;
|
CALLGRIND_START_INSTRUMENTATION;
|
||||||
cs.check(reads, results1, numPointReads + numRangeReads);
|
cs.check(reads, results1, numPointReads + numRangeReads);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
|
|
||||||
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
||||||
for (int i = 0; i < numPointReads + numRangeReads; ++i) {
|
|
||||||
if (results1[i] != results2[i]) {
|
auto compareResults = [reads](ConflictSet::Result *results1,
|
||||||
if (reads[i].end.len == 0) {
|
ConflictSet::Result *results2, int count) {
|
||||||
fprintf(stderr,
|
for (int i = 0; i < count; ++i) {
|
||||||
"Expected %s, got %s for read of {%s} at version %" PRId64
|
if (results1[i] != results2[i]) {
|
||||||
"\n",
|
if (reads[i].end.len == 0) {
|
||||||
resultToStr(results2[i]), resultToStr(results1[i]),
|
fprintf(stderr,
|
||||||
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
"Expected %s, got %s for read of {%s} at version %" PRId64
|
||||||
} else {
|
"\n",
|
||||||
fprintf(
|
resultToStr(results2[i]), resultToStr(results1[i]),
|
||||||
stderr,
|
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
||||||
"Expected %s, got %s for read of [%s, %s) at version %" PRId64
|
} else {
|
||||||
"\n",
|
fprintf(
|
||||||
resultToStr(results2[i]), resultToStr(results1[i]),
|
stderr,
|
||||||
printable(reads[i].begin).c_str(),
|
"Expected %s, got %s for read of [%s, %s) at version %" PRId64
|
||||||
printable(reads[i].end).c_str(), reads[i].readVersion);
|
"\n",
|
||||||
|
resultToStr(results2[i]), resultToStr(results1[i]),
|
||||||
|
printable(reads[i].begin).c_str(),
|
||||||
|
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
ok = false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
55
Jenkinsfile
vendored
55
Jenkinsfile
vendored
@@ -13,8 +13,6 @@ def CleanBuildAndTest(String cmakeArgs) {
|
|||||||
cd build
|
cd build
|
||||||
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 -T Test -j `nproc` --timeout 90
|
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 -T Test -j `nproc` --timeout 90
|
||||||
zstd Testing/*/Test.xml
|
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
|
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'
|
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]') {
|
stage('Release [gcc]') {
|
||||||
agent {
|
agent {
|
||||||
dockerfile {
|
dockerfile {
|
||||||
@@ -46,18 +67,35 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
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()])
|
recordIssues(tools: [gcc()])
|
||||||
sh '''
|
sh '''
|
||||||
cd build
|
cd build
|
||||||
cpack -G DEB
|
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 '''
|
sh '''
|
||||||
cd paper
|
cd paper
|
||||||
make
|
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') {
|
stage('Coverage') {
|
||||||
@@ -68,11 +106,14 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
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 '''
|
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
|
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
|
||||||
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
137
README.md
137
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.
|
Intended to replace FoundationDB's skip list.
|
||||||
|
|
||||||
|
Hardware for all benchmarks is a mac m1 2020.
|
||||||
|
|
||||||
# FoundationDB's benchmark
|
# FoundationDB's benchmark
|
||||||
|
|
||||||
## Skip list
|
## Skip list
|
||||||
|
|
||||||
```
|
```
|
||||||
New conflict set: 4.189 sec
|
New conflict set: 1.957 sec
|
||||||
0.298 Mtransactions/sec
|
0.639 Mtransactions/sec
|
||||||
1.194 Mkeys/sec
|
2.555 Mkeys/sec
|
||||||
Detect only: 3.990 sec
|
Detect only: 1.845 sec
|
||||||
0.313 Mtransactions/sec
|
0.678 Mtransactions/sec
|
||||||
1.253 Mkeys/sec
|
2.710 Mkeys/sec
|
||||||
Skiplist only: 2.849 sec
|
Skiplist only: 1.263 sec
|
||||||
0.439 Mtransactions/sec
|
0.990 Mtransactions/sec
|
||||||
1.755 Mkeys/sec
|
3.960 Mkeys/sec
|
||||||
Performance counters:
|
Performance counters:
|
||||||
Build: 0.0913
|
Build: 0.0546
|
||||||
Add: 0.0998
|
Add: 0.0563
|
||||||
Detect: 3.99
|
Detect: 1.84
|
||||||
D.Sort: 0.808
|
D.Sort: 0.412
|
||||||
D.Combine: 0.0309
|
D.Combine: 0.0141
|
||||||
D.CheckRead: 1.67
|
D.CheckRead: 0.671
|
||||||
D.CheckIntraBatch: 0.0305
|
D.CheckIntraBatch: 0.0068
|
||||||
D.MergeWrite: 1.18
|
D.MergeWrite: 0.592
|
||||||
D.RemoveBefore: 0.265
|
D.RemoveBefore: 0.146
|
||||||
```
|
```
|
||||||
|
|
||||||
## Radix tree (this implementation)
|
## Radix tree (this implementation)
|
||||||
|
|
||||||
```
|
```
|
||||||
New conflict set: 2.965 sec
|
New conflict set: 1.366 sec
|
||||||
0.422 Mtransactions/sec
|
0.915 Mtransactions/sec
|
||||||
1.686 Mkeys/sec
|
3.660 Mkeys/sec
|
||||||
Detect only: 2.761 sec
|
Detect only: 1.248 sec
|
||||||
0.453 Mtransactions/sec
|
1.002 Mtransactions/sec
|
||||||
1.811 Mkeys/sec
|
4.007 Mkeys/sec
|
||||||
Skiplist only: 1.580 sec
|
Skiplist only: 0.573 sec
|
||||||
0.791 Mtransactions/sec
|
2.182 Mtransactions/sec
|
||||||
3.165 Mkeys/sec
|
8.730 Mkeys/sec
|
||||||
Performance counters:
|
Performance counters:
|
||||||
Build: 0.0902
|
Build: 0.0594
|
||||||
Add: 0.107
|
Add: 0.0572
|
||||||
Detect: 2.76
|
Detect: 1.25
|
||||||
D.Sort: 0.809
|
D.Sort: 0.418
|
||||||
D.Combine: 0.0309
|
D.Combine: 0.0149
|
||||||
D.CheckRead: 0.658
|
D.CheckRead: 0.232
|
||||||
D.CheckIntraBatch: 0.0294
|
D.CheckIntraBatch: 0.0067
|
||||||
D.MergeWrite: 0.921
|
D.MergeWrite: 0.341
|
||||||
D.RemoveBefore: 0.305
|
D.RemoveBefore: 0.232
|
||||||
```
|
```
|
||||||
|
|
||||||
# Our benchmark
|
# Our benchmark
|
||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark
|
## Skip list
|
||||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
|
||||||
| 325.60 | 3,071,225.77 | 4.8% | 0.77 | `skip list (point reads)`
|
| ns/op | op/s | err% | total | benchmark |
|
||||||
| 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)`
|
| 246.99 | 4,048,700.59 | 0.2% | 0.01 | `point reads` |
|
||||||
| 261.88 | 3,818,471.08 | 1.3% | 0.73 | `skip list (point writes)`
|
| 260.16 | 3,843,784.65 | 0.1% | 0.01 | `prefix reads` |
|
||||||
| 253.54 | 3,944,191.08 | 0.1% | 0.61 | `skip list (prefix writes)`
|
| 493.35 | 2,026,953.19 | 0.1% | 0.01 | `range reads` |
|
||||||
| 258.73 | 3,865,078.52 | 0.8% | 0.62 | `skip list (range writes)`
|
| 462.05 | 2,164,289.23 | 0.6% | 0.01 | `point writes` |
|
||||||
| 489.56 | 2,042,648.19 | 1.8% | 0.01 | `skip list (monotonic increasing point writes)`
|
| 448.19 | 2,231,205.25 | 0.9% | 0.01 | `prefix writes` |
|
||||||
| 14.83 | 67,446,579.75 | 0.1% | 0.04 | `radix tree (point reads)`
|
| 255.83 | 3,908,845.72 | 1.5% | 0.02 | `range writes` |
|
||||||
| 59.68 | 16,756,917.37 | 0.1% | 0.14 | `radix tree (prefix reads)`
|
| 582.63 | 1,716,349.02 | 1.3% | 0.01 | `monotonic increasing point writes` |
|
||||||
| 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)`
|
## Radix tree (this implementation)
|
||||||
| 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)`
|
| ns/op | op/s | err% | total | benchmark |
|
||||||
| 118.37 | 8,448,345.29 | 0.6% | 0.01 | `radix tree (monotonic increasing point writes)`
|
| -----: | ------------: | ---: | ----: | :---------------------------------- |
|
||||||
|
| 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%
|
||||||
|
```
|
||||||
|
@@ -1,13 +1,18 @@
|
|||||||
#include <ConflictSet.h>
|
#include <ConflictSet.h>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <string_view>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace weaselab;
|
||||||
|
|
||||||
double now() {
|
double now() {
|
||||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||||
std::chrono::steady_clock::now().time_since_epoch())
|
std::chrono::steady_clock::now().time_since_epoch())
|
||||||
@@ -28,7 +33,7 @@ constexpr inline size_t rightAlign(size_t offset, size_t alignment) {
|
|||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
// Use with this dataset https://snap.stanford.edu/data/memetracker9.html
|
// 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 checkTime = 0;
|
||||||
double addTime = 0;
|
double addTime = 0;
|
||||||
@@ -40,6 +45,8 @@ int main(int argc, const char **argv) {
|
|||||||
int64_t version = 0;
|
int64_t version = 0;
|
||||||
double timer = 0;
|
double timer = 0;
|
||||||
|
|
||||||
|
int64_t peakMemory = 0;
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
int fd = open(argv[i], O_RDONLY);
|
int fd = open(argv[i], O_RDONLY);
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@@ -96,7 +103,6 @@ int main(int argc, const char **argv) {
|
|||||||
// Add unconditionally so that the load doesn't actually depend on the
|
// Add unconditionally so that the load doesn't actually depend on the
|
||||||
// conflict rate
|
// conflict rate
|
||||||
ConflictSet::WriteRange w;
|
ConflictSet::WriteRange w;
|
||||||
w.writeVersion = ++version;
|
|
||||||
w.begin.p = (const uint8_t *)write.data();
|
w.begin.p = (const uint8_t *)write.data();
|
||||||
w.begin.len = write.size();
|
w.begin.len = write.size();
|
||||||
w.end.len = 0;
|
w.end.len = 0;
|
||||||
@@ -104,12 +110,16 @@ int main(int argc, const char **argv) {
|
|||||||
addBytes += write.size();
|
addBytes += write.size();
|
||||||
|
|
||||||
timer = now();
|
timer = now();
|
||||||
cs.addWrites(&w, 1);
|
cs.addWrites(&w, 1, ++version);
|
||||||
addTime += now() - timer;
|
addTime += now() - timer;
|
||||||
|
|
||||||
write = {};
|
write = {};
|
||||||
reads.clear();
|
reads.clear();
|
||||||
|
|
||||||
|
if (cs.getBytes() > peakMemory) {
|
||||||
|
peakMemory = cs.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
timer = now();
|
timer = now();
|
||||||
cs.setOldestVersion(version - 10000);
|
cs.setOldestVersion(version - 10000);
|
||||||
gcTime += now() - timer;
|
gcTime += now() - timer;
|
||||||
@@ -119,8 +129,9 @@ int main(int argc, const char **argv) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(
|
printf("Check: %g seconds, %g MB/s, Add: %g seconds, %g MB/s, Gc ratio: "
|
||||||
"Check: %g seconds, %g MB/s, Add: %g seconds, %g MB/s, Gc ratio: %g%%\n",
|
"%g%%, Peak idle memory: %g\n",
|
||||||
checkTime, checkBytes / checkTime * 1e-6, addTime,
|
checkTime, checkBytes / checkTime * 1e-6, addTime,
|
||||||
addBytes / addTime * 1e-6, gcTime / (gcTime + addTime) * 1e2);
|
addBytes / addTime * 1e-6, gcTime / (gcTime + addTime) * 1e2,
|
||||||
}
|
double(peakMemory));
|
||||||
|
}
|
||||||
|
114
SkipList.cpp
114
SkipList.cpp
@@ -16,6 +16,8 @@
|
|||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* This source code is modified to compile outside of FoundationDB
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ConflictSet.h"
|
#include "ConflictSet.h"
|
||||||
@@ -149,7 +151,7 @@ private:
|
|||||||
setMaxVersion(level, v);
|
setMaxVersion(level, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy() { free(this); }
|
void destroy() { safe_free(this, getNodeSize()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int getNodeSize() const {
|
int getNodeSize() const {
|
||||||
@@ -268,13 +270,21 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
explicit SkipList(Version version = 0) {
|
explicit SkipList(Version version = 0) {
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
fprintf(stderr, "skip_list: create\n");
|
||||||
|
#endif
|
||||||
header = Node::create(StringRef(), MaxLevels - 1);
|
header = Node::create(StringRef(), MaxLevels - 1);
|
||||||
for (int l = 0; l < MaxLevels; l++) {
|
for (int l = 0; l < MaxLevels; l++) {
|
||||||
header->setNext(l, nullptr);
|
header->setNext(l, nullptr);
|
||||||
header->setMaxVersion(l, version);
|
header->setMaxVersion(l, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~SkipList() { destroy(); }
|
~SkipList() {
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
fprintf(stderr, "skip_list: destroy\n");
|
||||||
|
#endif
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
SkipList(SkipList &&other) noexcept : header(other.header) {
|
SkipList(SkipList &&other) noexcept : header(other.header) {
|
||||||
other.header = nullptr;
|
other.header = nullptr;
|
||||||
}
|
}
|
||||||
@@ -286,7 +296,7 @@ public:
|
|||||||
void swap(SkipList &other) { std::swap(header, other.header); }
|
void swap(SkipList &other) { std::swap(header, other.header); }
|
||||||
|
|
||||||
void addConflictRanges(const Finger *fingers, int rangeCount,
|
void addConflictRanges(const Finger *fingers, int rangeCount,
|
||||||
Version *version) {
|
Version version) {
|
||||||
for (int r = rangeCount - 1; r >= 0; r--) {
|
for (int r = rangeCount - 1; r >= 0; r--) {
|
||||||
const Finger &startF = fingers[r * 2];
|
const Finger &startF = fingers[r * 2];
|
||||||
const Finger &endF = fingers[r * 2 + 1];
|
const Finger &endF = fingers[r * 2 + 1];
|
||||||
@@ -295,7 +305,7 @@ public:
|
|||||||
insert(endF, endF.finger[0]->getMaxVersion(0));
|
insert(endF, endF.finger[0]->getMaxVersion(0));
|
||||||
|
|
||||||
remove(startF, endF);
|
remove(startF, endF);
|
||||||
insert(startF, version[r]);
|
insert(startF, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,7 +598,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;
|
Arena arena;
|
||||||
const int stringCount = count * 2;
|
const int stringCount = count * 2;
|
||||||
|
|
||||||
@@ -602,15 +613,18 @@ 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)}
|
||||||
: keyAfter(arena, values[i * 2]);
|
: keyAfter(arena, values[i * 2]);
|
||||||
writeVersions[i] = w.writeVersion;
|
keyUpdates += 3;
|
||||||
keyUpdates += 2;
|
|
||||||
}
|
}
|
||||||
skipList.find(values, fingers, temp, ss);
|
skipList.find(values, fingers, temp, ss);
|
||||||
skipList.addConflictRanges(fingers, ss / 2, writeVersions);
|
skipList.addConflictRanges(fingers, ss / 2, writeVersion);
|
||||||
ss = stripeSize;
|
ss = stripeSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -621,14 +635,16 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
int temp;
|
int temp;
|
||||||
std::span<const uint8_t> key = removalKey;
|
std::span<const uint8_t> key = removalKey;
|
||||||
skipList.find(&key, &finger, &temp, 1);
|
skipList.find(&key, &finger, &temp, 1);
|
||||||
skipList.removeBefore(oldestVersion, finger, std::exchange(keyUpdates, 0));
|
skipList.removeBefore(oldestVersion, finger, std::exchange(keyUpdates, 10));
|
||||||
removalArena = Arena();
|
removalArena = Arena();
|
||||||
removalKey = copyToArena(
|
removalKey = copyToArena(
|
||||||
removalArena, {finger.getValue().data(), finger.getValue().size()});
|
removalArena, {finger.getValue().data(), finger.getValue().size()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t totalBytes = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int64_t keyUpdates = 0;
|
int64_t keyUpdates = 10;
|
||||||
Arena removalArena;
|
Arena removalArena;
|
||||||
std::span<const uint8_t> removalKey;
|
std::span<const uint8_t> removalKey;
|
||||||
int64_t oldestVersion;
|
int64_t oldestVersion;
|
||||||
@@ -637,24 +653,44 @@ private:
|
|||||||
|
|
||||||
void ConflictSet::check(const ReadRange *reads, Result *results,
|
void ConflictSet::check(const ReadRange *reads, Result *results,
|
||||||
int count) const {
|
int count) const {
|
||||||
return impl->check(reads, results, count);
|
impl->check(reads, results, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConflictSet::addWrites(const WriteRange *writes, int count) {
|
void ConflictSet::addWrites(const WriteRange *writes, int count,
|
||||||
return impl->addWrites(writes, 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) {
|
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)
|
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() {
|
ConflictSet::~ConflictSet() {
|
||||||
if (impl) {
|
if (impl) {
|
||||||
impl->~Impl();
|
impl->~Impl();
|
||||||
free(impl);
|
safe_free(impl, sizeof(Impl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,22 +714,56 @@ ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
|
|||||||
((ConflictSet::Impl *)cs)->check(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,
|
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
|
||||||
int count) {
|
int64_t writeVersion) {
|
||||||
((ConflictSet::Impl *)cs)->addWrites(writes, count);
|
auto *impl = (ConflictSet::Impl *)cs;
|
||||||
|
mallocBytesDelta = 0;
|
||||||
|
impl->addWrites(writes, count, writeVersion);
|
||||||
|
impl->totalBytes += mallocBytesDelta;
|
||||||
|
#if SHOW_MEMORY
|
||||||
|
if (impl->totalBytes != mallocBytes) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void
|
__attribute__((__visibility__("default"))) void
|
||||||
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
||||||
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion);
|
auto *impl = (ConflictSet::Impl *)cs;
|
||||||
|
mallocBytesDelta = 0;
|
||||||
|
impl->setOldestVersion(oldestVersion);
|
||||||
|
impl->totalBytes += mallocBytesDelta;
|
||||||
|
#if SHOW_MEMORY
|
||||||
|
if (impl->totalBytes != mallocBytes) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void *
|
__attribute__((__visibility__("default"))) void *
|
||||||
ConflictSet_create(int64_t oldestVersion) {
|
ConflictSet_create(int64_t oldestVersion) {
|
||||||
return new (safe_malloc(sizeof(ConflictSet::Impl)))
|
mallocBytesDelta = 0;
|
||||||
|
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
|
||||||
ConflictSet::Impl{oldestVersion};
|
ConflictSet::Impl{oldestVersion};
|
||||||
|
result->totalBytes += mallocBytesDelta;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||||
using Impl = ConflictSet::Impl;
|
using Impl = ConflictSet::Impl;
|
||||||
((Impl *)cs)->~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("--- skip_list ---\n");
|
||||||
|
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) {
|
int main(int argc, char **argv) {
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
printf("Running: %s\n", argv[i]);
|
|
||||||
std::ifstream t(argv[i], std::ios::binary);
|
std::ifstream t(argv[i], std::ios::binary);
|
||||||
std::stringstream buffer;
|
std::stringstream buffer;
|
||||||
buffer << t.rdbuf();
|
buffer << t.rdbuf();
|
||||||
|
8
aarch64-symbol-imports.txt
Normal file
8
aarch64-symbol-imports.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
__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
|
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)
|
17
apple-symbol-exports.txt
Normal file
17
apple-symbol-exports.txt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
_ConflictSet_addWrites
|
||||||
|
_ConflictSet_check
|
||||||
|
_ConflictSet_create
|
||||||
|
_ConflictSet_destroy
|
||||||
|
_ConflictSet_getBytes
|
||||||
|
_ConflictSet_setOldestVersion
|
||||||
|
__ZN8weaselab11ConflictSet16setOldestVersionEx
|
||||||
|
__ZN8weaselab11ConflictSet9addWritesEPKNS0_10WriteRangeEix
|
||||||
|
__ZN8weaselab11ConflictSetC1EOS0_
|
||||||
|
__ZN8weaselab11ConflictSetC1Ex
|
||||||
|
__ZN8weaselab11ConflictSetC2EOS0_
|
||||||
|
__ZN8weaselab11ConflictSetC2Ex
|
||||||
|
__ZN8weaselab11ConflictSetD1Ev
|
||||||
|
__ZN8weaselab11ConflictSetD2Ev
|
||||||
|
__ZN8weaselab11ConflictSetaSEOS0_
|
||||||
|
__ZNK8weaselab11ConflictSet5checkEPKNS0_9ReadRangeEPNS0_6ResultEi
|
||||||
|
__ZNK8weaselab11ConflictSet8getBytesEv
|
8
apple-symbol-imports.txt
Normal file
8
apple-symbol-imports.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
__tlv_bootstrap
|
||||||
|
_abort
|
||||||
|
_bzero
|
||||||
|
_free
|
||||||
|
_malloc
|
||||||
|
_memcpy
|
||||||
|
_memmove
|
||||||
|
dyld_stub_binder
|
136
conflict_set.py
Normal file
136
conflict_set.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import ctypes
|
||||||
|
import enum
|
||||||
|
import os
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class _Key(ctypes.Structure):
|
||||||
|
_fields_ = [("p", ctypes.POINTER(ctypes.c_ubyte)), ("len", ctypes.c_int)]
|
||||||
|
|
||||||
|
|
||||||
|
class ReadRange(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("begin", _Key),
|
||||||
|
("end", _Key),
|
||||||
|
("readVersion", ctypes.c_int64),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class WriteRange(ctypes.Structure):
|
||||||
|
_fields_ = [("begin", _Key), ("end", _Key)]
|
||||||
|
|
||||||
|
|
||||||
|
class Result(enum.Enum):
|
||||||
|
COMMIT = 0
|
||||||
|
CONFLICT = 1
|
||||||
|
TOO_OLD = 2
|
||||||
|
|
||||||
|
|
||||||
|
def write(begin: bytes, end: Optional[bytes] = None) -> WriteRange:
|
||||||
|
b = (ctypes.c_ubyte * len(begin)).from_buffer(bytearray(begin))
|
||||||
|
|
||||||
|
if end is None:
|
||||||
|
e = (ctypes.c_ubyte * 0)()
|
||||||
|
else:
|
||||||
|
e = (ctypes.c_ubyte * len(end)).from_buffer(bytearray(end))
|
||||||
|
return WriteRange(_Key(b, len(b)), _Key(e, len(e)))
|
||||||
|
|
||||||
|
|
||||||
|
def read(version: int, begin: bytes, end: Optional[bytes] = None) -> ReadRange:
|
||||||
|
b = (ctypes.c_ubyte * len(begin)).from_buffer(bytearray(begin))
|
||||||
|
if end is None:
|
||||||
|
e = (ctypes.c_ubyte * 0)()
|
||||||
|
else:
|
||||||
|
e = (ctypes.c_ubyte * len(end)).from_buffer(bytearray(end))
|
||||||
|
return ReadRange(_Key(b, len(b)), _Key(e, len(e)), version)
|
||||||
|
|
||||||
|
|
||||||
|
class ConflictSet:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
version: int = 0,
|
||||||
|
build_dir: Optional[str] = None,
|
||||||
|
implementation: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
self._lib = None
|
||||||
|
if build_dir is None:
|
||||||
|
build_dir = os.path.dirname(__file__) + "/build"
|
||||||
|
if implementation is None:
|
||||||
|
implementation = "radix_tree"
|
||||||
|
for f in (
|
||||||
|
build_dir + "/" + implementation + "/libconflict-set.so.0",
|
||||||
|
os.path.dirname(__file__)
|
||||||
|
+ "/build/"
|
||||||
|
+ implementation
|
||||||
|
+ "/libconflict-set.0.dylib",
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
self._lib = ctypes.cdll.LoadLibrary(f)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self._lib is None:
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print(
|
||||||
|
"Could not find libconflict-set implementation " + implementation,
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_create.argtypes = (ctypes.c_int64,)
|
||||||
|
self._lib.ConflictSet_create.restype = ctypes.c_void_p
|
||||||
|
|
||||||
|
self._lib.ConflictSet_check.argtypes = (
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.POINTER(ReadRange),
|
||||||
|
ctypes.POINTER(ctypes.c_int),
|
||||||
|
ctypes.c_int,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_addWrites.argtypes = (
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.POINTER(WriteRange),
|
||||||
|
ctypes.c_int,
|
||||||
|
ctypes.c_int64,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_setOldestVersion.argtypes = (
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.c_int64,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_destroy.argtypes = (ctypes.c_void_p,)
|
||||||
|
|
||||||
|
self._lib.ConflictSet_getBytes.argtypes = (ctypes.c_void_p,)
|
||||||
|
self._lib.ConflictSet_getBytes.restype = ctypes.c_int64
|
||||||
|
|
||||||
|
self.p = self._lib.ConflictSet_create(version)
|
||||||
|
|
||||||
|
def addWrites(self, version: int, *writes: WriteRange):
|
||||||
|
self._lib.ConflictSet_addWrites(
|
||||||
|
self.p, (WriteRange * len(writes))(*writes), len(writes), version
|
||||||
|
)
|
||||||
|
|
||||||
|
def check(self, *reads: ReadRange) -> list[Result]:
|
||||||
|
r = (ctypes.c_int * len(reads))()
|
||||||
|
self._lib.ConflictSet_check(self.p, *reads, r, 1)
|
||||||
|
return [Result(x) for x in r]
|
||||||
|
|
||||||
|
def setOldestVersion(self, version: int) -> None:
|
||||||
|
self._lib.ConflictSet_setOldestVersion(self.p, version)
|
||||||
|
|
||||||
|
def getBytes(self) -> int:
|
||||||
|
return self._lib.ConflictSet_getBytes(self.p)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
if self.p is not None:
|
||||||
|
self._lib.ConflictSet_destroy(self.p)
|
||||||
|
self.p = None
|
||||||
|
|
||||||
|
def __exit__(self, exception_type, exception_value, exception_traceback):
|
||||||
|
self.close()
|
@@ -7,17 +7,19 @@ int main(void) {
|
|||||||
ConflictSet_WriteRange w;
|
ConflictSet_WriteRange w;
|
||||||
ConflictSet_Result result;
|
ConflictSet_Result result;
|
||||||
ConflictSet_ReadRange r;
|
ConflictSet_ReadRange r;
|
||||||
|
int64_t bytes;
|
||||||
w.begin.p = (const uint8_t *)"0000";
|
w.begin.p = (const uint8_t *)"0000";
|
||||||
w.begin.len = 4;
|
w.begin.len = 4;
|
||||||
w.end.len = 0;
|
w.end.len = 0;
|
||||||
w.writeVersion = 1;
|
ConflictSet_addWrites(cs, &w, 1, 1);
|
||||||
ConflictSet_addWrites(cs, &w, 1);
|
|
||||||
r.begin.p = (const uint8_t *)"0000";
|
r.begin.p = (const uint8_t *)"0000";
|
||||||
r.begin.len = 4;
|
r.begin.len = 4;
|
||||||
r.end.len = 0;
|
r.end.len = 0;
|
||||||
r.readVersion = 0;
|
r.readVersion = 0;
|
||||||
ConflictSet_check(cs, &r, &result, 1);
|
ConflictSet_check(cs, &r, &result, 1);
|
||||||
assert(result == ConflictSet_Conflict);
|
assert(result == ConflictSet_Conflict);
|
||||||
|
bytes = ConflictSet_getBytes(cs);
|
||||||
|
assert(bytes > 0);
|
||||||
ConflictSet_destroy(cs);
|
ConflictSet_destroy(cs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace weaselab;
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
ConflictSet cs(0);
|
ConflictSet cs(0);
|
||||||
ConflictSet::WriteRange w;
|
ConflictSet::WriteRange w;
|
||||||
w.begin.p = (const uint8_t *)"0000";
|
w.begin.p = (const uint8_t *)"0000";
|
||||||
w.begin.len = 4;
|
w.begin.len = 4;
|
||||||
w.end.len = 0;
|
w.end.len = 0;
|
||||||
w.writeVersion = 1;
|
cs.addWrites(&w, 1, 1);
|
||||||
cs.addWrites(&w, 1);
|
|
||||||
ConflictSet::Result result;
|
ConflictSet::Result result;
|
||||||
ConflictSet::ReadRange r;
|
ConflictSet::ReadRange r;
|
||||||
r.begin.p = (const uint8_t *)"0000";
|
r.begin.p = (const uint8_t *)"0000";
|
||||||
@@ -18,4 +19,6 @@ int main(void) {
|
|||||||
r.readVersion = 0;
|
r.readVersion = 0;
|
||||||
cs.check(&r, &result, 1);
|
cs.check(&r, &result, 1);
|
||||||
assert(result == ConflictSet::Conflict);
|
assert(result == ConflictSet::Conflict);
|
||||||
|
int64_t bytes = cs.getBytes();
|
||||||
|
assert(bytes > 0);
|
||||||
}
|
}
|
||||||
|
BIN
corpus/000d72883139e535b50cc5872c57e73db3f10f97
Normal file
BIN
corpus/000d72883139e535b50cc5872c57e73db3f10f97
Normal file
Binary file not shown.
BIN
corpus/001a4385cfd31070dd75eaa3e1ad21d0b25687d6
Normal file
BIN
corpus/001a4385cfd31070dd75eaa3e1ad21d0b25687d6
Normal file
Binary file not shown.
BIN
corpus/007b28db8fbd69dba10657e88e2db37747818c02
Normal file
BIN
corpus/007b28db8fbd69dba10657e88e2db37747818c02
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/017fe0001e79270892ec9a596580883273a1e4a2
Normal file
BIN
corpus/017fe0001e79270892ec9a596580883273a1e4a2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/02a1f402f107a5dc508b212da227d87b4ffa8904
Normal file
BIN
corpus/02a1f402f107a5dc508b212da227d87b4ffa8904
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/03262648f007cc440c36b2bef091dd76295074c5
Normal file
BIN
corpus/03262648f007cc440c36b2bef091dd76295074c5
Normal file
Binary file not shown.
BIN
corpus/0418013be6b44b43621f5c46a3487212c0f91a40
Normal file
BIN
corpus/0418013be6b44b43621f5c46a3487212c0f91a40
Normal file
Binary file not shown.
BIN
corpus/044269ca54d9557b8b91f3b47ec32d7afd9f6a87
Normal file
BIN
corpus/044269ca54d9557b8b91f3b47ec32d7afd9f6a87
Normal file
Binary file not shown.
BIN
corpus/0467f1f20c94c212ead6664b420e31e39159f437
Normal file
BIN
corpus/0467f1f20c94c212ead6664b420e31e39159f437
Normal file
Binary file not shown.
BIN
corpus/048484b4d6cf7bacce667023dea09dd052d74b6d
Normal file
BIN
corpus/048484b4d6cf7bacce667023dea09dd052d74b6d
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/07e675857aae0f133f253f146685f4c8e7492114
Normal file
BIN
corpus/07e675857aae0f133f253f146685f4c8e7492114
Normal file
Binary file not shown.
BIN
corpus/09328b5c92e0e4875e2f150ee2a013e765998a13
Normal file
BIN
corpus/09328b5c92e0e4875e2f150ee2a013e765998a13
Normal file
Binary file not shown.
BIN
corpus/099cd4e9ec7dfd33064b54ab678ccf2d0724b6ae
Normal file
BIN
corpus/099cd4e9ec7dfd33064b54ab678ccf2d0724b6ae
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/09e702c2f99064a568d4bc7ba5d8581fdc6f2071
Normal file
BIN
corpus/09e702c2f99064a568d4bc7ba5d8581fdc6f2071
Normal file
Binary file not shown.
BIN
corpus/09e7f7a7e9967f1d744fca5a2e10c0e6514a3551
Normal file
BIN
corpus/09e7f7a7e9967f1d744fca5a2e10c0e6514a3551
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0b4deead0afd2d2cf7bb0cd7bf56259b5f484dcb
Normal file
BIN
corpus/0b4deead0afd2d2cf7bb0cd7bf56259b5f484dcb
Normal file
Binary file not shown.
BIN
corpus/0cf423dcbda59e334384410c83cf003aaf6afca1
Normal file
BIN
corpus/0cf423dcbda59e334384410c83cf003aaf6afca1
Normal file
Binary file not shown.
BIN
corpus/0e21b3fc3c827043685bfb846df055c5f03aae96
Normal file
BIN
corpus/0e21b3fc3c827043685bfb846df055c5f03aae96
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0e31aad966875f897220c2437c69326443675fc7
Normal file
BIN
corpus/0e31aad966875f897220c2437c69326443675fc7
Normal file
Binary file not shown.
BIN
corpus/0e4de14128ec7b61ae2cb2835fddd641845ee743
Normal file
BIN
corpus/0e4de14128ec7b61ae2cb2835fddd641845ee743
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0f2768b4a7a65be84db67f2d6cebe5fe04bce172
Normal file
BIN
corpus/0f2768b4a7a65be84db67f2d6cebe5fe04bce172
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/10cc69d9013596429f0912712c0137591973c2d7
Normal file
BIN
corpus/10cc69d9013596429f0912712c0137591973c2d7
Normal file
Binary file not shown.
BIN
corpus/11e033caa0ce581acf472931b02588a6b2faa55d
Normal file
BIN
corpus/11e033caa0ce581acf472931b02588a6b2faa55d
Normal file
Binary file not shown.
BIN
corpus/12128a5bddff249ab102e679bb40da952bb94b5c
Normal file
BIN
corpus/12128a5bddff249ab102e679bb40da952bb94b5c
Normal file
Binary file not shown.
BIN
corpus/12ae1e1ff00a8c79f7a687e2aa70e94aca1dc34d
Normal file
BIN
corpus/12ae1e1ff00a8c79f7a687e2aa70e94aca1dc34d
Normal file
Binary file not shown.
BIN
corpus/13227d7d1bc7355aed3df9a4ea012d4a391764e7
Normal file
BIN
corpus/13227d7d1bc7355aed3df9a4ea012d4a391764e7
Normal file
Binary file not shown.
BIN
corpus/147990fa0ada11fb7025a8534f1e4f85164869b1
Normal file
BIN
corpus/147990fa0ada11fb7025a8534f1e4f85164869b1
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/156b43edfd8dc756184017bf78e382e4d129e33a
Normal file
BIN
corpus/156b43edfd8dc756184017bf78e382e4d129e33a
Normal file
Binary file not shown.
BIN
corpus/1607b170ded5a8458ab9b1514d34f8ed4ddd2975
Normal file
BIN
corpus/1607b170ded5a8458ab9b1514d34f8ed4ddd2975
Normal file
Binary file not shown.
BIN
corpus/160b10e0be9ea9eedfc99f5e3dc106bf50fc5f6d
Normal file
BIN
corpus/160b10e0be9ea9eedfc99f5e3dc106bf50fc5f6d
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1632064eae678ef9865ef9eeb43c7ebeca71422d
Normal file
BIN
corpus/1632064eae678ef9865ef9eeb43c7ebeca71422d
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/17a4cd288c819b380f7995273ad0414dbfcaffba
Normal file
BIN
corpus/17a4cd288c819b380f7995273ad0414dbfcaffba
Normal file
Binary file not shown.
BIN
corpus/18b6eaeb039242f729c8e3f90ea702dd2584e768
Normal file
BIN
corpus/18b6eaeb039242f729c8e3f90ea702dd2584e768
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/19101c188a484270f33420caa342612d44681616
Normal file
BIN
corpus/19101c188a484270f33420caa342612d44681616
Normal file
Binary file not shown.
BIN
corpus/197cb73b1a29e35935973aae7ae26b6c7ab92d07
Normal file
BIN
corpus/197cb73b1a29e35935973aae7ae26b6c7ab92d07
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1a69474ab8ba1187063bd0eb5d4899a203050917
Normal file
BIN
corpus/1a69474ab8ba1187063bd0eb5d4899a203050917
Normal file
Binary file not shown.
BIN
corpus/1a821dc4f6299984feff1ef1e7db28fe185158a4
Normal file
BIN
corpus/1a821dc4f6299984feff1ef1e7db28fe185158a4
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1b8a5ae93daf3a194087cc9747295352d7bb906b
Normal file
BIN
corpus/1b8a5ae93daf3a194087cc9747295352d7bb906b
Normal file
Binary file not shown.
BIN
corpus/1bd30a30823419a12d004729baa96734e410cbed
Normal file
BIN
corpus/1bd30a30823419a12d004729baa96734e410cbed
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1ce8fc23367508b9c141f66976e888910298d64e
Normal file
BIN
corpus/1ce8fc23367508b9c141f66976e888910298d64e
Normal file
Binary file not shown.
BIN
corpus/1d85043e4ef57d7d173ab3995f414fffc293f806
Normal file
BIN
corpus/1d85043e4ef57d7d173ab3995f414fffc293f806
Normal file
Binary file not shown.
BIN
corpus/1deefa50797a868862a42b0a5123bca56ac6dda3
Normal file
BIN
corpus/1deefa50797a868862a42b0a5123bca56ac6dda3
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1f0ed103e089f3d8e1677f2584de062e749325c9
Normal file
BIN
corpus/1f0ed103e089f3d8e1677f2584de062e749325c9
Normal file
Binary file not shown.
BIN
corpus/1f7df9092b27aac286e6b6083ea3a36fdc5de0dc
Normal file
BIN
corpus/1f7df9092b27aac286e6b6083ea3a36fdc5de0dc
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/20fbe40374050cc9baa7a98db1f38ab72fa93f56
Normal file
BIN
corpus/20fbe40374050cc9baa7a98db1f38ab72fa93f56
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/21d28bbe4e26ea1106971daa2dfc29e5494e96f5
Normal file
BIN
corpus/21d28bbe4e26ea1106971daa2dfc29e5494e96f5
Normal file
Binary file not shown.
BIN
corpus/220ff7bdf7c3beb959d74588e3a62adaea87c03a
Normal file
BIN
corpus/220ff7bdf7c3beb959d74588e3a62adaea87c03a
Normal file
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