Compare commits
22 Commits
v0.0.7
...
ed274c24d7
Author | SHA1 | Date | |
---|---|---|---|
ed274c24d7 | |||
cecfcc0da7 | |||
f6edde0e50 | |||
04ac41a7e7 | |||
354920f86f | |||
bfd02503e7 | |||
d0bd293f8d | |||
41e887c358 | |||
e394e3d96a | |||
3288c583e4 | |||
ef14003781 | |||
3ac16bc966 | |||
1e82f7fe22 | |||
4182d904c5 | |||
bd8ed4e7bd | |||
60cb274a15 | |||
687bc9c935 | |||
d50bb8bc80 | |||
f19b403f19 | |||
34cd210907 | |||
1a5da9e899 | |||
8ba9b04d8c |
23
Bench.cpp
23
Bench.cpp
@@ -358,7 +358,30 @@ void benchWorstCaseForRadixRangeRead() {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void benchMetrics() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
ConflictSet cs{0};
|
||||||
|
|
||||||
|
int count;
|
||||||
|
ConflictSet::MetricsV1 *m;
|
||||||
|
cs.getMetricsV1(&m, &count);
|
||||||
|
bench.batch(count);
|
||||||
|
bench.run("fetch metric", [&]() {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
m[i].getValue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void benchCreateAndDestroy() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
|
||||||
|
bench.run("create and destroy", [&]() { ConflictSet cs{0}; });
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
benchConflictSet();
|
benchConflictSet();
|
||||||
benchWorstCaseForRadixRangeRead();
|
benchWorstCaseForRadixRangeRead();
|
||||||
|
benchMetrics();
|
||||||
|
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.7
|
VERSION 0.0.9
|
||||||
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)
|
||||||
|
173
ConflictSet.cpp
173
ConflictSet.cpp
@@ -546,7 +546,50 @@ static_assert(kBytesPerKey - sizeof(Node0) >= kMinNodeSurplus);
|
|||||||
|
|
||||||
constexpr int64_t kFreeListMaxMemory = 1 << 20;
|
constexpr int64_t kFreeListMaxMemory = 1 << 20;
|
||||||
|
|
||||||
|
struct Metric {
|
||||||
|
Metric *prev;
|
||||||
|
const char *name;
|
||||||
|
const char *help;
|
||||||
|
ConflictSet::MetricsV1::Type type;
|
||||||
|
std::atomic<double> value;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Metric(ConflictSet::Impl *impl, const char *name, const char *help,
|
||||||
|
ConflictSet::MetricsV1::Type type);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Gauge : private Metric {
|
||||||
|
Gauge(ConflictSet::Impl *impl, const char *name, const char *help)
|
||||||
|
: Metric(impl, name, help, ConflictSet::MetricsV1::Gauge) {}
|
||||||
|
|
||||||
|
void set(double value) {
|
||||||
|
this->value.store(value, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Counter : private Metric {
|
||||||
|
Counter(ConflictSet::Impl *impl, const char *name, const char *help)
|
||||||
|
: Metric(impl, name, help, ConflictSet::MetricsV1::Counter) {}
|
||||||
|
// Expensive. Accumulate locally and then call add instead of repeatedly
|
||||||
|
// calling add.
|
||||||
|
void add(double value) {
|
||||||
|
assert(value >= 0);
|
||||||
|
static_assert(std::atomic<double>::is_always_lock_free);
|
||||||
|
double old = this->value.load(std::memory_order_relaxed);
|
||||||
|
for (;;) {
|
||||||
|
double newVal = old + value;
|
||||||
|
if (this->value.compare_exchange_weak(old, newVal,
|
||||||
|
std::memory_order_relaxed)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <class T> struct BoundedFreeListAllocator {
|
template <class T> struct BoundedFreeListAllocator {
|
||||||
|
Counter *allocated_total = nullptr;
|
||||||
|
Counter *released_total = nullptr;
|
||||||
|
|
||||||
static_assert(sizeof(T) >= sizeof(void *));
|
static_assert(sizeof(T) >= sizeof(void *));
|
||||||
static_assert(std::derived_from<T, Node>);
|
static_assert(std::derived_from<T, Node>);
|
||||||
static_assert(std::is_trivial_v<T>);
|
static_assert(std::is_trivial_v<T>);
|
||||||
@@ -580,6 +623,7 @@ template <class T> struct BoundedFreeListAllocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
T *allocate(int partialKeyCapacity) {
|
T *allocate(int partialKeyCapacity) {
|
||||||
|
allocated_total->add(1);
|
||||||
T *result = allocate_helper(partialKeyCapacity);
|
T *result = allocate_helper(partialKeyCapacity);
|
||||||
if constexpr (!std::is_same_v<T, Node0>) {
|
if constexpr (!std::is_same_v<T, Node0>) {
|
||||||
memset(result->children, 0, sizeof(result->children));
|
memset(result->children, 0, sizeof(result->children));
|
||||||
@@ -596,6 +640,7 @@ template <class T> struct BoundedFreeListAllocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void release(T *p) {
|
void release(T *p) {
|
||||||
|
released_total->add(1);
|
||||||
if (freeListBytes >= kFreeListMaxMemory) {
|
if (freeListBytes >= kFreeListMaxMemory) {
|
||||||
removeNode(p);
|
removeNode(p);
|
||||||
return safe_free(p, sizeof(T) + p->partialKeyCapacity);
|
return safe_free(p, sizeof(T) + p->partialKeyCapacity);
|
||||||
@@ -1670,11 +1715,17 @@ struct SearchStepWise {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
thread_local double accume_point_read = 0;
|
||||||
|
thread_local double accume_prefix_read = 0;
|
||||||
|
thread_local double accume_range_read = 0;
|
||||||
|
thread_local double accume_max_between = 0;
|
||||||
|
|
||||||
// Logically this is the same as performing firstGeq and then checking against
|
// Logically this is the same as performing firstGeq and then checking against
|
||||||
// point or range version according to cmp, but this version short circuits as
|
// point or range version according to cmp, but this version short circuits as
|
||||||
// soon as it can prove that there's no conflict.
|
// soon as it can prove that there's no conflict.
|
||||||
bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
||||||
InternalVersionT readVersion, ConflictSet::Impl *impl) {
|
InternalVersionT readVersion, ConflictSet::Impl *impl) {
|
||||||
|
++accume_point_read;
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
fprintf(stderr, "Check point read: %s\n", printable(key).c_str());
|
fprintf(stderr, "Check point read: %s\n", printable(key).c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -1752,6 +1803,7 @@ downLeftSpine:
|
|||||||
// short circuits as soon as it can prove that there's no conflict.
|
// short circuits as soon as it can prove that there's no conflict.
|
||||||
bool checkPrefixRead(Node *n, const std::span<const uint8_t> key,
|
bool checkPrefixRead(Node *n, const std::span<const uint8_t> key,
|
||||||
InternalVersionT readVersion, ConflictSet::Impl *impl) {
|
InternalVersionT readVersion, ConflictSet::Impl *impl) {
|
||||||
|
++accume_prefix_read;
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
fprintf(stderr, "Check prefix read: %s\n", printable(key).c_str());
|
fprintf(stderr, "Check prefix read: %s\n", printable(key).c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -1993,6 +2045,7 @@ scan16(const InternalVersionT *vs, int begin, int end,
|
|||||||
template <bool kAVX512>
|
template <bool kAVX512>
|
||||||
bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
||||||
InternalVersionT readVersion) {
|
InternalVersionT readVersion) {
|
||||||
|
++accume_max_between;
|
||||||
assume(-1 <= begin);
|
assume(-1 <= begin);
|
||||||
assume(begin <= 256);
|
assume(begin <= 256);
|
||||||
assume(-1 <= end);
|
assume(-1 <= end);
|
||||||
@@ -2590,6 +2643,7 @@ template <bool kAVX512>
|
|||||||
bool checkRangeReadImpl(Node *n, std::span<const uint8_t> begin,
|
bool checkRangeReadImpl(Node *n, std::span<const uint8_t> begin,
|
||||||
std::span<const uint8_t> end,
|
std::span<const uint8_t> end,
|
||||||
InternalVersionT readVersion, ConflictSet::Impl *impl) {
|
InternalVersionT readVersion, ConflictSet::Impl *impl) {
|
||||||
|
++accume_range_read;
|
||||||
int lcp = longestCommonPrefix(begin.data(), end.data(),
|
int lcp = longestCommonPrefix(begin.data(), end.data(),
|
||||||
std::min(begin.size(), end.size()));
|
std::min(begin.size(), end.size()));
|
||||||
if (lcp == int(begin.size()) && end.size() == begin.size() + 1 &&
|
if (lcp == int(begin.size()) && end.size() == begin.size() + 1 &&
|
||||||
@@ -3007,6 +3061,10 @@ Node *firstGeqPhysical(Node *n, const std::span<const uint8_t> key) {
|
|||||||
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||||
|
|
||||||
void check(const ReadRange *reads, Result *result, int count) {
|
void check(const ReadRange *reads, Result *result, int count) {
|
||||||
|
assert(accume_point_read == 0);
|
||||||
|
assert(accume_prefix_read == 0);
|
||||||
|
assert(accume_range_read == 0);
|
||||||
|
assert(accume_max_between == 0);
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const auto &r = reads[i];
|
const auto &r = reads[i];
|
||||||
auto begin = std::span<const uint8_t>(r.begin.p, r.begin.len);
|
auto begin = std::span<const uint8_t>(r.begin.p, r.begin.len);
|
||||||
@@ -3023,6 +3081,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
? Commit
|
? Commit
|
||||||
: Conflict;
|
: Conflict;
|
||||||
}
|
}
|
||||||
|
read_point_total.add(std::exchange(accume_point_read, 0));
|
||||||
|
read_prefix_total.add(std::exchange(accume_prefix_read, 0));
|
||||||
|
read_range_total.add(std::exchange(accume_range_read, 0));
|
||||||
|
read_range_node_total.add(std::exchange(accume_max_between, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void addWrites(const WriteRange *writes, int count, int64_t writeVersion) {
|
void addWrites(const WriteRange *writes, int count, int64_t writeVersion) {
|
||||||
@@ -3056,6 +3118,8 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memory_bytes.set(totalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spends up to `fuel` gc'ing, and returns its unused fuel. Reclaims memory
|
// Spends up to `fuel` gc'ing, and returns its unused fuel. Reclaims memory
|
||||||
@@ -3070,8 +3134,12 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
rootMaxVersion = std::max(rootMaxVersion, oldestVersion);
|
rootMaxVersion = std::max(rootMaxVersion, oldestVersion);
|
||||||
n = nextPhysical(n);
|
n = nextPhysical(n);
|
||||||
}
|
}
|
||||||
for (; fuel > 0 && n != nullptr; --fuel) {
|
for (; fuel > 0 && n != nullptr;) {
|
||||||
rezero(n, oldestVersion);
|
rezero(n, oldestVersion);
|
||||||
|
// The "make sure gc keeps up with writes" calculations assume that we're
|
||||||
|
// scanning key by key, not node by node. Make sure we only spend fuel
|
||||||
|
// when there's a logical entry.
|
||||||
|
fuel -= n->entryPresent;
|
||||||
if (n->entryPresent && std::max(n->entry.pointVersion,
|
if (n->entryPresent && std::max(n->entry.pointVersion,
|
||||||
n->entry.rangeVersion) <= oldestVersion) {
|
n->entry.rangeVersion) <= oldestVersion) {
|
||||||
// Any transaction n would have prevented from committing is
|
// Any transaction n would have prevented from committing is
|
||||||
@@ -3121,6 +3189,8 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
keyUpdates = gcScanStep(keyUpdates);
|
keyUpdates = gcScanStep(keyUpdates);
|
||||||
|
|
||||||
|
memory_bytes.set(totalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t getBytes() const { return totalBytes; }
|
int64_t getBytes() const { return totalBytes; }
|
||||||
@@ -3132,6 +3202,16 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
|
|
||||||
allocators.~NodeAllocators();
|
allocators.~NodeAllocators();
|
||||||
new (&allocators) NodeAllocators();
|
new (&allocators) NodeAllocators();
|
||||||
|
allocators.node0.allocated_total = &node0_allocated_total;
|
||||||
|
allocators.node3.allocated_total = &node3_allocated_total;
|
||||||
|
allocators.node16.allocated_total = &node16_allocated_total;
|
||||||
|
allocators.node48.allocated_total = &node48_allocated_total;
|
||||||
|
allocators.node256.allocated_total = &node256_allocated_total;
|
||||||
|
allocators.node0.released_total = &node0_released_total;
|
||||||
|
allocators.node3.released_total = &node3_released_total;
|
||||||
|
allocators.node16.released_total = &node16_released_total;
|
||||||
|
allocators.node48.released_total = &node48_released_total;
|
||||||
|
allocators.node256.released_total = &node256_released_total;
|
||||||
|
|
||||||
removalKeyArena = Arena{};
|
removalKeyArena = Arena{};
|
||||||
removalKey = {};
|
removalKey = {};
|
||||||
@@ -3156,8 +3236,14 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
// Intentionally not resetting totalBytes
|
// Intentionally not resetting totalBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit Impl(int64_t oldestVersion) { init(oldestVersion); }
|
explicit Impl(int64_t oldestVersion) {
|
||||||
~Impl() { destroyTree(root); }
|
init(oldestVersion);
|
||||||
|
initMetrics();
|
||||||
|
}
|
||||||
|
~Impl() {
|
||||||
|
destroyTree(root);
|
||||||
|
safe_free(metrics, metricsCount * sizeof(metrics[0]));
|
||||||
|
}
|
||||||
|
|
||||||
NodeAllocators allocators;
|
NodeAllocators allocators;
|
||||||
|
|
||||||
@@ -3173,8 +3259,71 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
int64_t oldestVersionAtGcBegin;
|
int64_t oldestVersionAtGcBegin;
|
||||||
int64_t newestVersionFullPrecision;
|
int64_t newestVersionFullPrecision;
|
||||||
int64_t totalBytes = 0;
|
int64_t totalBytes = 0;
|
||||||
|
|
||||||
|
MetricsV1 *metrics;
|
||||||
|
int metricsCount = 0;
|
||||||
|
void initMetrics() {
|
||||||
|
metrics = (MetricsV1 *)safe_malloc(metricsCount * sizeof(metrics[0]));
|
||||||
|
for (auto [i, m] = std::make_tuple(metricsCount - 1, metricList); i >= 0;
|
||||||
|
--i, m = m->prev) {
|
||||||
|
metrics[i].name = m->name;
|
||||||
|
metrics[i].help = m->help;
|
||||||
|
metrics[i].p = m;
|
||||||
|
metrics[i].type = m->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Metric *metricList = nullptr;
|
||||||
|
|
||||||
|
#define GAUGE(name, help) \
|
||||||
|
Gauge name { this, #name, help }
|
||||||
|
#define COUNTER(name, help) \
|
||||||
|
Counter name { this, #name, help }
|
||||||
|
// ==================== METRICS DEFINITIONS ====================
|
||||||
|
COUNTER(node0_allocated_total,
|
||||||
|
"Total number of nodes of type \"Node0\" that have been allocated");
|
||||||
|
COUNTER(node3_allocated_total,
|
||||||
|
"Total number of nodes of type \"Node3\" that have been allocated");
|
||||||
|
COUNTER(node16_allocated_total,
|
||||||
|
"Total number of nodes of type \"Node16\" that have been allocated");
|
||||||
|
COUNTER(node48_allocated_total,
|
||||||
|
"Total number of nodes of type \"Node48\" that have been allocated");
|
||||||
|
COUNTER(node256_allocated_total,
|
||||||
|
"Total number of nodes of type \"Node256\" that have been allocated");
|
||||||
|
COUNTER(node0_released_total,
|
||||||
|
"Total number of nodes of type \"Node0\" that have been released");
|
||||||
|
COUNTER(node3_released_total,
|
||||||
|
"Total number of nodes of type \"Node3\" that have been released");
|
||||||
|
COUNTER(node16_released_total,
|
||||||
|
"Total number of nodes of type \"Node16\" that have been released");
|
||||||
|
COUNTER(node48_released_total,
|
||||||
|
"Total number of nodes of type \"Node48\" that have been released");
|
||||||
|
COUNTER(node256_released_total,
|
||||||
|
"Total number of nodes of type \"Node256\" that have been released");
|
||||||
|
GAUGE(memory_bytes, "Total number of bytes in use.");
|
||||||
|
COUNTER(read_point_total, "Total number of point reads checked.");
|
||||||
|
COUNTER(read_prefix_total, "Total number of prefix reads checked.");
|
||||||
|
COUNTER(read_range_total, "Total number of range reads checked.");
|
||||||
|
COUNTER(read_range_node_total,
|
||||||
|
"Total number of range checks of individual nodes while "
|
||||||
|
"checking a range read.");
|
||||||
|
// ==================== END METRICS DEFINITIONS ====================
|
||||||
|
#undef GAUGE
|
||||||
|
#undef COUNTER
|
||||||
|
|
||||||
|
void getMetricsV1(MetricsV1 **metrics, int *count) {
|
||||||
|
*metrics = this->metrics;
|
||||||
|
*count = metricsCount;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Metric::Metric(ConflictSet::Impl *impl, const char *name, const char *help,
|
||||||
|
ConflictSet::MetricsV1::Type type)
|
||||||
|
: prev(std::exchange(impl->metricList, this)), name(name), help(help),
|
||||||
|
type(type), value(0) {
|
||||||
|
++impl->metricsCount;
|
||||||
|
}
|
||||||
|
|
||||||
InternalVersionT maxVersion(Node *n, ConflictSet::Impl *impl) {
|
InternalVersionT maxVersion(Node *n, ConflictSet::Impl *impl) {
|
||||||
int index = n->parentsIndex;
|
int index = n->parentsIndex;
|
||||||
n = n->parent;
|
n = n->parent;
|
||||||
@@ -3304,6 +3453,15 @@ void internal_destroy(ConflictSet::Impl *impl) {
|
|||||||
|
|
||||||
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->getBytes(); }
|
int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->getBytes(); }
|
||||||
|
|
||||||
|
void internal_getMetricsV1(ConflictSet::Impl *impl,
|
||||||
|
ConflictSet::MetricsV1 **metrics, int *count) {
|
||||||
|
impl->getMetricsV1(metrics, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
double internal_getMetricValue(const ConflictSet::MetricsV1 *metric) {
|
||||||
|
return ((Metric *)metric->p)->value.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== END IMPLEMENTATION ====================
|
// ==================== END IMPLEMENTATION ====================
|
||||||
|
|
||||||
// GCOVR_EXCL_START
|
// GCOVR_EXCL_START
|
||||||
@@ -3388,6 +3546,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)) {}
|
||||||
|
|
||||||
@@ -3443,6 +3609,7 @@ static_assert(std::is_standard_layout_v<ConflictSet::Result>);
|
|||||||
static_assert(std::is_standard_layout_v<ConflictSet::Key>);
|
static_assert(std::is_standard_layout_v<ConflictSet::Key>);
|
||||||
static_assert(std::is_standard_layout_v<ConflictSet::ReadRange>);
|
static_assert(std::is_standard_layout_v<ConflictSet::ReadRange>);
|
||||||
static_assert(std::is_standard_layout_v<ConflictSet::WriteRange>);
|
static_assert(std::is_standard_layout_v<ConflictSet::WriteRange>);
|
||||||
|
static_assert(std::is_standard_layout_v<ConflictSet::MetricsV1>);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
80
Internal.h
80
Internal.h
@@ -664,17 +664,74 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
fprintf(stderr, "Write @ %" PRId64 "\n", v);
|
fprintf(stderr, "Write @ %" PRId64 "\n", v);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Test non-canonical writes
|
||||||
|
if (numPointWrites > 0) {
|
||||||
|
int overlaps = arbitrary.bounded(numPointWrites);
|
||||||
|
for (int i = 0; i < numPointWrites + numRangeWrites && overlaps > 0;
|
||||||
|
++i) {
|
||||||
|
if (writes[i].end.len == 0) {
|
||||||
|
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
||||||
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
|
memset(begin, prefixByte, prefixLen);
|
||||||
|
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
|
writes[i].end.len = keyLen;
|
||||||
|
writes[i].end.p = begin;
|
||||||
|
auto c =
|
||||||
|
std::span<const uint8_t>(writes[i].begin.p,
|
||||||
|
writes[i].begin.len) <=>
|
||||||
|
std::span<const uint8_t>(writes[i].end.p, writes[i].end.len);
|
||||||
|
if (c > 0) {
|
||||||
|
using std::swap;
|
||||||
|
swap(writes[i].begin, writes[i].end);
|
||||||
|
} else if (c == 0) {
|
||||||
|
// It's a point write after all, I guess
|
||||||
|
writes[i].end.len = 0;
|
||||||
|
}
|
||||||
|
--overlaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arbitrary.bounded(2)) {
|
||||||
|
// Shuffle writes
|
||||||
|
for (int i = numPointWrites + numRangeWrites - 1; i > 0; --i) {
|
||||||
|
int j = arbitrary.bounded(i + 1);
|
||||||
|
if (i != j) {
|
||||||
|
using std::swap;
|
||||||
|
swap(writes[i], writes[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
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);
|
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
|
|
||||||
oldestVersion +=
|
|
||||||
arbitrary.bounded(10) ? arbitrary.bounded(10) : arbitrary.next();
|
|
||||||
oldestVersion = std::min(oldestVersion, writeVersion);
|
|
||||||
cs.setOldestVersion(oldestVersion);
|
cs.setOldestVersion(oldestVersion);
|
||||||
refImpl.setOldestVersion(oldestVersion);
|
refImpl.setOldestVersion(oldestVersion);
|
||||||
|
|
||||||
|
#ifdef THREAD_TEST
|
||||||
|
thread2.join();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int numPointReads = arbitrary.bounded(100);
|
int numPointReads = arbitrary.bounded(100);
|
||||||
@@ -747,7 +804,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
|
||||||
@@ -756,6 +821,15 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
cs.check(reads, results1, numPointReads + numRangeReads);
|
cs.check(reads, results1, numPointReads + numRangeReads);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
|
|
||||||
|
// 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);
|
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
||||||
|
|
||||||
auto compareResults = [reads](ConflictSet::Result *results1,
|
auto compareResults = [reads](ConflictSet::Result *results1,
|
||||||
|
24
Jenkinsfile
vendored
24
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 {
|
||||||
@@ -59,17 +70,6 @@ pipeline {
|
|||||||
CleanBuildAndTest("-DUSE_SIMD_FALLBACK=ON")
|
CleanBuildAndTest("-DUSE_SIMD_FALLBACK=ON")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('32-bit versions') {
|
|
||||||
agent {
|
|
||||||
dockerfile {
|
|
||||||
args '-v /home/jenkins/ccache:/ccache'
|
|
||||||
reuseNode true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
CleanBuildAndTest("-DUSE_32_BIT_VERSIONS=ON")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Release [gcc]') {
|
stage('Release [gcc]') {
|
||||||
agent {
|
agent {
|
||||||
dockerfile {
|
dockerfile {
|
||||||
@@ -117,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
|
||||||
'''
|
'''
|
||||||
|
32
README.md
32
README.md
@@ -60,27 +60,27 @@ Performance counters:
|
|||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark
|
| ns/op | op/s | err% | total | benchmark
|
||||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
|--------------------:|--------------------:|--------:|----------:|:----------
|
||||||
| 256.89 | 3,892,784.92 | 0.3% | 0.01 | `point reads`
|
| 245.99 | 4,065,232.81 | 0.3% | 0.01 | `point reads`
|
||||||
| 272.90 | 3,664,395.04 | 0.2% | 0.01 | `prefix reads`
|
| 265.93 | 3,760,430.49 | 0.2% | 0.01 | `prefix reads`
|
||||||
| 507.22 | 1,971,549.50 | 0.7% | 0.01 | `range reads`
|
| 485.30 | 2,060,569.50 | 0.2% | 0.01 | `range reads`
|
||||||
| 452.66 | 2,209,181.91 | 0.5% | 0.01 | `point writes`
|
| 449.60 | 2,224,195.17 | 0.4% | 0.01 | `point writes`
|
||||||
| 438.09 | 2,282,619.96 | 0.4% | 0.01 | `prefix writes`
|
| 441.76 | 2,263,688.18 | 1.1% | 0.01 | `prefix writes`
|
||||||
| 253.33 | 3,947,420.36 | 2.5% | 0.02 | `range writes`
|
| 245.42 | 4,074,647.54 | 2.4% | 0.02 | `range writes`
|
||||||
| 574.07 | 1,741,936.71 | 0.3% | 0.01 | `monotonic increasing point writes`
|
| 572.80 | 1,745,810.06 | 1.3% | 0.01 | `monotonic increasing point writes`
|
||||||
| 151,562.50 | 6,597.94 | 1.5% | 0.01 | `worst case for radix tree`
|
| 154,819.33 | 6,459.14 | 0.9% | 0.01 | `worst case for radix tree`
|
||||||
|
|
||||||
## Radix tree (this implementation)
|
## Radix tree (this implementation)
|
||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark
|
| ns/op | op/s | err% | total | benchmark
|
||||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
|--------------------:|--------------------:|--------:|----------:|:----------
|
||||||
| 19.83 | 50,420,955.28 | 0.1% | 0.01 | `point reads`
|
| 19.17 | 52,163,930.66 | 0.1% | 0.01 | `point reads`
|
||||||
| 55.95 | 17,872,542.40 | 0.5% | 0.01 | `prefix reads`
|
| 23.68 | 42,224,388.21 | 0.7% | 0.01 | `prefix reads`
|
||||||
| 88.28 | 11,327,709.50 | 0.4% | 0.01 | `range reads`
|
| 63.30 | 15,797,506.06 | 0.9% | 0.01 | `range reads`
|
||||||
| 29.15 | 34,309,531.64 | 0.5% | 0.01 | `point writes`
|
| 29.66 | 33,720,994.74 | 0.3% | 0.01 | `point writes`
|
||||||
| 42.36 | 23,607,424.27 | 1.1% | 0.01 | `prefix writes`
|
| 43.50 | 22,987,781.25 | 1.0% | 0.01 | `prefix writes`
|
||||||
| 50.00 | 20,000,000.00 | 0.0% | 0.01 | `range writes`
|
| 50.00 | 20,000,000.00 | 0.8% | 0.01 | `range writes`
|
||||||
| 93.52 | 10,692,413.79 | 3.3% | 0.01 | `monotonic increasing point writes`
|
| 103.25 | 9,684,786.47 | 2.9% | 0.01 | `monotonic increasing point writes`
|
||||||
| 2,388,417.00 | 418.69 | 0.4% | 0.03 | `worst case for radix tree`
|
| 1,181,500.00 | 846.38 | 2.3% | 0.01 | `worst case for radix tree`
|
||||||
|
|
||||||
# "Real data" test
|
# "Real data" test
|
||||||
|
|
||||||
|
@@ -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,
|
||||||
|
193
SkipList.cpp
193
SkipList.cpp
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#include "ConflictSet.h"
|
#include "ConflictSet.h"
|
||||||
#include "Internal.h"
|
#include "Internal.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
std::span<const uint8_t> keyAfter(Arena &arena, std::span<const uint8_t> key) {
|
std::span<const uint8_t> keyAfter(Arena &arena, std::span<const uint8_t> key) {
|
||||||
@@ -40,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 {
|
||||||
@@ -52,6 +54,135 @@ struct KeyRangeRef {
|
|||||||
: begin(begin), end(keyAfter(arena, begin)) {}
|
: begin(begin), end(keyAfter(arena, begin)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct KeyInfo {
|
||||||
|
StringRef key;
|
||||||
|
bool begin;
|
||||||
|
bool write;
|
||||||
|
|
||||||
|
KeyInfo() = default;
|
||||||
|
KeyInfo(StringRef key, bool begin, bool write)
|
||||||
|
: key(key), begin(begin), write(write) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
force_inline int extra_ordering(const KeyInfo &ki) {
|
||||||
|
return ki.begin * 2 + (ki.write ^ ki.begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if done with string
|
||||||
|
force_inline bool getCharacter(const KeyInfo &ki, int character,
|
||||||
|
int &outputCharacter) {
|
||||||
|
// normal case
|
||||||
|
if (character < ki.key.size()) {
|
||||||
|
outputCharacter = 5 + ki.key.begin()[character];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// termination
|
||||||
|
if (character == ki.key.size()) {
|
||||||
|
outputCharacter = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character == ki.key.size() + 1) {
|
||||||
|
// end/begin+read/write relative sorting
|
||||||
|
outputCharacter = extra_ordering(ki);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputCharacter = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const KeyInfo &lhs, const KeyInfo &rhs) {
|
||||||
|
int i = std::min(lhs.key.size(), rhs.key.size());
|
||||||
|
int c = memcmp(lhs.key.data(), rhs.key.data(), i);
|
||||||
|
if (c != 0)
|
||||||
|
return c < 0;
|
||||||
|
|
||||||
|
// Always sort shorter keys before longer keys.
|
||||||
|
if (lhs.key.size() < rhs.key.size()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lhs.key.size() > rhs.key.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the keys are the same length, use the extra ordering constraint.
|
||||||
|
return extra_ordering(lhs) < extra_ordering(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const KeyInfo &lhs, const KeyInfo &rhs) {
|
||||||
|
return !(lhs < rhs || rhs < lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swapSort(std::vector<KeyInfo> &points, int a, int b) {
|
||||||
|
if (points[b] < points[a]) {
|
||||||
|
KeyInfo temp;
|
||||||
|
temp = points[a];
|
||||||
|
points[a] = points[b];
|
||||||
|
points[b] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SortTask {
|
||||||
|
int begin;
|
||||||
|
int size;
|
||||||
|
int character;
|
||||||
|
SortTask(int begin, int size, int character)
|
||||||
|
: begin(begin), size(size), character(character) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void sortPoints(std::vector<KeyInfo> &points) {
|
||||||
|
std::vector<SortTask> tasks;
|
||||||
|
std::vector<KeyInfo> newPoints;
|
||||||
|
std::vector<int> counts;
|
||||||
|
|
||||||
|
tasks.emplace_back(0, points.size(), 0);
|
||||||
|
|
||||||
|
while (tasks.size()) {
|
||||||
|
SortTask st = tasks.back();
|
||||||
|
tasks.pop_back();
|
||||||
|
|
||||||
|
if (st.size < 10) {
|
||||||
|
std::sort(points.begin() + st.begin, points.begin() + st.begin + st.size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newPoints.resize(st.size);
|
||||||
|
counts.assign(256 + 5, 0);
|
||||||
|
|
||||||
|
// get counts
|
||||||
|
int c;
|
||||||
|
bool allDone = true;
|
||||||
|
for (int i = st.begin; i < st.begin + st.size; i++) {
|
||||||
|
allDone &= getCharacter(points[i], st.character, c);
|
||||||
|
counts[c]++;
|
||||||
|
}
|
||||||
|
if (allDone)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// calculate offsets from counts and build next level of tasks
|
||||||
|
int total = 0;
|
||||||
|
for (int i = 0; i < counts.size(); i++) {
|
||||||
|
int temp = counts[i];
|
||||||
|
if (temp > 1)
|
||||||
|
tasks.emplace_back(st.begin + total, temp, st.character + 1);
|
||||||
|
counts[i] = total;
|
||||||
|
total += temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put in their places
|
||||||
|
for (int i = st.begin; i < st.begin + st.size; i++) {
|
||||||
|
getCharacter(points[i], st.character, c);
|
||||||
|
newPoints[counts[c]++] = points[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy back into original points array
|
||||||
|
for (int i = 0; i < st.size; i++)
|
||||||
|
points[st.begin + i] = newPoints[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static thread_local uint32_t g_seed = 0;
|
static thread_local uint32_t g_seed = 0;
|
||||||
|
|
||||||
static inline int skfastrand() {
|
static inline int skfastrand() {
|
||||||
@@ -602,10 +733,40 @@ 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) {
|
int64_t writeVersion) {
|
||||||
|
auto points = std::vector<KeyInfo>(count * 2);
|
||||||
|
Arena arena;
|
||||||
|
|
||||||
|
for (int r = 0; r < count; r++) {
|
||||||
|
points.emplace_back(StringRef(writes[r].begin.p, writes[r].begin.len),
|
||||||
|
true, true);
|
||||||
|
points.emplace_back(
|
||||||
|
writes[r].end.len > 0
|
||||||
|
? StringRef{writes[r].end.p, size_t(writes[r].end.len)}
|
||||||
|
: keyAfter(arena, points.back().key),
|
||||||
|
false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
sortPoints(points);
|
||||||
|
|
||||||
|
int activeWriteCount = 0;
|
||||||
|
std::vector<std::pair<StringRef, StringRef>> combinedWriteConflictRanges;
|
||||||
|
for (const KeyInfo &point : points) {
|
||||||
|
if (point.write) {
|
||||||
|
if (point.begin) {
|
||||||
|
activeWriteCount++;
|
||||||
|
if (activeWriteCount == 1)
|
||||||
|
combinedWriteConflictRanges.emplace_back(point.key, StringRef());
|
||||||
|
} else /*if (point.end)*/ {
|
||||||
|
activeWriteCount--;
|
||||||
|
if (activeWriteCount == 0)
|
||||||
|
combinedWriteConflictRanges.back().second = point.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert(writeVersion >= newestVersion);
|
assert(writeVersion >= newestVersion);
|
||||||
newestVersion = writeVersion;
|
newestVersion = writeVersion;
|
||||||
Arena arena;
|
const int stringCount = combinedWriteConflictRanges.size() * 2;
|
||||||
const int stringCount = count * 2;
|
|
||||||
|
|
||||||
const int stripeSize = 16;
|
const int stripeSize = 16;
|
||||||
SkipList::Finger fingers[stripeSize];
|
SkipList::Finger fingers[stripeSize];
|
||||||
@@ -616,15 +777,13 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
int ss = stringCount - (stripes - 1) * stripeSize;
|
int ss = stringCount - (stripes - 1) * stripeSize;
|
||||||
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 = combinedWriteConflictRanges[s * stripeSize / 2 + i];
|
||||||
#if DEBUG_VERBOSE
|
#if DEBUG_VERBOSE
|
||||||
printf("Write begin: %s\n", printable(w.begin).c_str());
|
printf("Write begin: %s\n", printable(w.begin).c_str());
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
values[i * 2] = {w.begin.p, size_t(w.begin.len)};
|
values[i * 2] = w.first;
|
||||||
values[i * 2 + 1] = w.end.len > 0
|
values[i * 2 + 1] = w.second;
|
||||||
? StringRef{w.end.p, size_t(w.end.len)}
|
|
||||||
: keyAfter(arena, values[i * 2]);
|
|
||||||
keyUpdates += 3;
|
keyUpdates += 3;
|
||||||
}
|
}
|
||||||
skipList.find(values, fingers, temp, ss);
|
skipList.find(values, fingers, temp, ss);
|
||||||
@@ -702,6 +861,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);
|
||||||
@@ -718,6 +887,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)) {}
|
||||||
|
|
||||||
|
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
corpus/005e2b0059b0261bc2288a5843a31e098d31013b
Normal file
BIN
corpus/005e2b0059b0261bc2288a5843a31e098d31013b
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0164498b5d5fbc2a3a5979deec1a0446c0e1abb6
Normal file
BIN
corpus/0164498b5d5fbc2a3a5979deec1a0446c0e1abb6
Normal file
Binary file not shown.
BIN
corpus/01cab80c8efd804c267cc9242a12a4dac2959f98
Normal file
BIN
corpus/01cab80c8efd804c267cc9242a12a4dac2959f98
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/027cb1a49430f1677a7bb0510841e2078c69a40e
Normal file
BIN
corpus/027cb1a49430f1677a7bb0510841e2078c69a40e
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/037a852532d83bba4b2a366b1c2e88902ec43a62
Normal file
BIN
corpus/037a852532d83bba4b2a366b1c2e88902ec43a62
Normal file
Binary file not shown.
BIN
corpus/04cddf0d0e2f0466d26efa1595a76858bcde4c94
Normal file
BIN
corpus/04cddf0d0e2f0466d26efa1595a76858bcde4c94
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/076b27f409c8bda741fb719e5d10681e5ae1db31
Normal file
BIN
corpus/076b27f409c8bda741fb719e5d10681e5ae1db31
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/091f1883731c2f0ba9d3705b44bfcd6dd3bf88db
Normal file
BIN
corpus/091f1883731c2f0ba9d3705b44bfcd6dd3bf88db
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0ae06dc325d95c19967ac97b3de10c2fc8983b1b
Normal file
BIN
corpus/0ae06dc325d95c19967ac97b3de10c2fc8983b1b
Normal file
Binary file not shown.
BIN
corpus/0b626de7d1730e4a677757381713ba32ddbc943c
Normal file
BIN
corpus/0b626de7d1730e4a677757381713ba32ddbc943c
Normal file
Binary file not shown.
BIN
corpus/0b6558613333c201962d579fad084b280bd96aa7
Normal file
BIN
corpus/0b6558613333c201962d579fad084b280bd96aa7
Normal file
Binary file not shown.
BIN
corpus/0b82dea314f067dc8fd7b52459c1b855c784fde4
Normal file
BIN
corpus/0b82dea314f067dc8fd7b52459c1b855c784fde4
Normal file
Binary file not shown.
BIN
corpus/0bc38a2aff322bfcf5ca402f996ba12f8daf31d1
Normal file
BIN
corpus/0bc38a2aff322bfcf5ca402f996ba12f8daf31d1
Normal file
Binary file not shown.
BIN
corpus/0cabceb6692bc5ad27cffc1f00d412e6c1b0afc0
Normal file
BIN
corpus/0cabceb6692bc5ad27cffc1f00d412e6c1b0afc0
Normal file
Binary file not shown.
BIN
corpus/0cb1c7a2c5ad6f089cd3b2d48c658974aa338b2c
Normal file
BIN
corpus/0cb1c7a2c5ad6f089cd3b2d48c658974aa338b2c
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0da6dc7ca616a5de655f860192079e4859382371
Normal file
BIN
corpus/0da6dc7ca616a5de655f860192079e4859382371
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
corpus/0ef85a238153205a34565c63f4ad6c373a90b73e
Normal file
BIN
corpus/0ef85a238153205a34565c63f4ad6c373a90b73e
Normal file
Binary file not shown.
BIN
corpus/0f045e5e1a36ee449803f31d0ec334fb1218cc33
Normal file
BIN
corpus/0f045e5e1a36ee449803f31d0ec334fb1218cc33
Normal file
Binary file not shown.
BIN
corpus/0f2e401e0fe0e1d6267142355cc156ee7d2c3c87
Normal file
BIN
corpus/0f2e401e0fe0e1d6267142355cc156ee7d2c3c87
Normal file
Binary file not shown.
BIN
corpus/0fc692696f94afbb5d2027bc12fa3dc4a19ac3a9
Normal file
BIN
corpus/0fc692696f94afbb5d2027bc12fa3dc4a19ac3a9
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/113ddcf047b2f8c3684853b0f086ead6f056fac2
Normal file
BIN
corpus/113ddcf047b2f8c3684853b0f086ead6f056fac2
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/11510f8502ab47f7d57cc205f3d6af50f36eb98c
Normal file
BIN
corpus/11510f8502ab47f7d57cc205f3d6af50f36eb98c
Normal file
Binary file not shown.
BIN
corpus/11919ca53b7efc88c8501dddde8fad916197f54c
Normal file
BIN
corpus/11919ca53b7efc88c8501dddde8fad916197f54c
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/12a815319620aa136dd77ceb9f6389fd74765f8d
Normal file
BIN
corpus/12a815319620aa136dd77ceb9f6389fd74765f8d
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/133c0de112bdcb419b188a1fe8574941517764e8
Normal file
BIN
corpus/133c0de112bdcb419b188a1fe8574941517764e8
Normal file
Binary file not shown.
BIN
corpus/135debe2b0cde6a9d63eb07116d7c85c8dbcc268
Normal file
BIN
corpus/135debe2b0cde6a9d63eb07116d7c85c8dbcc268
Normal file
Binary file not shown.
BIN
corpus/14ba3bc0137f0791498781c534a4a81c41e5f565
Normal file
BIN
corpus/14ba3bc0137f0791498781c534a4a81c41e5f565
Normal file
Binary file not shown.
BIN
corpus/1534c7f0f2bac8015e9df650deb62fe383c58440
Normal file
BIN
corpus/1534c7f0f2bac8015e9df650deb62fe383c58440
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1681caabc3d5e8d79b4dd79a465a3678bc6ab498
Normal file
BIN
corpus/1681caabc3d5e8d79b4dd79a465a3678bc6ab498
Normal file
Binary file not shown.
BIN
corpus/16da9b15792f7c488d3d9f8354199cc512cd012f
Normal file
BIN
corpus/16da9b15792f7c488d3d9f8354199cc512cd012f
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/17f44a42bb6e083988b3eb00ee6f14dccc10bcbf
Normal file
BIN
corpus/17f44a42bb6e083988b3eb00ee6f14dccc10bcbf
Normal file
Binary file not shown.
BIN
corpus/180e912ab1eee5ccaa1cab79c9fd4d81d2009b6a
Normal file
BIN
corpus/180e912ab1eee5ccaa1cab79c9fd4d81d2009b6a
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1906d18823ade0beb690c68d8585dfb3b565956f
Normal file
BIN
corpus/1906d18823ade0beb690c68d8585dfb3b565956f
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/199d8d206d74e0e77ac21334cfe398a3885d84a2
Normal file
BIN
corpus/199d8d206d74e0e77ac21334cfe398a3885d84a2
Normal file
Binary file not shown.
BIN
corpus/1ae168891529281e4d601dbbe83a064fb330e722
Normal file
BIN
corpus/1ae168891529281e4d601dbbe83a064fb330e722
Normal file
Binary file not shown.
BIN
corpus/1af2516afd85fafa8a6c40dae32059f8b60536b0
Normal file
BIN
corpus/1af2516afd85fafa8a6c40dae32059f8b60536b0
Normal file
Binary file not shown.
BIN
corpus/1af26a82f432e06da3d093435c1baa2251955ee7
Normal file
BIN
corpus/1af26a82f432e06da3d093435c1baa2251955ee7
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1c2b4d5da82dc79063550e5cf0c15b867098b47c
Normal file
BIN
corpus/1c2b4d5da82dc79063550e5cf0c15b867098b47c
Normal file
Binary file not shown.
BIN
corpus/1cbd253ecbe54b30051695216915f273716fb7eb
Normal file
BIN
corpus/1cbd253ecbe54b30051695216915f273716fb7eb
Normal file
Binary file not shown.
BIN
corpus/1d4c3da489e29554871120e2f9eb1227c92e9737
Normal file
BIN
corpus/1d4c3da489e29554871120e2f9eb1227c92e9737
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1f03804706499daec8988095a00faa51cffa139f
Normal file
BIN
corpus/1f03804706499daec8988095a00faa51cffa139f
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/1f3e8fba12eab3f4019d41a4818bf20599af0f20
Normal file
BIN
corpus/1f3e8fba12eab3f4019d41a4818bf20599af0f20
Normal file
Binary file not shown.
BIN
corpus/2002d20caa25f4a189811044fc8605ded3b8e7de
Normal file
BIN
corpus/2002d20caa25f4a189811044fc8605ded3b8e7de
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/2170d4c6e441dff70a24d5dc3de7229afc2a0450
Normal file
BIN
corpus/2170d4c6e441dff70a24d5dc3de7229afc2a0450
Normal file
Binary file not shown.
BIN
corpus/21dc5306743b5c81261800b3a8bb65e9a42c0738
Normal file
BIN
corpus/21dc5306743b5c81261800b3a8bb65e9a42c0738
Normal file
Binary file not shown.
BIN
corpus/221318f0be1a1ad5ada43568dcc677405ade50fd
Normal file
BIN
corpus/221318f0be1a1ad5ada43568dcc677405ade50fd
Normal file
Binary file not shown.
BIN
corpus/222a983c4f1ab6b78288e51da692baecbc662928
Normal file
BIN
corpus/222a983c4f1ab6b78288e51da692baecbc662928
Normal file
Binary file not shown.
BIN
corpus/2235efbdee0d8983c7a37e67e02b514d223bab70
Normal file
BIN
corpus/2235efbdee0d8983c7a37e67e02b514d223bab70
Normal file
Binary file not shown.
BIN
corpus/22b32d6fd448f613abf904370dc69bd962cf547f
Normal file
BIN
corpus/22b32d6fd448f613abf904370dc69bd962cf547f
Normal file
Binary file not shown.
BIN
corpus/22c0cdb7ca97288ede20571b4a2fa3582090f541
Normal file
BIN
corpus/22c0cdb7ca97288ede20571b4a2fa3582090f541
Normal file
Binary file not shown.
@@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
|
|
BIN
corpus/234e754ea806b2caa38c95ca634b5c29db721559
Normal file
BIN
corpus/234e754ea806b2caa38c95ca634b5c29db721559
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/24c2be04754d36985bbda27392a7dbba7db93cb4
Normal file
BIN
corpus/24c2be04754d36985bbda27392a7dbba7db93cb4
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