forked from weaselab/conflict-set
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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) {
|
||||
benchConflictSet();
|
||||
benchWorstCaseForRadixRangeRead();
|
||||
benchCreateAndDestroy();
|
||||
}
|
||||
|
||||
+9
-2
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(
|
||||
conflict-set
|
||||
VERSION 0.0.8
|
||||
VERSION 0.0.9
|
||||
DESCRIPTION
|
||||
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
||||
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
||||
@@ -60,6 +60,8 @@ cmake_pop_check_state()
|
||||
option(USE_SIMD_FALLBACK
|
||||
"Use fallback implementations of functions that use SIMD" OFF)
|
||||
|
||||
option(DISABLE_TSAN "Disable TSAN" OFF)
|
||||
|
||||
# This is encouraged according to
|
||||
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
||||
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()
|
||||
|
||||
# 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)
|
||||
target_compile_options(tsan_driver PRIVATE ${TEST_FLAGS} -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)
|
||||
target_link_libraries(real_data_bench PRIVATE ${PROJECT_NAME})
|
||||
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()
|
||||
|
||||
# packaging
|
||||
|
||||
+421
-114
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; }
|
||||
|
||||
void ConflictSet::getMetricsV1(MetricsV1 **metrics, int *count) const {
|
||||
*metrics = nullptr;
|
||||
*count = 0;
|
||||
}
|
||||
|
||||
double ConflictSet::MetricsV1::getValue() const { return 0; }
|
||||
|
||||
ConflictSet::ConflictSet(int64_t oldestVersion)
|
||||
: impl(new(safe_malloc(sizeof(Impl))) Impl{oldestVersion}) {}
|
||||
|
||||
|
||||
+56
-10
@@ -578,7 +578,8 @@ inline const char *resultToStr(ConflictSet::Result r) {
|
||||
|
||||
namespace {
|
||||
|
||||
template <class ConflictSetImpl> struct TestDriver {
|
||||
template <class ConflictSetImpl, bool kEnableAssertions = true>
|
||||
struct TestDriver {
|
||||
Arbitrary arbitrary;
|
||||
explicit TestDriver(const uint8_t *data, size_t size)
|
||||
: arbitrary({data, size}) {}
|
||||
@@ -702,17 +703,40 @@ 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
|
||||
|
||||
CALLGRIND_START_INSTRUMENTATION;
|
||||
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||
CALLGRIND_STOP_INSTRUMENTATION;
|
||||
|
||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||
if constexpr (kEnableAssertions) {
|
||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||
}
|
||||
|
||||
oldestVersion +=
|
||||
arbitrary.bounded(10) ? arbitrary.bounded(10) : arbitrary.next();
|
||||
oldestVersion = std::min(oldestVersion, writeVersion);
|
||||
cs.setOldestVersion(oldestVersion);
|
||||
refImpl.setOldestVersion(oldestVersion);
|
||||
if constexpr (kEnableAssertions) {
|
||||
refImpl.setOldestVersion(oldestVersion);
|
||||
}
|
||||
|
||||
#ifdef THREAD_TEST
|
||||
thread2.join();
|
||||
#endif
|
||||
}
|
||||
{
|
||||
int numPointReads = arbitrary.bounded(100);
|
||||
@@ -785,7 +809,15 @@ template <class ConflictSetImpl> struct TestDriver {
|
||||
std::latch ready{1};
|
||||
std::thread thread2{[&]() {
|
||||
ready.count_down();
|
||||
// Call all const methods
|
||||
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();
|
||||
#endif
|
||||
@@ -794,7 +826,18 @@ template <class ConflictSetImpl> struct TestDriver {
|
||||
cs.check(reads, results1, numPointReads + numRangeReads);
|
||||
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();
|
||||
}
|
||||
|
||||
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
||||
}
|
||||
|
||||
auto compareResults = [reads](ConflictSet::Result *results1,
|
||||
ConflictSet::Result *results2, int count) {
|
||||
@@ -821,9 +864,12 @@ template <class ConflictSetImpl> struct TestDriver {
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!compareResults(results1, results2, numPointReads + numRangeReads)) {
|
||||
ok = false;
|
||||
return true;
|
||||
if constexpr (kEnableAssertions) {
|
||||
if (!compareResults(results1, results2,
|
||||
numPointReads + numRangeReads)) {
|
||||
ok = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef THREAD_TEST
|
||||
|
||||
Vendored
+12
-1
@@ -48,6 +48,17 @@ pipeline {
|
||||
recordIssues(tools: [clang()])
|
||||
}
|
||||
}
|
||||
stage('Debug') {
|
||||
agent {
|
||||
dockerfile {
|
||||
args '-v /home/jenkins/ccache:/ccache'
|
||||
reuseNode true
|
||||
}
|
||||
}
|
||||
steps {
|
||||
CleanBuildAndTest("-DCMAKE_BUILD_TYPE=Debug")
|
||||
}
|
||||
}
|
||||
stage('SIMD fallback') {
|
||||
agent {
|
||||
dockerfile {
|
||||
@@ -106,7 +117,7 @@ pipeline {
|
||||
}
|
||||
}
|
||||
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 '''
|
||||
gcovr -f ConflictSet.cpp --cobertura > build/coverage.xml
|
||||
'''
|
||||
|
||||
@@ -129,6 +129,16 @@ int main(int argc, const char **argv) {
|
||||
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: "
|
||||
"%g%%, Peak idle memory: %g\n",
|
||||
checkTime, checkBytes / checkTime * 1e-6, addTime,
|
||||
|
||||
+19
-1
@@ -42,7 +42,7 @@ std::span<const uint8_t> copyToArena(Arena &arena,
|
||||
}
|
||||
|
||||
using Version = int64_t;
|
||||
#define force_inline __attribute__((always_inline))
|
||||
#define force_inline inline __attribute__((always_inline))
|
||||
using StringRef = std::span<const uint8_t>;
|
||||
|
||||
struct KeyRangeRef {
|
||||
@@ -861,6 +861,16 @@ void internal_destroy(ConflictSet::Impl *impl) {
|
||||
|
||||
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,
|
||||
int count) const {
|
||||
internal_check(impl, reads, results, count);
|
||||
@@ -877,6 +887,14 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
||||
|
||||
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)
|
||||
: impl(internal_create(oldestVersion)) {}
|
||||
|
||||
|
||||
+19
-1
@@ -3,17 +3,35 @@
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#ifndef PERF_TEST
|
||||
#define PERF_TEST 0
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::ifstream t(argv[i], std::ios::binary);
|
||||
std::stringstream buffer;
|
||||
buffer << t.rdbuf();
|
||||
auto str = buffer.str();
|
||||
TestDriver<ConflictSet> driver{(const uint8_t *)str.data(), str.size()};
|
||||
TestDriver<ConflictSet, !PERF_TEST> driver{(const uint8_t *)str.data(),
|
||||
str.size()};
|
||||
while (!driver.next())
|
||||
;
|
||||
if (!driver.ok) {
|
||||
abort();
|
||||
}
|
||||
|
||||
ConflictSet::MetricsV1 *metrics;
|
||||
int metricsCount;
|
||||
driver.cs.getMetricsV1(&metrics, &metricsCount);
|
||||
printf("#################### METRICS 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_guard@GLIBC_2.17
|
||||
abort@GLIBC_2.17
|
||||
|
||||
@@ -13,5 +13,7 @@ __ZN8weaselab11ConflictSetC2Ex
|
||||
__ZN8weaselab11ConflictSetD1Ev
|
||||
__ZN8weaselab11ConflictSetD2Ev
|
||||
__ZN8weaselab11ConflictSetaSEOS0_
|
||||
__ZNK8weaselab11ConflictSet12getMetricsV1EPPNS0_9MetricsV1EPi
|
||||
__ZNK8weaselab11ConflictSet5checkEPKNS0_9ReadRangeEPNS0_6ResultEi
|
||||
__ZNK8weaselab11ConflictSet8getBytesEv
|
||||
__ZNK8weaselab11ConflictSet9MetricsV18getValueEv
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ConflictSet.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
using namespace weaselab;
|
||||
|
||||
@@ -21,4 +22,14 @@ int main(void) {
|
||||
assert(result == ConflictSet::Conflict);
|
||||
int64_t bytes = cs.getBytes();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,35 @@ struct __attribute__((__visibility__("default"))) ConflictSet {
|
||||
/** Returns the total bytes in use by this ConflictSet */
|
||||
int64_t getBytes() const;
|
||||
|
||||
/** Experimental! */
|
||||
struct MetricsV1 {
|
||||
/** A null-terminated string with static lifetime. Identifies this metric.
|
||||
* Matches the regex [a-zA-Z][a-zA-Z0-9_]*
|
||||
*/
|
||||
const char *name;
|
||||
/** A null-terminated string with static lifetime. Describes this metric.
|
||||
* May contain any sequence of UTF-8 characters, but the backslash and the
|
||||
* line feed characters are escaped as \\ and \n, respectively.
|
||||
*/
|
||||
const char *help;
|
||||
/** Counters are >= 0 and non-decreasing. Gauges are any value. */
|
||||
enum Type { Counter, Gauge } type;
|
||||
/** Get the most up-to-date (best effort) value for this metric.
|
||||
* Thread-safe. */
|
||||
double getValue() const;
|
||||
/** @private */
|
||||
void *p;
|
||||
};
|
||||
|
||||
/** Experimental! Store a pointer to an array of MetricsV1 (owned by the
|
||||
* ConflictSet) to `*metrics`, and its length to `*count`. This function makes
|
||||
* no guarantees about the contents of the metrics (e.g. names, help text, and
|
||||
* the meaning of values). A correct implementation is free to return nonsense
|
||||
* or nothing at all. Not intended to be inspected programmatically. Only
|
||||
* intended to be plumbed along to e.g. Prometheus. Callers may repeatedly
|
||||
* call `getValue` on the metrics they're interested in. */
|
||||
void getMetricsV1(MetricsV1 **metrics, int *count) const;
|
||||
|
||||
#if __cplusplus > 199711L
|
||||
ConflictSet(ConflictSet &&) noexcept;
|
||||
ConflictSet &operator=(ConflictSet &&) noexcept;
|
||||
|
||||
+5
-3
@@ -6,12 +6,14 @@ ConflictSet_getBytes
|
||||
ConflictSet_setOldestVersion
|
||||
_ZN8weaselab11ConflictSet16setOldestVersionEl
|
||||
_ZN8weaselab11ConflictSet9addWritesEPKNS0_10WriteRangeEil
|
||||
_ZN8weaselab11ConflictSetaSEOS0_
|
||||
_ZN8weaselab11ConflictSetC1El
|
||||
_ZN8weaselab11ConflictSetC1EOS0_
|
||||
_ZN8weaselab11ConflictSetC2El
|
||||
_ZN8weaselab11ConflictSetC1El
|
||||
_ZN8weaselab11ConflictSetC2EOS0_
|
||||
_ZN8weaselab11ConflictSetC2El
|
||||
_ZN8weaselab11ConflictSetD1Ev
|
||||
_ZN8weaselab11ConflictSetD2Ev
|
||||
_ZN8weaselab11ConflictSetaSEOS0_
|
||||
_ZNK8weaselab11ConflictSet12getMetricsV1EPPNS0_9MetricsV1EPi
|
||||
_ZNK8weaselab11ConflictSet5checkEPKNS0_9ReadRangeEPNS0_6ResultEi
|
||||
_ZNK8weaselab11ConflictSet8getBytesEv
|
||||
_ZNK8weaselab11ConflictSet9MetricsV18getValueEv
|
||||
+1
-1
@@ -8,4 +8,4 @@ free@GLIBC_2.2.5
|
||||
malloc@GLIBC_2.2.5
|
||||
memcpy@GLIBC_2.14
|
||||
memmove@GLIBC_2.2.5
|
||||
memset@GLIBC_2.2.5
|
||||
memset@GLIBC_2.2.5
|
||||
@@ -56,6 +56,20 @@ def test_conflict_set():
|
||||
assert cs.check(read(0, key), read(1, key)) == [Result.TOO_OLD, Result.COMMIT]
|
||||
|
||||
|
||||
def test_update_zero():
|
||||
with DebugConflictSet() as cs1:
|
||||
with DebugConflictSet() as cs2:
|
||||
cs1.addWrites(2, write(b""))
|
||||
cs1.setOldestVersion(2)
|
||||
# "zero" is now 2
|
||||
|
||||
# make a Node48
|
||||
for i in range(256 - 17, 256):
|
||||
cs2.addWrites(int(1), write(bytes([i])))
|
||||
# Scan until first point write
|
||||
cs2.check(read(0, b"\x00", bytes([256 - 17])))
|
||||
|
||||
|
||||
def test_inner_full_words():
|
||||
with DebugConflictSet() as cs:
|
||||
cs.addWrites(1, write(b"\x3f\x61"), write(b"\x81\x61"))
|
||||
|
||||
Reference in New Issue
Block a user