Compare commits
44 Commits
v0.0.8
...
55271ad06c
Author | SHA1 | Date | |
---|---|---|---|
55271ad06c | |||
e675612599 | |||
42b5d50492 | |||
6394995def | |||
c649bc7964 | |||
ec85a06d01 | |||
fb9f5ce6f4 | |||
2b1c710953 | |||
ebf281220b | |||
6051b2fb2e | |||
11c3ca6766 | |||
b45dec2f1f | |||
c5e9f18c47 | |||
cebbf89cbe | |||
abb791d86b | |||
12f361f33a | |||
640c1ca9dd | |||
b7d54d44e1 | |||
95596f831f | |||
542371d562 | |||
958a4e2d0e | |||
8ce14c58a4 | |||
56e847b63c | |||
7fd1c9e140 | |||
ebaac253e2 | |||
9b470a367c | |||
e7806a36d1 | |||
ffd1dfe74d | |||
c39af9117f | |||
ed274c24d7 | |||
cecfcc0da7 | |||
f6edde0e50 | |||
04ac41a7e7 | |||
354920f86f | |||
bfd02503e7 | |||
d0bd293f8d | |||
41e887c358 | |||
e394e3d96a | |||
3288c583e4 | |||
ef14003781 | |||
3ac16bc966 | |||
1e82f7fe22 | |||
4182d904c5 | |||
bd8ed4e7bd |
@@ -358,7 +358,14 @@ void benchWorstCaseForRadixRangeRead() {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void benchCreateAndDestroy() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
|
||||||
|
bench.run("create and destroy", [&]() { ConflictSet cs{0}; });
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
benchConflictSet();
|
benchConflictSet();
|
||||||
benchWorstCaseForRadixRangeRead();
|
benchWorstCaseForRadixRangeRead();
|
||||||
|
benchCreateAndDestroy();
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
project(
|
project(
|
||||||
conflict-set
|
conflict-set
|
||||||
VERSION 0.0.8
|
VERSION 0.0.10
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
||||||
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
||||||
@@ -60,6 +60,8 @@ cmake_pop_check_state()
|
|||||||
option(USE_SIMD_FALLBACK
|
option(USE_SIMD_FALLBACK
|
||||||
"Use fallback implementations of functions that use SIMD" OFF)
|
"Use fallback implementations of functions that use SIMD" OFF)
|
||||||
|
|
||||||
|
option(DISABLE_TSAN "Disable TSAN" 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_CURRENT_SOURCE_DIR}/third_party/valgrind)
|
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/valgrind)
|
||||||
@@ -245,7 +247,7 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# tsan tests
|
# tsan tests
|
||||||
if(NOT CMAKE_CROSSCOMPILING AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
if(NOT CMAKE_CROSSCOMPILING AND NOT DISABLE_TSAN)
|
||||||
add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
||||||
target_compile_options(tsan_driver PRIVATE ${TEST_FLAGS} -fsanitize=thread)
|
target_compile_options(tsan_driver PRIVATE ${TEST_FLAGS} -fsanitize=thread)
|
||||||
target_link_options(tsan_driver PRIVATE -fsanitize=thread)
|
target_link_options(tsan_driver PRIVATE -fsanitize=thread)
|
||||||
@@ -351,6 +353,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
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)
|
||||||
|
|
||||||
|
# fuzzer-based perf
|
||||||
|
add_executable(driver_perf TestDriver.cpp)
|
||||||
|
target_compile_definitions(driver_perf PRIVATE PERF_TEST=1)
|
||||||
|
target_link_libraries(driver_perf PRIVATE ${PROJECT_NAME})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# packaging
|
# packaging
|
||||||
|
585
ConflictSet.cpp
585
ConflictSet.cpp
File diff suppressed because it is too large
Load Diff
@@ -98,6 +98,13 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
|||||||
|
|
||||||
int64_t ConflictSet::getBytes() const { return -1; }
|
int64_t ConflictSet::getBytes() const { return -1; }
|
||||||
|
|
||||||
|
void ConflictSet::getMetricsV1(MetricsV1 **metrics, int *count) const {
|
||||||
|
*metrics = nullptr;
|
||||||
|
*count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ConflictSet::MetricsV1::getValue() const { return 0; }
|
||||||
|
|
||||||
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}) {}
|
||||||
|
|
||||||
|
190
Internal.h
190
Internal.h
@@ -578,12 +578,16 @@ inline const char *resultToStr(ConflictSet::Result r) {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <class ConflictSetImpl> struct TestDriver {
|
template <class ConflictSetImpl, bool kEnableAssertions = true>
|
||||||
Arbitrary arbitrary;
|
struct TestDriver {
|
||||||
explicit TestDriver(const uint8_t *data, size_t size)
|
Arbitrary *arbitrary;
|
||||||
: arbitrary({data, size}) {}
|
explicit TestDriver(Arbitrary &a) : arbitrary(&a) {
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr, "%p Initial version: {%" PRId64 "}\n", this, writeVersion);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int64_t oldestVersion = arbitrary.next();
|
int64_t oldestVersion = arbitrary->next();
|
||||||
int64_t writeVersion = oldestVersion;
|
int64_t writeVersion = oldestVersion;
|
||||||
ConflictSetImpl cs{oldestVersion};
|
ConflictSetImpl cs{oldestVersion};
|
||||||
ReferenceImpl refImpl{oldestVersion};
|
ReferenceImpl refImpl{oldestVersion};
|
||||||
@@ -592,33 +596,34 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
||||||
const int prefixLen = arbitrary.bounded(512);
|
const int prefixLen = arbitrary->bounded(512);
|
||||||
const int prefixByte = arbitrary.randT<uint8_t>();
|
const int prefixByte = arbitrary->randT<uint8_t>();
|
||||||
|
|
||||||
// Call until it returns true, for "done". Check internal invariants etc
|
// Call until it returns true, for "done". Check internal invariants etc
|
||||||
// between calls to next.
|
// between calls to next.
|
||||||
bool next() {
|
bool next() {
|
||||||
assert(cs.getBytes() >= 0);
|
assert(cs.getBytes() >= 0);
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Arena arena;
|
Arena arena;
|
||||||
{
|
{
|
||||||
int numPointWrites = arbitrary.bounded(100);
|
int numPointWrites = arbitrary->bounded(100);
|
||||||
int numRangeWrites = arbitrary.bounded(100);
|
int numRangeWrites = arbitrary->bounded(100);
|
||||||
int64_t v = (writeVersion += arbitrary.bounded(10) ? arbitrary.bounded(10)
|
int64_t v =
|
||||||
: arbitrary.next());
|
(writeVersion +=
|
||||||
|
arbitrary->bounded(10) ? arbitrary->bounded(10) : arbitrary->next());
|
||||||
auto *writes =
|
auto *writes =
|
||||||
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
||||||
auto keys = set<std::string_view>(arena);
|
auto keys = set<std::string_view>(arena);
|
||||||
while (int(keys.size()) < numPointWrites + numRangeWrites * 2) {
|
while (int(keys.size()) < numPointWrites + numRangeWrites * 2) {
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +633,7 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
rangesRemaining = numRangeWrites;
|
rangesRemaining = numRangeWrites;
|
||||||
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
||||||
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
||||||
? bool(arbitrary.bounded(2))
|
? bool(arbitrary->bounded(2))
|
||||||
: pointsRemaining > 0;
|
: pointsRemaining > 0;
|
||||||
if (pointRead) {
|
if (pointRead) {
|
||||||
assert(pointsRemaining > 0);
|
assert(pointsRemaining > 0);
|
||||||
@@ -647,33 +652,20 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
++iter;
|
++iter;
|
||||||
--rangesRemaining;
|
--rangesRemaining;
|
||||||
}
|
}
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
|
||||||
if (writes[i].end.len == 0) {
|
|
||||||
fprintf(stderr, "Write: {%s}\n", printable(writes[i].begin).c_str());
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Write: [%s, %s)\n",
|
|
||||||
printable(writes[i].begin).c_str(),
|
|
||||||
printable(writes[i].end).c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
assert(iter == keys.end());
|
assert(iter == keys.end());
|
||||||
assert(i == numPointWrites + numRangeWrites);
|
assert(i == numPointWrites + numRangeWrites);
|
||||||
|
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
|
||||||
fprintf(stderr, "Write @ %" PRId64 "\n", v);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Test non-canonical writes
|
// Test non-canonical writes
|
||||||
if (numPointWrites > 0) {
|
if (numPointWrites > 0) {
|
||||||
int overlaps = arbitrary.bounded(numPointWrites);
|
int overlaps = arbitrary->bounded(numPointWrites);
|
||||||
for (int i = 0; i < numPointWrites + numRangeWrites && overlaps > 0;
|
for (int i = 0; i < numPointWrites + numRangeWrites && overlaps > 0;
|
||||||
++i) {
|
++i) {
|
||||||
if (writes[i].end.len == 0) {
|
if (writes[i].end.len == 0) {
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
arbitrary->randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
writes[i].end.len = keyLen;
|
writes[i].end.len = keyLen;
|
||||||
writes[i].end.p = begin;
|
writes[i].end.p = begin;
|
||||||
auto c =
|
auto c =
|
||||||
@@ -691,10 +683,10 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (arbitrary.bounded(2)) {
|
if (arbitrary->bounded(2)) {
|
||||||
// Shuffle writes
|
// Shuffle writes
|
||||||
for (int i = numPointWrites + numRangeWrites - 1; i > 0; --i) {
|
for (int i = numPointWrites + numRangeWrites - 1; i > 0; --i) {
|
||||||
int j = arbitrary.bounded(i + 1);
|
int j = arbitrary->bounded(i + 1);
|
||||||
if (i != j) {
|
if (i != j) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(writes[i], writes[j]);
|
swap(writes[i], writes[j]);
|
||||||
@@ -702,37 +694,78 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldestVersion +=
|
||||||
|
arbitrary->bounded(10) ? arbitrary->bounded(10) : arbitrary->next();
|
||||||
|
oldestVersion = std::min(oldestVersion, writeVersion);
|
||||||
|
|
||||||
|
#ifdef THREAD_TEST
|
||||||
|
std::latch ready{1};
|
||||||
|
std::thread thread2{[&]() {
|
||||||
|
ready.count_down();
|
||||||
|
ConflictSet::MetricsV1 *m;
|
||||||
|
int count;
|
||||||
|
cs.getMetricsV1(&m, &count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
m[i].getValue();
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
ready.wait();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
for (int i = 0; i < numPointWrites + numRangeWrites; ++i) {
|
||||||
|
if (writes[i].end.len == 0) {
|
||||||
|
fprintf(stderr, "%p Write: {%s}\n", this,
|
||||||
|
printable(writes[i].begin).c_str());
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%p Write: [%s, %s)\n", this,
|
||||||
|
printable(writes[i].begin).c_str(),
|
||||||
|
printable(writes[i].end).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%p Write @ %" PRId64 "\n", this, v);
|
||||||
|
#endif
|
||||||
|
|
||||||
CALLGRIND_START_INSTRUMENTATION;
|
CALLGRIND_START_INSTRUMENTATION;
|
||||||
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
|
|
||||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
if constexpr (kEnableAssertions) {
|
||||||
|
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
|
}
|
||||||
|
|
||||||
oldestVersion +=
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
arbitrary.bounded(10) ? arbitrary.bounded(10) : arbitrary.next();
|
fprintf(stderr, "%p Set oldest version: %" PRId64 "\n", this,
|
||||||
oldestVersion = std::min(oldestVersion, writeVersion);
|
oldestVersion);
|
||||||
|
#endif
|
||||||
cs.setOldestVersion(oldestVersion);
|
cs.setOldestVersion(oldestVersion);
|
||||||
refImpl.setOldestVersion(oldestVersion);
|
if constexpr (kEnableAssertions) {
|
||||||
|
refImpl.setOldestVersion(oldestVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef THREAD_TEST
|
||||||
|
thread2.join();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int numPointReads = arbitrary.bounded(100);
|
int numPointReads = arbitrary->bounded(100);
|
||||||
int numRangeReads = arbitrary.bounded(100);
|
int numRangeReads = arbitrary->bounded(100);
|
||||||
|
|
||||||
int64_t v = std::max<int64_t>(writeVersion - (arbitrary.bounded(10)
|
int64_t v = std::max<int64_t>(writeVersion - (arbitrary->bounded(10)
|
||||||
? arbitrary.bounded(10)
|
? arbitrary->bounded(10)
|
||||||
: arbitrary.next()),
|
: arbitrary->next()),
|
||||||
0);
|
0);
|
||||||
auto *reads =
|
auto *reads =
|
||||||
new (arena) ConflictSet::ReadRange[numPointReads + numRangeReads];
|
new (arena) ConflictSet::ReadRange[numPointReads + numRangeReads];
|
||||||
auto keys = set<std::string_view>(arena);
|
auto keys = set<std::string_view>(arena);
|
||||||
while (int(keys.size()) < numPointReads + numRangeReads * 2) {
|
while (int(keys.size()) < numPointReads + numRangeReads * 2) {
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,7 +774,7 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
for (int pointsRemaining = numPointReads, rangesRemaining = numRangeReads;
|
for (int pointsRemaining = numPointReads, rangesRemaining = numRangeReads;
|
||||||
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
||||||
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
||||||
? bool(arbitrary.bounded(2))
|
? bool(arbitrary->bounded(2))
|
||||||
: pointsRemaining > 0;
|
: pointsRemaining > 0;
|
||||||
if (pointRead) {
|
if (pointRead) {
|
||||||
assert(pointsRemaining > 0);
|
assert(pointsRemaining > 0);
|
||||||
@@ -763,10 +796,10 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
reads[i].readVersion = v;
|
reads[i].readVersion = v;
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
if (reads[i].end.len == 0) {
|
if (reads[i].end.len == 0) {
|
||||||
fprintf(stderr, "Read: {%s} @ %" PRId64 "\n",
|
fprintf(stderr, "%p Read: {%s} @ %" PRId64 "\n", this,
|
||||||
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Read: [%s, %s) @ %" PRId64 "\n",
|
fprintf(stderr, "%p Read: [%s, %s) @ %" PRId64 "\n", this,
|
||||||
printable(reads[i].begin).c_str(),
|
printable(reads[i].begin).c_str(),
|
||||||
printable(reads[i].end).c_str(), reads[i].readVersion);
|
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
}
|
}
|
||||||
@@ -785,7 +818,15 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
std::latch ready{1};
|
std::latch ready{1};
|
||||||
std::thread thread2{[&]() {
|
std::thread thread2{[&]() {
|
||||||
ready.count_down();
|
ready.count_down();
|
||||||
|
// Call all const methods
|
||||||
cs.check(reads, results3, numPointReads + numRangeReads);
|
cs.check(reads, results3, numPointReads + numRangeReads);
|
||||||
|
cs.getBytes();
|
||||||
|
ConflictSet::MetricsV1 *m;
|
||||||
|
int count;
|
||||||
|
cs.getMetricsV1(&m, &count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
m[i].getValue();
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
ready.wait();
|
ready.wait();
|
||||||
#endif
|
#endif
|
||||||
@@ -794,26 +835,40 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
cs.check(reads, results1, numPointReads + numRangeReads);
|
cs.check(reads, results1, numPointReads + numRangeReads);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
|
|
||||||
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
if constexpr (kEnableAssertions) {
|
||||||
|
// Call remaining const methods
|
||||||
|
cs.getBytes();
|
||||||
|
ConflictSet::MetricsV1 *m;
|
||||||
|
int count;
|
||||||
|
cs.getMetricsV1(&m, &count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
m[i].getValue();
|
||||||
|
}
|
||||||
|
|
||||||
auto compareResults = [reads](ConflictSet::Result *results1,
|
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
||||||
ConflictSet::Result *results2, int count) {
|
}
|
||||||
|
|
||||||
|
auto compareResults = [reads, this](ConflictSet::Result *results1,
|
||||||
|
ConflictSet::Result *results2,
|
||||||
|
int count) {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
if (results1[i] != results2[i]) {
|
if (results1[i] != results2[i]) {
|
||||||
if (reads[i].end.len == 0) {
|
if (reads[i].end.len == 0) {
|
||||||
fprintf(stderr,
|
|
||||||
"Expected %s, got %s for read of {%s} at version %" PRId64
|
|
||||||
"\n",
|
|
||||||
resultToStr(results2[i]), resultToStr(results1[i]),
|
|
||||||
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
|
||||||
} else {
|
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"Expected %s, got %s for read of [%s, %s) at version %" PRId64
|
"%p Expected %s, got %s for read of {%s} at version %" PRId64
|
||||||
"\n",
|
"\n",
|
||||||
resultToStr(results2[i]), resultToStr(results1[i]),
|
(void *)this, resultToStr(results2[i]),
|
||||||
printable(reads[i].begin).c_str(),
|
resultToStr(results1[i]), printable(reads[i].begin).c_str(),
|
||||||
printable(reads[i].end).c_str(), reads[i].readVersion);
|
reads[i].readVersion);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%p Expected %s, got %s for read of [%s, %s) at version "
|
||||||
|
"%" PRId64 "\n",
|
||||||
|
(void *)this, resultToStr(results2[i]),
|
||||||
|
resultToStr(results1[i]),
|
||||||
|
printable(reads[i].begin).c_str(),
|
||||||
|
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -821,9 +876,12 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!compareResults(results1, results2, numPointReads + numRangeReads)) {
|
if constexpr (kEnableAssertions) {
|
||||||
ok = false;
|
if (!compareResults(results1, results2,
|
||||||
return true;
|
numPointReads + numRangeReads)) {
|
||||||
|
ok = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef THREAD_TEST
|
#ifdef THREAD_TEST
|
||||||
|
13
Jenkinsfile
vendored
13
Jenkinsfile
vendored
@@ -48,6 +48,17 @@ pipeline {
|
|||||||
recordIssues(tools: [clang()])
|
recordIssues(tools: [clang()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage('Debug') {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
args '-v /home/jenkins/ccache:/ccache'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
CleanBuildAndTest("-DCMAKE_BUILD_TYPE=Debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
stage('SIMD fallback') {
|
stage('SIMD fallback') {
|
||||||
agent {
|
agent {
|
||||||
dockerfile {
|
dockerfile {
|
||||||
@@ -106,7 +117,7 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -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 -DDISABLE_TSAN=ON")
|
||||||
sh '''
|
sh '''
|
||||||
gcovr -f ConflictSet.cpp --cobertura > build/coverage.xml
|
gcovr -f ConflictSet.cpp --cobertura > build/coverage.xml
|
||||||
'''
|
'''
|
||||||
|
@@ -129,6 +129,16 @@ int main(int argc, const char **argv) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConflictSet::MetricsV1 *metrics;
|
||||||
|
int metricsCount;
|
||||||
|
cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
|
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||||
|
printf("# TYPE %s %s\n", metrics[i].name,
|
||||||
|
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
||||||
|
printf("%s %g\n", metrics[i].name, metrics[i].getValue());
|
||||||
|
}
|
||||||
|
|
||||||
printf("Check: %g seconds, %g MB/s, Add: %g seconds, %g MB/s, Gc ratio: "
|
printf("Check: %g seconds, %g MB/s, Add: %g seconds, %g MB/s, Gc ratio: "
|
||||||
"%g%%, Peak idle memory: %g\n",
|
"%g%%, Peak idle memory: %g\n",
|
||||||
checkTime, checkBytes / checkTime * 1e-6, addTime,
|
checkTime, checkBytes / checkTime * 1e-6, addTime,
|
||||||
|
24
SkipList.cpp
24
SkipList.cpp
@@ -42,7 +42,7 @@ std::span<const uint8_t> copyToArena(Arena &arena,
|
|||||||
}
|
}
|
||||||
|
|
||||||
using Version = int64_t;
|
using Version = int64_t;
|
||||||
#define force_inline __attribute__((always_inline))
|
#define force_inline inline __attribute__((always_inline))
|
||||||
using StringRef = std::span<const uint8_t>;
|
using StringRef = std::span<const uint8_t>;
|
||||||
|
|
||||||
struct KeyRangeRef {
|
struct KeyRangeRef {
|
||||||
@@ -778,10 +778,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
for (int s = stripes - 1; s >= 0; s--) {
|
for (int s = stripes - 1; s >= 0; s--) {
|
||||||
for (int i = 0; i * 2 < ss; ++i) {
|
for (int i = 0; i * 2 < ss; ++i) {
|
||||||
const auto &w = combinedWriteConflictRanges[s * stripeSize / 2 + i];
|
const auto &w = combinedWriteConflictRanges[s * stripeSize / 2 + i];
|
||||||
#if DEBUG_VERBOSE
|
|
||||||
printf("Write begin: %s\n", printable(w.begin).c_str());
|
|
||||||
fflush(stdout);
|
|
||||||
#endif
|
|
||||||
values[i * 2] = w.first;
|
values[i * 2] = w.first;
|
||||||
values[i * 2 + 1] = w.second;
|
values[i * 2 + 1] = w.second;
|
||||||
keyUpdates += 3;
|
keyUpdates += 3;
|
||||||
@@ -861,6 +857,16 @@ void internal_destroy(ConflictSet::Impl *impl) {
|
|||||||
|
|
||||||
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; }
|
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; }
|
||||||
|
|
||||||
|
void internal_getMetricsV1(ConflictSet::Impl *impl,
|
||||||
|
ConflictSet::MetricsV1 **metrics, int *count) {
|
||||||
|
*metrics = nullptr;
|
||||||
|
*count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double internal_getMetricValue(const ConflictSet::MetricsV1 *metric) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void ConflictSet::check(const ReadRange *reads, Result *results,
|
void ConflictSet::check(const ReadRange *reads, Result *results,
|
||||||
int count) const {
|
int count) const {
|
||||||
internal_check(impl, reads, results, count);
|
internal_check(impl, reads, results, count);
|
||||||
@@ -877,6 +883,14 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
|||||||
|
|
||||||
int64_t ConflictSet::getBytes() const { return internal_getBytes(impl); }
|
int64_t ConflictSet::getBytes() const { return internal_getBytes(impl); }
|
||||||
|
|
||||||
|
void ConflictSet::getMetricsV1(MetricsV1 **metrics, int *count) const {
|
||||||
|
return internal_getMetricsV1(impl, metrics, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
double ConflictSet::MetricsV1::getValue() const {
|
||||||
|
return internal_getMetricValue(this);
|
||||||
|
}
|
||||||
|
|
||||||
ConflictSet::ConflictSet(int64_t oldestVersion)
|
ConflictSet::ConflictSet(int64_t oldestVersion)
|
||||||
: impl(internal_create(oldestVersion)) {}
|
: impl(internal_create(oldestVersion)) {}
|
||||||
|
|
||||||
|
@@ -3,17 +3,68 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#ifndef PERF_TEST
|
||||||
|
#define PERF_TEST 0
|
||||||
|
#endif
|
||||||
|
|
||||||
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) {
|
||||||
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();
|
||||||
auto str = buffer.str();
|
auto str = buffer.str();
|
||||||
TestDriver<ConflictSet> driver{(const uint8_t *)str.data(), str.size()};
|
Arbitrary arbitrary({(const uint8_t *)str.data(), str.size()});
|
||||||
while (!driver.next())
|
TestDriver<ConflictSet, !PERF_TEST> driver1{arbitrary};
|
||||||
;
|
TestDriver<ConflictSet, !PERF_TEST> driver2{arbitrary};
|
||||||
if (!driver.ok) {
|
bool done1 = false;
|
||||||
abort();
|
bool done2 = false;
|
||||||
|
for (;;) {
|
||||||
|
if (!done1) {
|
||||||
|
done1 = driver1.next();
|
||||||
|
if (!driver1.ok) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!done2) {
|
||||||
|
done2 = driver2.next();
|
||||||
|
if (!driver2.ok) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done1 && done2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ConflictSet::MetricsV1 *metrics;
|
||||||
|
int metricsCount;
|
||||||
|
driver1.cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
printf("#################### METRICS for ConflictSet 1 for %s "
|
||||||
|
"####################\n",
|
||||||
|
argv[i]);
|
||||||
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
|
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||||
|
printf("# TYPE %s %s\n", metrics[i].name,
|
||||||
|
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
||||||
|
printf("%s %g\n", metrics[i].name, metrics[i].getValue());
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ConflictSet::MetricsV1 *metrics;
|
||||||
|
int metricsCount;
|
||||||
|
driver2.cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
printf("#################### METRICS for ConflictSet 2 for %s "
|
||||||
|
"####################\n",
|
||||||
|
argv[i]);
|
||||||
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
|
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||||
|
printf("# TYPE %s %s\n", metrics[i].name,
|
||||||
|
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
||||||
|
printf("%s %g\n", metrics[i].name, metrics[i].getValue());
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
__aarch64_cas8_relax
|
||||||
|
__getauxval@GLIBC_2.17
|
||||||
__stack_chk_fail@GLIBC_2.17
|
__stack_chk_fail@GLIBC_2.17
|
||||||
__stack_chk_guard@GLIBC_2.17
|
__stack_chk_guard@GLIBC_2.17
|
||||||
abort@GLIBC_2.17
|
abort@GLIBC_2.17
|
||||||
|
@@ -13,5 +13,7 @@ __ZN8weaselab11ConflictSetC2Ex
|
|||||||
__ZN8weaselab11ConflictSetD1Ev
|
__ZN8weaselab11ConflictSetD1Ev
|
||||||
__ZN8weaselab11ConflictSetD2Ev
|
__ZN8weaselab11ConflictSetD2Ev
|
||||||
__ZN8weaselab11ConflictSetaSEOS0_
|
__ZN8weaselab11ConflictSetaSEOS0_
|
||||||
|
__ZNK8weaselab11ConflictSet12getMetricsV1EPPNS0_9MetricsV1EPi
|
||||||
__ZNK8weaselab11ConflictSet5checkEPKNS0_9ReadRangeEPNS0_6ResultEi
|
__ZNK8weaselab11ConflictSet5checkEPKNS0_9ReadRangeEPNS0_6ResultEi
|
||||||
__ZNK8weaselab11ConflictSet8getBytesEv
|
__ZNK8weaselab11ConflictSet8getBytesEv
|
||||||
|
__ZNK8weaselab11ConflictSet9MetricsV18getValueEv
|
@@ -1,6 +1,7 @@
|
|||||||
#include "ConflictSet.h"
|
#include "ConflictSet.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
using namespace weaselab;
|
using namespace weaselab;
|
||||||
|
|
||||||
@@ -21,4 +22,14 @@ int main(void) {
|
|||||||
assert(result == ConflictSet::Conflict);
|
assert(result == ConflictSet::Conflict);
|
||||||
int64_t bytes = cs.getBytes();
|
int64_t bytes = cs.getBytes();
|
||||||
assert(bytes > 0);
|
assert(bytes > 0);
|
||||||
|
|
||||||
|
ConflictSet::MetricsV1 *metrics;
|
||||||
|
int metricsCount;
|
||||||
|
cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
|
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||||
|
printf("# TYPE %s %s\n", metrics[i].name,
|
||||||
|
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
||||||
|
printf("%s %g\n", metrics[i].name, metrics[i].getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user