Compare commits
31 Commits
493a6572ad
...
v0.0.11
Author | SHA1 | Date | |
---|---|---|---|
84c6a2bfc2 | |||
b5772a6aa0 | |||
e6c39981b9 | |||
c20c08f112 | |||
ac98d4a443 | |||
1d9e8ab68b | |||
7d86beb14c | |||
2fa954ed36 | |||
ded6e7fc2c | |||
781ba15cae | |||
9b56a74b2f | |||
6da9cbdec9 | |||
29c05187fb | |||
d89028dd2f | |||
09cf807747 | |||
051eb5919d | |||
ed5589e4ed | |||
a7b3d8fe4c | |||
c3a047fdf8 | |||
b4b469a175 | |||
0201e27498 | |||
2010920a2c | |||
19af8da65c | |||
80785e3c3b | |||
4580ee44b4 | |||
2d3985ca40 | |||
c8be68db40 | |||
f5d021d6b6 | |||
1c41605b53 | |||
8f03a105bb | |||
0e574856be |
@@ -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.10
|
VERSION 0.0.11
|
||||||
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"
|
||||||
@@ -31,14 +31,12 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|||||||
"MinSizeRel" "RelWithDebInfo")
|
"MinSizeRel" "RelWithDebInfo")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_compile_options(
|
add_compile_options(-fdata-sections -ffunction-sections -Wswitch-enum
|
||||||
-fdata-sections
|
-Werror=switch-enum -fPIC)
|
||||||
-ffunction-sections
|
if(NOT APPLE)
|
||||||
-Wswitch-enum
|
# This causes some versions of clang to crash on macos
|
||||||
-Werror=switch-enum
|
add_compile_options(-g -fno-omit-frame-pointer)
|
||||||
-fPIC
|
endif()
|
||||||
-g
|
|
||||||
-fno-omit-frame-pointer)
|
|
||||||
|
|
||||||
set(full_relro_flags "-pie;LINKER:-z,relro,-z,now,-z,noexecstack")
|
set(full_relro_flags "-pie;LINKER:-z,relro,-z,now,-z,noexecstack")
|
||||||
cmake_push_check_state()
|
cmake_push_check_state()
|
||||||
@@ -342,6 +340,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
add_executable(driver_perf TestDriver.cpp)
|
add_executable(driver_perf TestDriver.cpp)
|
||||||
target_compile_definitions(driver_perf PRIVATE PERF_TEST=1)
|
target_compile_definitions(driver_perf PRIVATE PERF_TEST=1)
|
||||||
target_link_libraries(driver_perf PRIVATE ${PROJECT_NAME})
|
target_link_libraries(driver_perf PRIVATE ${PROJECT_NAME})
|
||||||
|
|
||||||
|
# server bench
|
||||||
|
add_executable(server_bench ServerBench.cpp)
|
||||||
|
target_link_libraries(server_bench PRIVATE ${PROJECT_NAME})
|
||||||
|
set_target_properties(server_bench PROPERTIES SKIP_BUILD_RPATH ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# packaging
|
# packaging
|
||||||
|
565
ConflictSet.cpp
565
ConflictSet.cpp
@@ -902,6 +902,7 @@ Node *&getChildExists(Node *self, uint8_t index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
InternalVersionT maxVersion(Node *n, ConflictSet::Impl *);
|
InternalVersionT maxVersion(Node *n, ConflictSet::Impl *);
|
||||||
|
InternalVersionT exchangeMaxVersion(Node *n, InternalVersionT newMax);
|
||||||
|
|
||||||
void setMaxVersion(Node *n, ConflictSet::Impl *, InternalVersionT maxVersion);
|
void setMaxVersion(Node *n, ConflictSet::Impl *, InternalVersionT maxVersion);
|
||||||
|
|
||||||
@@ -939,6 +940,54 @@ Node *getChild(Node *self, uint8_t index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ChildAndMaxVersion {
|
||||||
|
Node *child;
|
||||||
|
InternalVersionT maxVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
ChildAndMaxVersion getChildAndMaxVersion(Node0 *, uint8_t) { return {}; }
|
||||||
|
ChildAndMaxVersion getChildAndMaxVersion(Node3 *self, uint8_t index) {
|
||||||
|
int i = getNodeIndex(self, index);
|
||||||
|
if (i < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {self->children[i], self->childMaxVersion[i]};
|
||||||
|
}
|
||||||
|
ChildAndMaxVersion getChildAndMaxVersion(Node16 *self, uint8_t index) {
|
||||||
|
int i = getNodeIndex(self, index);
|
||||||
|
if (i < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {self->children[i], self->childMaxVersion[i]};
|
||||||
|
}
|
||||||
|
ChildAndMaxVersion getChildAndMaxVersion(Node48 *self, uint8_t index) {
|
||||||
|
int i = self->index[index];
|
||||||
|
if (i < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {self->children[i], self->childMaxVersion[i]};
|
||||||
|
}
|
||||||
|
ChildAndMaxVersion getChildAndMaxVersion(Node256 *self, uint8_t index) {
|
||||||
|
return {self->children[index], self->childMaxVersion[index]};
|
||||||
|
}
|
||||||
|
|
||||||
|
ChildAndMaxVersion getChildAndMaxVersion(Node *self, uint8_t index) {
|
||||||
|
switch (self->getType()) {
|
||||||
|
case Type_Node0:
|
||||||
|
return getChildAndMaxVersion(static_cast<Node0 *>(self), index);
|
||||||
|
case Type_Node3:
|
||||||
|
return getChildAndMaxVersion(static_cast<Node3 *>(self), index);
|
||||||
|
case Type_Node16:
|
||||||
|
return getChildAndMaxVersion(static_cast<Node16 *>(self), index);
|
||||||
|
case Type_Node48:
|
||||||
|
return getChildAndMaxVersion(static_cast<Node48 *>(self), index);
|
||||||
|
case Type_Node256:
|
||||||
|
return getChildAndMaxVersion(static_cast<Node256 *>(self), index);
|
||||||
|
default: // GCOVR_EXCL_LINE
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <class NodeT> Node *getChildGeqSimd(NodeT *self, int child) {
|
template <class NodeT> Node *getChildGeqSimd(NodeT *self, int child) {
|
||||||
static_assert(std::is_same_v<NodeT, Node3> || std::is_same_v<NodeT, Node16>);
|
static_assert(std::is_same_v<NodeT, Node3> || std::is_same_v<NodeT, Node16>);
|
||||||
|
|
||||||
@@ -1075,8 +1124,10 @@ Node *getFirstChildExists(Node *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Caller is responsible for assigning a non-null pointer to the returned
|
// Caller is responsible for assigning a non-null pointer to the returned
|
||||||
// reference if null
|
// reference if null. Updates child's max version to `newMaxVersion` if child
|
||||||
Node *&getOrCreateChild(Node *&self, uint8_t index, WriteContext *tls) {
|
// exists but does not have a partial key.
|
||||||
|
Node *&getOrCreateChild(Node *&self, uint8_t index,
|
||||||
|
InternalVersionT newMaxVersion, WriteContext *tls) {
|
||||||
|
|
||||||
// Fast path for if it exists already
|
// Fast path for if it exists already
|
||||||
switch (self->getType()) {
|
switch (self->getType()) {
|
||||||
@@ -1086,6 +1137,9 @@ Node *&getOrCreateChild(Node *&self, uint8_t index, WriteContext *tls) {
|
|||||||
auto *self3 = static_cast<Node3 *>(self);
|
auto *self3 = static_cast<Node3 *>(self);
|
||||||
int i = getNodeIndex(self3, index);
|
int i = getNodeIndex(self3, index);
|
||||||
if (i >= 0) {
|
if (i >= 0) {
|
||||||
|
if (self3->children[i]->partialKeyLen == 0) {
|
||||||
|
self3->childMaxVersion[i] = newMaxVersion;
|
||||||
|
}
|
||||||
return self3->children[i];
|
return self3->children[i];
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -1093,6 +1147,9 @@ Node *&getOrCreateChild(Node *&self, uint8_t index, WriteContext *tls) {
|
|||||||
auto *self16 = static_cast<Node16 *>(self);
|
auto *self16 = static_cast<Node16 *>(self);
|
||||||
int i = getNodeIndex(self16, index);
|
int i = getNodeIndex(self16, index);
|
||||||
if (i >= 0) {
|
if (i >= 0) {
|
||||||
|
if (self16->children[i]->partialKeyLen == 0) {
|
||||||
|
self16->childMaxVersion[i] = newMaxVersion;
|
||||||
|
}
|
||||||
return self16->children[i];
|
return self16->children[i];
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -1100,12 +1157,23 @@ Node *&getOrCreateChild(Node *&self, uint8_t index, WriteContext *tls) {
|
|||||||
auto *self48 = static_cast<Node48 *>(self);
|
auto *self48 = static_cast<Node48 *>(self);
|
||||||
int secondIndex = self48->index[index];
|
int secondIndex = self48->index[index];
|
||||||
if (secondIndex >= 0) {
|
if (secondIndex >= 0) {
|
||||||
|
if (self48->children[secondIndex]->partialKeyLen == 0) {
|
||||||
|
self48->childMaxVersion[secondIndex] = newMaxVersion;
|
||||||
|
self48->maxOfMax[secondIndex >> Node48::kMaxOfMaxShift] =
|
||||||
|
std::max(self48->maxOfMax[secondIndex >> Node48::kMaxOfMaxShift],
|
||||||
|
newMaxVersion);
|
||||||
|
}
|
||||||
return self48->children[secondIndex];
|
return self48->children[secondIndex];
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case Type_Node256: {
|
case Type_Node256: {
|
||||||
auto *self256 = static_cast<Node256 *>(self);
|
auto *self256 = static_cast<Node256 *>(self);
|
||||||
if (auto &result = self256->children[index]; result != nullptr) {
|
if (auto &result = self256->children[index]; result != nullptr) {
|
||||||
|
if (self256->children[index]->partialKeyLen == 0) {
|
||||||
|
self256->childMaxVersion[index] = newMaxVersion;
|
||||||
|
self256->maxOfMax[index >> Node256::kMaxOfMaxShift] = std::max(
|
||||||
|
self256->maxOfMax[index >> Node256::kMaxOfMaxShift], newMaxVersion);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -1332,6 +1400,29 @@ void maybeDecreaseCapacity(Node *&self, WriteContext *tls,
|
|||||||
freeAndMakeCapacityAtLeast(self, maxCapacity, tls, impl, false);
|
freeAndMakeCapacityAtLeast(self, maxCapacity, tls, impl, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAS_AVX) && !defined(__SANITIZE_THREAD__)
|
||||||
|
// This gets covered in local development
|
||||||
|
// GCOVR_EXCL_START
|
||||||
|
__attribute__((target("avx512f"))) void rezero16(InternalVersionT *vs,
|
||||||
|
InternalVersionT zero) {
|
||||||
|
uint32_t z;
|
||||||
|
memcpy(&z, &zero, sizeof(z));
|
||||||
|
const auto zvec = _mm512_set1_epi32(z);
|
||||||
|
auto m = _mm512_cmplt_epi32_mask(
|
||||||
|
_mm512_sub_epi32(_mm512_loadu_epi32(vs), zvec), _mm512_setzero_epi32());
|
||||||
|
_mm512_mask_storeu_epi32(vs, m, zvec);
|
||||||
|
}
|
||||||
|
// GCOVR_EXCL_STOP
|
||||||
|
|
||||||
|
__attribute__((target("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void rezero16(InternalVersionT *vs, InternalVersionT zero) {
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
vs[i] = std::max(vs[i], zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void rezero(Node *n, InternalVersionT z) {
|
void rezero(Node *n, InternalVersionT z) {
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
fprintf(stderr, "rezero to %" PRId64 ": %s\n", z.toInt64(),
|
fprintf(stderr, "rezero to %" PRId64 ": %s\n", z.toInt64(),
|
||||||
@@ -1352,14 +1443,12 @@ void rezero(Node *n, InternalVersionT z) {
|
|||||||
} break;
|
} break;
|
||||||
case Type_Node16: {
|
case Type_Node16: {
|
||||||
auto *self = static_cast<Node16 *>(n);
|
auto *self = static_cast<Node16 *>(n);
|
||||||
for (int i = 0; i < 16; ++i) {
|
rezero16(self->childMaxVersion, z);
|
||||||
self->childMaxVersion[i] = std::max(self->childMaxVersion[i], z);
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
case Type_Node48: {
|
case Type_Node48: {
|
||||||
auto *self = static_cast<Node48 *>(n);
|
auto *self = static_cast<Node48 *>(n);
|
||||||
for (int i = 0; i < 48; ++i) {
|
for (int i = 0; i < 48; i += 16) {
|
||||||
self->childMaxVersion[i] = std::max(self->childMaxVersion[i], z);
|
rezero16(self->childMaxVersion + i, z);
|
||||||
}
|
}
|
||||||
for (auto &m : self->maxOfMax) {
|
for (auto &m : self->maxOfMax) {
|
||||||
m = std::max(m, z);
|
m = std::max(m, z);
|
||||||
@@ -1367,12 +1456,10 @@ void rezero(Node *n, InternalVersionT z) {
|
|||||||
} break;
|
} break;
|
||||||
case Type_Node256: {
|
case Type_Node256: {
|
||||||
auto *self = static_cast<Node256 *>(n);
|
auto *self = static_cast<Node256 *>(n);
|
||||||
for (int i = 0; i < 256; ++i) {
|
for (int i = 0; i < 256; i += 16) {
|
||||||
self->childMaxVersion[i] = std::max(self->childMaxVersion[i], z);
|
rezero16(self->childMaxVersion + i, z);
|
||||||
}
|
|
||||||
for (auto &m : self->maxOfMax) {
|
|
||||||
m = std::max(m, z);
|
|
||||||
}
|
}
|
||||||
|
rezero16(self->maxOfMax, z);
|
||||||
} break;
|
} break;
|
||||||
default: // GCOVR_EXCL_LINE
|
default: // GCOVR_EXCL_LINE
|
||||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
@@ -1676,7 +1763,7 @@ int firstNeqStride(const uint8_t *ap, const uint8_t *bp) {
|
|||||||
// This gets covered in local development
|
// This gets covered in local development
|
||||||
// GCOVR_EXCL_START
|
// GCOVR_EXCL_START
|
||||||
#if defined(HAS_AVX) && !defined(__SANITIZE_THREAD__)
|
#if defined(HAS_AVX) && !defined(__SANITIZE_THREAD__)
|
||||||
__attribute__((target("avx512bw"))) int
|
__attribute__((target("avx512f,avx512bw"))) int
|
||||||
longestCommonPrefix(const uint8_t *ap, const uint8_t *bp, int cl) {
|
longestCommonPrefix(const uint8_t *ap, const uint8_t *bp, int cl) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int end = cl & ~63;
|
int end = cl & ~63;
|
||||||
@@ -1761,41 +1848,6 @@ int longestCommonPrefix(const uint8_t *ap, const uint8_t *bp, int cl) {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs a physical search for remaining
|
|
||||||
struct SearchStepWise {
|
|
||||||
Node *n;
|
|
||||||
std::span<const uint8_t> remaining;
|
|
||||||
|
|
||||||
SearchStepWise() {}
|
|
||||||
SearchStepWise(Node *n, std::span<const uint8_t> remaining)
|
|
||||||
: n(n), remaining(remaining) {
|
|
||||||
assert(n->partialKeyLen == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool step() {
|
|
||||||
if (remaining.size() == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
|
||||||
if (child == nullptr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (child->partialKeyLen > 0) {
|
|
||||||
int cl = std::min<int>(child->partialKeyLen, remaining.size() - 1);
|
|
||||||
int i =
|
|
||||||
longestCommonPrefix(child->partialKey(), remaining.data() + 1, cl);
|
|
||||||
if (i != child->partialKeyLen) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n = child;
|
|
||||||
remaining =
|
|
||||||
remaining.subspan(1 + child->partialKeyLen,
|
|
||||||
remaining.size() - (1 + child->partialKeyLen));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -1806,12 +1858,7 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
fprintf(stderr, "Check point read: %s\n", printable(key).c_str());
|
fprintf(stderr, "Check point read: %s\n", printable(key).c_str());
|
||||||
#endif
|
#endif
|
||||||
auto remaining = key;
|
auto remaining = key;
|
||||||
auto *impl = tls->impl;
|
|
||||||
for (;; ++tls->point_read_iterations_accum) {
|
for (;; ++tls->point_read_iterations_accum) {
|
||||||
if (maxVersion(n, impl) <= readVersion) {
|
|
||||||
++tls->point_read_short_circuit_accum;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (remaining.size() == 0) {
|
if (remaining.size() == 0) {
|
||||||
if (n->entryPresent) {
|
if (n->entryPresent) {
|
||||||
return n->entry.pointVersion <= readVersion;
|
return n->entry.pointVersion <= readVersion;
|
||||||
@@ -1820,7 +1867,7 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto [child, maxV] = getChildAndMaxVersion(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
auto c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c != nullptr) {
|
if (c != nullptr) {
|
||||||
@@ -1862,6 +1909,11 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (maxV <= readVersion) {
|
||||||
|
++tls->point_read_short_circuit_accum;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
downLeftSpine:
|
downLeftSpine:
|
||||||
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||||
@@ -1881,17 +1933,11 @@ bool checkPrefixRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
auto remaining = key;
|
auto remaining = key;
|
||||||
auto *impl = tls->impl;
|
auto *impl = tls->impl;
|
||||||
for (;; ++tls->prefix_read_iterations_accum) {
|
for (;; ++tls->prefix_read_iterations_accum) {
|
||||||
auto m = maxVersion(n, impl);
|
|
||||||
if (remaining.size() == 0) {
|
if (remaining.size() == 0) {
|
||||||
return m <= readVersion;
|
return maxVersion(n, impl) <= readVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m <= readVersion) {
|
auto [child, maxV] = getChildAndMaxVersion(n, remaining[0]);
|
||||||
++tls->prefix_read_short_circuit_accum;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
auto c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c != nullptr) {
|
if (c != nullptr) {
|
||||||
@@ -1937,6 +1983,11 @@ bool checkPrefixRead(Node *n, const std::span<const uint8_t> key,
|
|||||||
goto downLeftSpine;
|
goto downLeftSpine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (maxV <= readVersion) {
|
||||||
|
++tls->prefix_read_short_circuit_accum;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
downLeftSpine:
|
downLeftSpine:
|
||||||
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
for (; !n->entryPresent; n = getFirstChildExists(n)) {
|
||||||
@@ -2053,13 +2104,9 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if v[i] <= readVersion for all i such that begin <= i < end
|
// Returns true if v[i] <= readVersion for all i such that begin <= i < end
|
||||||
//
|
|
||||||
// always_inline So that we can optimize when begin or end is a constant.
|
|
||||||
// gcovr exclude annotation necessary because of always_inline?
|
|
||||||
template <bool kAVX512>
|
template <bool kAVX512>
|
||||||
inline __attribute__((always_inline)) bool
|
bool scan16(const InternalVersionT *vs, int begin, int end,
|
||||||
scan16(const InternalVersionT *vs, int begin, int end, // GCOVR_EXCL_LINE
|
InternalVersionT readVersion) {
|
||||||
InternalVersionT readVersion) { // GCOVR_EXCL_LINE
|
|
||||||
assert(0 <= begin && begin < 16);
|
assert(0 <= begin && begin < 16);
|
||||||
assert(0 <= end && end <= 16);
|
assert(0 <= end && end <= 16);
|
||||||
assert(begin <= end);
|
assert(begin <= end);
|
||||||
@@ -2114,8 +2161,9 @@ scan16(const InternalVersionT *vs, int begin, int end, // GCOVR_EXCL_LINE
|
|||||||
// path of n + [child], where child in (begin, end) is <= readVersion. Does not
|
// path of n + [child], where child in (begin, end) is <= readVersion. Does not
|
||||||
// account for the range version of firstGt(searchpath(n) + [end - 1])
|
// account for the range version of firstGt(searchpath(n) + [end - 1])
|
||||||
template <bool kAVX512>
|
template <bool kAVX512>
|
||||||
bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
bool checkMaxBetweenExclusiveImpl(Node *n, int begin, int end,
|
||||||
InternalVersionT readVersion, ReadContext *tls) {
|
InternalVersionT readVersion,
|
||||||
|
ReadContext *tls) {
|
||||||
++tls->range_read_node_scan_accum;
|
++tls->range_read_node_scan_accum;
|
||||||
assume(-1 <= begin);
|
assume(-1 <= begin);
|
||||||
assume(begin <= 256);
|
assume(begin <= 256);
|
||||||
@@ -2147,7 +2195,7 @@ bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
|||||||
if (!mask) {
|
if (!mask) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto *child = self->children[__builtin_ctz(mask)];
|
auto *child = self->children[std::countr_zero(mask)];
|
||||||
const bool firstRangeOk =
|
const bool firstRangeOk =
|
||||||
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||||
uint32_t compared = 0;
|
uint32_t compared = 0;
|
||||||
@@ -2182,7 +2230,7 @@ bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
|||||||
if (!mask) {
|
if (!mask) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto *child = self->children[__builtin_ctzll(mask) >> 2];
|
auto *child = self->children[std::countr_zero(mask) >> 2];
|
||||||
const bool firstRangeOk =
|
const bool firstRangeOk =
|
||||||
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||||
|
|
||||||
@@ -2222,7 +2270,7 @@ bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
|||||||
if (!mask) {
|
if (!mask) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto *child = self->children[__builtin_ctz(mask)];
|
auto *child = self->children[std::countr_zero(mask)];
|
||||||
const bool firstRangeOk =
|
const bool firstRangeOk =
|
||||||
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||||
|
|
||||||
@@ -2253,7 +2301,7 @@ bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
|||||||
if (!mask) {
|
if (!mask) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto *child = self->children[__builtin_ctz(mask)];
|
auto *child = self->children[std::countr_zero(mask)];
|
||||||
const bool firstRangeOk =
|
const bool firstRangeOk =
|
||||||
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||||
uint32_t compared = 0;
|
uint32_t compared = 0;
|
||||||
@@ -2363,6 +2411,22 @@ bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAS_AVX) && !defined(__SANITIZE_THREAD__)
|
||||||
|
// This gets covered in local development
|
||||||
|
// GCOVR_EXCL_START
|
||||||
|
__attribute__((target("avx512f"))) bool
|
||||||
|
checkMaxBetweenExclusive(Node *n, int begin, int end,
|
||||||
|
InternalVersionT readVersion, ReadContext *tls) {
|
||||||
|
return checkMaxBetweenExclusiveImpl<true>(n, begin, end, readVersion, tls);
|
||||||
|
}
|
||||||
|
// GCOVR_EXCL_STOP
|
||||||
|
__attribute__((target("default")))
|
||||||
|
#endif
|
||||||
|
bool checkMaxBetweenExclusive(Node *n, int begin, int end,
|
||||||
|
InternalVersionT readVersion, ReadContext *tls) {
|
||||||
|
return checkMaxBetweenExclusiveImpl<false>(n, begin, end, readVersion, tls);
|
||||||
|
}
|
||||||
|
|
||||||
Vector<uint8_t> getSearchPath(Arena &arena, Node *n) {
|
Vector<uint8_t> getSearchPath(Arena &arena, Node *n) {
|
||||||
assert(n != nullptr);
|
assert(n != nullptr);
|
||||||
auto result = vector<uint8_t>(arena);
|
auto result = vector<uint8_t>(arena);
|
||||||
@@ -2385,7 +2449,6 @@ Vector<uint8_t> getSearchPath(Arena &arena, Node *n) {
|
|||||||
//
|
//
|
||||||
// Precondition: transitively, no child of n has a search path that's a longer
|
// Precondition: transitively, no child of n has a search path that's a longer
|
||||||
// prefix of key than n
|
// prefix of key than n
|
||||||
template <bool kAVX512>
|
|
||||||
bool checkRangeStartsWith(Node *n, std::span<const uint8_t> key, int begin,
|
bool checkRangeStartsWith(Node *n, std::span<const uint8_t> key, int begin,
|
||||||
int end, InternalVersionT readVersion,
|
int end, InternalVersionT readVersion,
|
||||||
ReadContext *tls) {
|
ReadContext *tls) {
|
||||||
@@ -2395,7 +2458,7 @@ bool checkRangeStartsWith(Node *n, std::span<const uint8_t> key, int begin,
|
|||||||
auto remaining = key;
|
auto remaining = key;
|
||||||
auto *impl = tls->impl;
|
auto *impl = tls->impl;
|
||||||
if (remaining.size() == 0) {
|
if (remaining.size() == 0) {
|
||||||
return checkMaxBetweenExclusive<kAVX512>(n, begin, end, readVersion, tls);
|
return checkMaxBetweenExclusive(n, begin, end, readVersion, tls);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
@@ -2454,7 +2517,7 @@ downLeftSpine:
|
|||||||
namespace {
|
namespace {
|
||||||
// Return true if the max version among all keys that start with key[:prefixLen]
|
// Return true if the max version among all keys that start with key[:prefixLen]
|
||||||
// that are >= key is <= readVersion
|
// that are >= key is <= readVersion
|
||||||
template <bool kAVX512> struct CheckRangeLeftSide {
|
struct CheckRangeLeftSide {
|
||||||
CheckRangeLeftSide(Node *n, std::span<const uint8_t> key, int prefixLen,
|
CheckRangeLeftSide(Node *n, std::span<const uint8_t> key, int prefixLen,
|
||||||
InternalVersionT readVersion, ReadContext *tls)
|
InternalVersionT readVersion, ReadContext *tls)
|
||||||
: n(n), remaining(key), prefixLen(prefixLen), readVersion(readVersion),
|
: n(n), remaining(key), prefixLen(prefixLen), readVersion(readVersion),
|
||||||
@@ -2476,10 +2539,6 @@ template <bool kAVX512> struct CheckRangeLeftSide {
|
|||||||
bool ok;
|
bool ok;
|
||||||
|
|
||||||
bool step() {
|
bool step() {
|
||||||
if (maxVersion(n, impl) <= readVersion) {
|
|
||||||
ok = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (remaining.size() == 0) {
|
if (remaining.size() == 0) {
|
||||||
assert(searchPathLen >= prefixLen);
|
assert(searchPathLen >= prefixLen);
|
||||||
ok = maxVersion(n, impl) <= readVersion;
|
ok = maxVersion(n, impl) <= readVersion;
|
||||||
@@ -2487,14 +2546,13 @@ template <bool kAVX512> struct CheckRangeLeftSide {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (searchPathLen >= prefixLen) {
|
if (searchPathLen >= prefixLen) {
|
||||||
if (!checkMaxBetweenExclusive<kAVX512>(n, remaining[0], 256, readVersion,
|
if (!checkMaxBetweenExclusive(n, remaining[0], 256, readVersion, tls)) {
|
||||||
tls)) {
|
|
||||||
ok = false;
|
ok = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto [child, maxV] = getChildAndMaxVersion(n, remaining[0]);
|
||||||
if (child == nullptr) {
|
if (child == nullptr) {
|
||||||
auto c = getChildGeq(n, remaining[0]);
|
auto c = getChildGeq(n, remaining[0]);
|
||||||
if (c != nullptr) {
|
if (c != nullptr) {
|
||||||
@@ -2557,6 +2615,10 @@ template <bool kAVX512> struct CheckRangeLeftSide {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (maxV <= readVersion) {
|
||||||
|
ok = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2570,7 +2632,7 @@ template <bool kAVX512> struct CheckRangeLeftSide {
|
|||||||
|
|
||||||
// Return true if the max version among all keys that start with key[:prefixLen]
|
// Return true if the max version among all keys that start with key[:prefixLen]
|
||||||
// that are < key is <= readVersion
|
// that are < key is <= readVersion
|
||||||
template <bool kAVX512> struct CheckRangeRightSide {
|
struct CheckRangeRightSide {
|
||||||
CheckRangeRightSide(Node *n, std::span<const uint8_t> key, int prefixLen,
|
CheckRangeRightSide(Node *n, std::span<const uint8_t> key, int prefixLen,
|
||||||
InternalVersionT readVersion, ReadContext *tls)
|
InternalVersionT readVersion, ReadContext *tls)
|
||||||
: n(n), key(key), remaining(key), prefixLen(prefixLen),
|
: n(n), key(key), remaining(key), prefixLen(prefixLen),
|
||||||
@@ -2613,8 +2675,7 @@ template <bool kAVX512> struct CheckRangeRightSide {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkMaxBetweenExclusive<kAVX512>(n, -1, remaining[0], readVersion,
|
if (!checkMaxBetweenExclusive(n, -1, remaining[0], readVersion, tls)) {
|
||||||
tls)) {
|
|
||||||
ok = false;
|
ok = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2701,10 +2762,9 @@ template <bool kAVX512> struct CheckRangeRightSide {
|
|||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
template <bool kAVX512>
|
bool checkRangeRead(Node *n, std::span<const uint8_t> begin,
|
||||||
bool checkRangeReadImpl(Node *n, std::span<const uint8_t> begin,
|
std::span<const uint8_t> end, InternalVersionT readVersion,
|
||||||
std::span<const uint8_t> end,
|
ReadContext *tls) {
|
||||||
InternalVersionT readVersion, ReadContext *tls) {
|
|
||||||
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 &&
|
||||||
@@ -2718,50 +2778,63 @@ bool checkRangeReadImpl(Node *n, std::span<const uint8_t> begin,
|
|||||||
|
|
||||||
++tls->range_read_accum;
|
++tls->range_read_accum;
|
||||||
|
|
||||||
SearchStepWise search{n, begin.subspan(0, lcp)};
|
auto remaining = begin.subspan(0, lcp);
|
||||||
Arena arena;
|
Arena arena;
|
||||||
auto *impl = tls->impl;
|
// If the common prefix isn't a prefix of any physical entry in the tree, we
|
||||||
|
// can go to "downLeftSpine"
|
||||||
for (;; ++tls->range_read_iterations_accum) {
|
for (;; ++tls->range_read_iterations_accum) {
|
||||||
assert(getSearchPath(arena, search.n) <=>
|
assert(getSearchPath(arena, n) <=>
|
||||||
begin.subspan(0, lcp - search.remaining.size()) ==
|
begin.subspan(0, lcp - remaining.size()) ==
|
||||||
0);
|
0);
|
||||||
if (maxVersion(search.n, impl) <= readVersion) {
|
if (remaining.size() == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto [child, v] = getChildAndMaxVersion(n, remaining[0]);
|
||||||
|
if (child == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child->partialKeyLen > 0) {
|
||||||
|
int cl = std::min<int>(child->partialKeyLen, remaining.size() - 1);
|
||||||
|
int i =
|
||||||
|
longestCommonPrefix(child->partialKey(), remaining.data() + 1, cl);
|
||||||
|
if (i != child->partialKeyLen) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (v <= readVersion) {
|
||||||
++tls->range_read_short_circuit_accum;
|
++tls->range_read_short_circuit_accum;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (search.step()) {
|
n = child;
|
||||||
break;
|
remaining =
|
||||||
}
|
remaining.subspan(1 + child->partialKeyLen,
|
||||||
|
remaining.size() - (1 + child->partialKeyLen));
|
||||||
}
|
}
|
||||||
assert(getSearchPath(arena, search.n) <=>
|
assert(getSearchPath(arena, n) <=> begin.subspan(0, lcp - remaining.size()) ==
|
||||||
begin.subspan(0, lcp - search.remaining.size()) ==
|
|
||||||
0);
|
0);
|
||||||
|
|
||||||
const int consumed = lcp - search.remaining.size();
|
const int consumed = lcp - remaining.size();
|
||||||
assume(consumed >= 0);
|
assume(consumed >= 0);
|
||||||
|
|
||||||
begin = begin.subspan(consumed, int(begin.size()) - consumed);
|
begin = begin.subspan(consumed, int(begin.size()) - consumed);
|
||||||
end = end.subspan(consumed, int(end.size()) - consumed);
|
end = end.subspan(consumed, int(end.size()) - consumed);
|
||||||
n = search.n;
|
|
||||||
lcp -= consumed;
|
lcp -= consumed;
|
||||||
|
|
||||||
if (lcp == int(begin.size())) {
|
if (lcp == int(begin.size())) {
|
||||||
CheckRangeRightSide<kAVX512> checkRangeRightSide{n, end, lcp, readVersion,
|
CheckRangeRightSide checkRangeRightSide{n, end, lcp, readVersion, tls};
|
||||||
tls};
|
|
||||||
while (!checkRangeRightSide.step())
|
while (!checkRangeRightSide.step())
|
||||||
;
|
;
|
||||||
return checkRangeRightSide.ok;
|
return checkRangeRightSide.ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkRangeStartsWith<kAVX512>(n, begin.subspan(0, lcp), begin[lcp],
|
if (!checkRangeStartsWith(n, begin.subspan(0, lcp), begin[lcp], end[lcp],
|
||||||
end[lcp], readVersion, tls)) {
|
readVersion, tls)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckRangeLeftSide<kAVX512> checkRangeLeftSide{n, begin, lcp + 1, readVersion,
|
CheckRangeLeftSide checkRangeLeftSide{n, begin, lcp + 1, readVersion, tls};
|
||||||
tls};
|
CheckRangeRightSide checkRangeRightSide{n, end, lcp + 1, readVersion, tls};
|
||||||
CheckRangeRightSide<kAVX512> checkRangeRightSide{n, end, lcp + 1, readVersion,
|
|
||||||
tls};
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
bool leftDone = checkRangeLeftSide.step();
|
bool leftDone = checkRangeLeftSide.step();
|
||||||
@@ -2796,138 +2869,101 @@ bool checkRangeReadImpl(Node *n, std::span<const uint8_t> begin,
|
|||||||
template __attribute__((target("avx512f"))) bool
|
template __attribute__((target("avx512f"))) bool
|
||||||
scan16<true>(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
scan16<true>(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
||||||
InternalVersionT readVersion);
|
InternalVersionT readVersion);
|
||||||
template __attribute__((always_inline, target("avx512f"))) bool
|
template __attribute__((target("avx512f"))) bool
|
||||||
scan16<true>(const InternalVersionT *vs, int begin, int end,
|
scan16<true>(const InternalVersionT *vs, int begin, int end,
|
||||||
InternalVersionT readVersion);
|
InternalVersionT readVersion);
|
||||||
template __attribute__((target("avx512f"))) bool
|
template __attribute__((target("avx512f"))) bool
|
||||||
checkMaxBetweenExclusive<true>(Node *n, int begin, int end,
|
checkMaxBetweenExclusiveImpl<true>(Node *n, int begin, int end,
|
||||||
InternalVersionT readVersion, ReadContext *);
|
InternalVersionT readVersion, ReadContext *);
|
||||||
template __attribute__((target("avx512f"))) bool
|
|
||||||
checkRangeStartsWith<true>(Node *n, std::span<const uint8_t> key, int begin,
|
|
||||||
int end, InternalVersionT readVersion,
|
|
||||||
ReadContext *);
|
|
||||||
template __attribute__((target("avx512f"))) bool
|
|
||||||
CheckRangeLeftSide<true>::step();
|
|
||||||
template __attribute__((target("avx512f"))) bool
|
|
||||||
CheckRangeRightSide<true>::step();
|
|
||||||
template __attribute__((target("avx512f"))) bool
|
|
||||||
checkRangeReadImpl<true>(Node *n, std::span<const uint8_t> begin,
|
|
||||||
std::span<const uint8_t> end,
|
|
||||||
InternalVersionT readVersion, ReadContext *);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__SANITIZE_THREAD__) || !defined(__x86_64__)
|
// Consume the partial key of `self` (which must exist), and update `self` and
|
||||||
bool checkRangeRead(Node *n, std::span<const uint8_t> begin,
|
// `key` such that `self` is along the search path of `key`
|
||||||
std::span<const uint8_t> end, InternalVersionT readVersion,
|
void consumePartialKey(Node *&self, std::span<const uint8_t> &key,
|
||||||
ReadContext *tls) {
|
InternalVersionT writeVersion, WriteContext *tls) {
|
||||||
return checkRangeReadImpl<false>(n, begin, end, readVersion, tls);
|
assert(self->partialKeyLen > 0);
|
||||||
|
// Handle an existing partial key
|
||||||
|
int commonLen = std::min<int>(self->partialKeyLen, key.size());
|
||||||
|
int partialKeyIndex =
|
||||||
|
longestCommonPrefix(self->partialKey(), key.data(), commonLen);
|
||||||
|
if (partialKeyIndex < self->partialKeyLen) {
|
||||||
|
auto *old = self;
|
||||||
|
// Since root cannot have a partial key
|
||||||
|
assert(old->parent != nullptr);
|
||||||
|
InternalVersionT oldMaxVersion = exchangeMaxVersion(old, writeVersion);
|
||||||
|
|
||||||
|
// *self will have one child (old)
|
||||||
|
auto *newSelf = tls->allocate<Node3>(partialKeyIndex);
|
||||||
|
|
||||||
|
newSelf->parent = old->parent;
|
||||||
|
newSelf->parentsIndex = old->parentsIndex;
|
||||||
|
newSelf->partialKeyLen = partialKeyIndex;
|
||||||
|
newSelf->entryPresent = false;
|
||||||
|
newSelf->numChildren = 1;
|
||||||
|
|
||||||
|
memcpy(newSelf->partialKey(), old->partialKey(), newSelf->partialKeyLen);
|
||||||
|
|
||||||
|
uint8_t oldDistinguishingByte = old->partialKey()[partialKeyIndex];
|
||||||
|
old->parent = newSelf;
|
||||||
|
old->parentsIndex = oldDistinguishingByte;
|
||||||
|
newSelf->index[0] = oldDistinguishingByte;
|
||||||
|
newSelf->children[0] = old;
|
||||||
|
newSelf->childMaxVersion[0] = oldMaxVersion;
|
||||||
|
self = newSelf;
|
||||||
|
|
||||||
|
memmove(old->partialKey(), old->partialKey() + partialKeyIndex + 1,
|
||||||
|
old->partialKeyLen - (partialKeyIndex + 1));
|
||||||
|
old->partialKeyLen -= partialKeyIndex + 1;
|
||||||
|
|
||||||
|
// We would consider decreasing capacity here, but we can't invalidate
|
||||||
|
// old since it's not on the search path. setOldestVersion will clean it
|
||||||
|
// up.
|
||||||
|
}
|
||||||
|
key = key.subspan(partialKeyIndex, key.size() - partialKeyIndex);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
// Only one of these is ever exercised. I'm not worried about somehow not
|
|
||||||
// calling one of these though.
|
|
||||||
// GCOVR_EXCL_START
|
|
||||||
__attribute__((target("default"))) bool
|
|
||||||
checkRangeRead(Node *n, std::span<const uint8_t> begin,
|
|
||||||
std::span<const uint8_t> end, InternalVersionT readVersion,
|
|
||||||
ReadContext *tls) {
|
|
||||||
return checkRangeReadImpl<false>(n, begin, end, readVersion, tls);
|
|
||||||
}
|
|
||||||
__attribute__((target("avx512f"))) bool
|
|
||||||
checkRangeRead(Node *n, std::span<const uint8_t> begin,
|
|
||||||
std::span<const uint8_t> end, InternalVersionT readVersion,
|
|
||||||
ReadContext *tls) {
|
|
||||||
return checkRangeReadImpl<true>(n, begin, end, readVersion, tls);
|
|
||||||
}
|
|
||||||
// GCOVR_EXCL_STOP
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Returns a pointer to the newly inserted node. Caller must set
|
// Returns a pointer to the newly inserted node. Caller must set
|
||||||
// `entryPresent`, `entry` fields and `maxVersion` on the result. The search
|
// `entryPresent`, and `entry` fields. All nodes along the search path of the
|
||||||
// path of the result's parent will have `maxVersion` at least `writeVersion` as
|
// result will have `maxVersion` set to `writeVersion` as a postcondition. Nodes
|
||||||
// a postcondition. Nodes along the search path to `key` may be invalidated.
|
// along the search path may be invalidated.
|
||||||
template <bool kBegin>
|
[[nodiscard]]
|
||||||
[[nodiscard]] Node *insert(Node **self, std::span<const uint8_t> key,
|
Node *insert(Node **self, std::span<const uint8_t> key,
|
||||||
InternalVersionT writeVersion, WriteContext *tls,
|
InternalVersionT writeVersion, WriteContext *tls,
|
||||||
ConflictSet::Impl *impl) {
|
ConflictSet::Impl *impl) {
|
||||||
|
|
||||||
|
if ((*self)->partialKeyLen > 0) {
|
||||||
|
consumePartialKey(*self, key, writeVersion, tls);
|
||||||
|
}
|
||||||
|
assert(maxVersion(*self, impl) <= writeVersion);
|
||||||
|
setMaxVersion(*self, impl, writeVersion);
|
||||||
|
|
||||||
for (;; ++tls->accum.insert_iterations) {
|
for (;; ++tls->accum.insert_iterations) {
|
||||||
|
|
||||||
if ((*self)->partialKeyLen > 0) {
|
|
||||||
// Handle an existing partial key
|
|
||||||
int commonLen = std::min<int>((*self)->partialKeyLen, key.size());
|
|
||||||
int partialKeyIndex =
|
|
||||||
longestCommonPrefix((*self)->partialKey(), key.data(), commonLen);
|
|
||||||
if (partialKeyIndex < (*self)->partialKeyLen) {
|
|
||||||
auto *old = *self;
|
|
||||||
InternalVersionT oldMaxVersion = maxVersion(old, impl);
|
|
||||||
|
|
||||||
// *self will have one child
|
|
||||||
*self = tls->allocate<Node3>(partialKeyIndex);
|
|
||||||
|
|
||||||
memcpy((char *)*self + kNodeCopyBegin, (char *)old + kNodeCopyBegin,
|
|
||||||
kNodeCopySize);
|
|
||||||
(*self)->partialKeyLen = partialKeyIndex;
|
|
||||||
|
|
||||||
// Not necessary to call removeKey here, since this node is "synthetic"
|
|
||||||
(*self)->entryPresent = false;
|
|
||||||
|
|
||||||
(*self)->numChildren = 0;
|
|
||||||
memcpy((*self)->partialKey(), old->partialKey(),
|
|
||||||
(*self)->partialKeyLen);
|
|
||||||
|
|
||||||
getOrCreateChild(*self, old->partialKey()[partialKeyIndex], tls) = old;
|
|
||||||
old->parent = *self;
|
|
||||||
old->parentsIndex = old->partialKey()[partialKeyIndex];
|
|
||||||
setMaxVersion(old, impl, oldMaxVersion);
|
|
||||||
|
|
||||||
memmove(old->partialKey(), old->partialKey() + partialKeyIndex + 1,
|
|
||||||
old->partialKeyLen - (partialKeyIndex + 1));
|
|
||||||
old->partialKeyLen -= partialKeyIndex + 1;
|
|
||||||
|
|
||||||
// We would consider decreasing capacity here, but we can't invalidate
|
|
||||||
// old since it's not on the search path. setOldestVersion will clean it
|
|
||||||
// up.
|
|
||||||
}
|
|
||||||
key = key.subspan(partialKeyIndex, key.size() - partialKeyIndex);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Consider adding a partial key
|
|
||||||
if ((*self)->numChildren == 0 && !(*self)->entryPresent) {
|
|
||||||
assert((*self)->getCapacity() >= int(key.size()));
|
|
||||||
(*self)->partialKeyLen = key.size();
|
|
||||||
memcpy((*self)->partialKey(), key.data(), (*self)->partialKeyLen);
|
|
||||||
key = key.subspan((*self)->partialKeyLen,
|
|
||||||
key.size() - (*self)->partialKeyLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (kBegin) {
|
|
||||||
assert(maxVersion(*self, impl) <= writeVersion);
|
|
||||||
setMaxVersion(*self, impl, writeVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.size() == 0) {
|
if (key.size() == 0) {
|
||||||
return *self;
|
return *self;
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (!kBegin) {
|
auto &child = getOrCreateChild(*self, key.front(), writeVersion, tls);
|
||||||
assert(maxVersion(*self, impl) <= writeVersion);
|
|
||||||
setMaxVersion(*self, impl, writeVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &child = getOrCreateChild(*self, key.front(), tls);
|
|
||||||
if (!child) {
|
if (!child) {
|
||||||
child = tls->allocate<Node0>(key.size() - 1);
|
child = tls->allocate<Node0>(key.size() - 1);
|
||||||
child->numChildren = 0;
|
child->numChildren = 0;
|
||||||
child->entryPresent = false;
|
child->entryPresent = false;
|
||||||
child->partialKeyLen = 0;
|
child->partialKeyLen = key.size() - 1;
|
||||||
child->parent = *self;
|
child->parent = *self;
|
||||||
child->parentsIndex = key.front();
|
child->parentsIndex = key.front();
|
||||||
setMaxVersion(child, impl, kBegin ? writeVersion : tls->zero);
|
setMaxVersion(child, impl, writeVersion);
|
||||||
|
memcpy(child->partialKey(), key.data() + 1, child->partialKeyLen);
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
self = &child;
|
self = &child;
|
||||||
key = key.subspan(1, key.size() - 1);
|
key = key.subspan(1, key.size() - 1);
|
||||||
|
|
||||||
|
if ((*self)->partialKeyLen > 0) {
|
||||||
|
consumePartialKey(*self, key, writeVersion, tls);
|
||||||
|
assert(maxVersion(*self, impl) <= writeVersion);
|
||||||
|
setMaxVersion(*self, impl, writeVersion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2960,7 +2996,7 @@ void addPointWrite(Node *&root, std::span<const uint8_t> key,
|
|||||||
InternalVersionT writeVersion, WriteContext *tls,
|
InternalVersionT writeVersion, WriteContext *tls,
|
||||||
ConflictSet::Impl *impl) {
|
ConflictSet::Impl *impl) {
|
||||||
++tls->accum.point_writes;
|
++tls->accum.point_writes;
|
||||||
auto *n = insert<true>(&root, key, writeVersion, tls, impl);
|
auto *n = insert(&root, key, writeVersion, tls, impl);
|
||||||
if (!n->entryPresent) {
|
if (!n->entryPresent) {
|
||||||
++tls->accum.entries_inserted;
|
++tls->accum.entries_inserted;
|
||||||
auto *p = nextLogical(n);
|
auto *p = nextLogical(n);
|
||||||
@@ -2969,7 +3005,6 @@ void addPointWrite(Node *&root, std::span<const uint8_t> key,
|
|||||||
n->entryPresent = true;
|
n->entryPresent = true;
|
||||||
|
|
||||||
n->entry.pointVersion = writeVersion;
|
n->entry.pointVersion = writeVersion;
|
||||||
setMaxVersion(n, impl, writeVersion);
|
|
||||||
n->entry.rangeVersion =
|
n->entry.rangeVersion =
|
||||||
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
||||||
} else {
|
} else {
|
||||||
@@ -2978,6 +3013,44 @@ void addPointWrite(Node *&root, std::span<const uint8_t> key,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Precondition: `node->entryPresent`
|
||||||
|
void fixupMaxVersion(Node *node, ConflictSet::Impl *impl, WriteContext *tls) {
|
||||||
|
InternalVersionT max;
|
||||||
|
assert(node->entryPresent);
|
||||||
|
max = std::max(node->entry.pointVersion, tls->zero);
|
||||||
|
switch (node->getType()) {
|
||||||
|
case Type_Node0:
|
||||||
|
break;
|
||||||
|
case Type_Node3: {
|
||||||
|
auto *self3 = static_cast<Node3 *>(node);
|
||||||
|
for (int i = 0; i < self3->numChildren; ++i) {
|
||||||
|
max = std::max(self3->childMaxVersion[i], max);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node16: {
|
||||||
|
auto *self16 = static_cast<Node16 *>(node);
|
||||||
|
for (int i = 0; i < self16->numChildren; ++i) {
|
||||||
|
max = std::max(self16->childMaxVersion[i], max);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node48: {
|
||||||
|
auto *self48 = static_cast<Node48 *>(node);
|
||||||
|
for (auto v : self48->maxOfMax) {
|
||||||
|
max = std::max(v, max);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node256: {
|
||||||
|
auto *self256 = static_cast<Node256 *>(node);
|
||||||
|
for (auto v : self256->maxOfMax) {
|
||||||
|
max = std::max(v, max);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default: // GCOVR_EXCL_LINE
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
}
|
||||||
|
setMaxVersion(node, impl, max);
|
||||||
|
}
|
||||||
|
|
||||||
void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
||||||
std::span<const uint8_t> end, InternalVersionT writeVersion,
|
std::span<const uint8_t> end, InternalVersionT writeVersion,
|
||||||
WriteContext *tls, ConflictSet::Impl *impl) {
|
WriteContext *tls, ConflictSet::Impl *impl) {
|
||||||
@@ -3024,7 +3097,7 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
|||||||
begin = begin.subspan(consumed, begin.size() - consumed);
|
begin = begin.subspan(consumed, begin.size() - consumed);
|
||||||
end = end.subspan(consumed, end.size() - consumed);
|
end = end.subspan(consumed, end.size() - consumed);
|
||||||
|
|
||||||
auto *beginNode = insert<true>(useAsRoot, begin, writeVersion, tls, impl);
|
auto *beginNode = insert(useAsRoot, begin, writeVersion, tls, impl);
|
||||||
|
|
||||||
const bool insertedBegin = !beginNode->entryPresent;
|
const bool insertedBegin = !beginNode->entryPresent;
|
||||||
|
|
||||||
@@ -3037,14 +3110,11 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
|||||||
beginNode->entry.rangeVersion =
|
beginNode->entry.rangeVersion =
|
||||||
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
||||||
beginNode->entry.pointVersion = writeVersion;
|
beginNode->entry.pointVersion = writeVersion;
|
||||||
assert(maxVersion(beginNode, impl) <= writeVersion);
|
|
||||||
setMaxVersion(beginNode, impl, writeVersion);
|
|
||||||
}
|
}
|
||||||
setMaxVersion(beginNode, impl, writeVersion);
|
|
||||||
assert(writeVersion >= beginNode->entry.pointVersion);
|
assert(writeVersion >= beginNode->entry.pointVersion);
|
||||||
beginNode->entry.pointVersion = writeVersion;
|
beginNode->entry.pointVersion = writeVersion;
|
||||||
|
|
||||||
auto *endNode = insert<false>(useAsRoot, end, writeVersion, tls, impl);
|
auto *endNode = insert(useAsRoot, end, writeVersion, tls, impl);
|
||||||
|
|
||||||
const bool insertedEnd = !endNode->entryPresent;
|
const bool insertedEnd = !endNode->entryPresent;
|
||||||
|
|
||||||
@@ -3056,22 +3126,22 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
|||||||
auto *p = nextLogical(endNode);
|
auto *p = nextLogical(endNode);
|
||||||
endNode->entry.pointVersion =
|
endNode->entry.pointVersion =
|
||||||
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
||||||
auto m = maxVersion(endNode, impl);
|
|
||||||
setMaxVersion(endNode, impl,
|
|
||||||
std::max<InternalVersionT>(m, endNode->entry.pointVersion));
|
|
||||||
}
|
}
|
||||||
endNode->entry.rangeVersion = writeVersion;
|
endNode->entry.rangeVersion = writeVersion;
|
||||||
|
|
||||||
if (beginIsPrefix && insertedEnd) {
|
if (beginIsPrefix && insertedEnd) {
|
||||||
// beginNode may have been invalidated when inserting end. TODO can we do
|
// beginNode may have been invalidated when inserting end. TODO can we do
|
||||||
// better?
|
// better?
|
||||||
beginNode = insert<true>(useAsRoot, begin, writeVersion, tls, impl);
|
beginNode = insert(useAsRoot, begin, writeVersion, tls, impl);
|
||||||
assert(beginNode->entryPresent);
|
assert(beginNode->entryPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (beginNode = nextLogical(beginNode); beginNode != endNode;
|
for (beginNode = nextLogical(beginNode); beginNode != endNode;
|
||||||
beginNode = erase(beginNode, tls, impl, /*logical*/ true, endNode)) {
|
beginNode = erase(beginNode, tls, impl, /*logical*/ true, endNode)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inserting end trashed endNode's maxVersion. Fix that
|
||||||
|
fixupMaxVersion(endNode, impl, tls);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *firstGeqPhysical(Node *n, const std::span<const uint8_t> key) {
|
Node *firstGeqPhysical(Node *n, const std::span<const uint8_t> key) {
|
||||||
@@ -3488,6 +3558,39 @@ InternalVersionT maxVersion(Node *n, ConflictSet::Impl *impl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Precondition `n` is not the root
|
||||||
|
InternalVersionT exchangeMaxVersion(Node *n, InternalVersionT newMax) {
|
||||||
|
int index = n->parentsIndex;
|
||||||
|
n = n->parent;
|
||||||
|
assert(n != nullptr);
|
||||||
|
switch (n->getType()) {
|
||||||
|
case Type_Node0: // GCOVR_EXCL_LINE
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
case Type_Node3: {
|
||||||
|
auto *n3 = static_cast<Node3 *>(n);
|
||||||
|
int i = getNodeIndex(n3, index);
|
||||||
|
return std::exchange(n3->childMaxVersion[i], newMax);
|
||||||
|
}
|
||||||
|
case Type_Node16: {
|
||||||
|
auto *n16 = static_cast<Node16 *>(n);
|
||||||
|
int i = getNodeIndex(n16, index);
|
||||||
|
return std::exchange(n16->childMaxVersion[i], newMax);
|
||||||
|
}
|
||||||
|
case Type_Node48: {
|
||||||
|
auto *n48 = static_cast<Node48 *>(n);
|
||||||
|
assert(n48->bitSet.test(index));
|
||||||
|
return std::exchange(n48->childMaxVersion[n48->index[index]], newMax);
|
||||||
|
}
|
||||||
|
case Type_Node256: {
|
||||||
|
auto *n256 = static_cast<Node256 *>(n);
|
||||||
|
assert(n256->bitSet.test(index));
|
||||||
|
return std::exchange(n256->childMaxVersion[index], newMax);
|
||||||
|
}
|
||||||
|
default: // GCOVR_EXCL_LINE
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setMaxVersion(Node *n, ConflictSet::Impl *impl, InternalVersionT newMax) {
|
void setMaxVersion(Node *n, ConflictSet::Impl *impl, InternalVersionT newMax) {
|
||||||
int index = n->parentsIndex;
|
int index = n->parentsIndex;
|
||||||
n = n->parent;
|
n = n->parent;
|
||||||
@@ -3998,6 +4101,10 @@ checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
|
|||||||
ConflictSet::Impl *impl) {
|
ConflictSet::Impl *impl) {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
|
if (node->partialKeyLen > 0) {
|
||||||
|
fprintf(stderr, "Root cannot have a partial key");
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
checkParentPointers(node, success);
|
checkParentPointers(node, success);
|
||||||
checkMaxVersion(node, node, oldestVersion, success, impl);
|
checkMaxVersion(node, node, oldestVersion, success, impl);
|
||||||
checkEntriesExist(node, success);
|
checkEntriesExist(node, success);
|
||||||
|
20
README.md
20
README.md
@@ -24,15 +24,15 @@ Hardware for all benchmarks is an AMD Ryzen 9 7900 with (2x32GB) 5600MT/s CL28-3
|
|||||||
|
|
||||||
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||||
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||||
| 17.03 | 58,732,967.93 | 0.6% | 276.28 | 87.96 | 3.141 | 52.15 | 0.4% | 0.01 | `point reads`
|
| 12.42 | 80,500,398.66 | 0.8% | 180.38 | 61.57 | 2.930 | 41.51 | 0.4% | 0.01 | `point reads`
|
||||||
| 19.52 | 51,239,158.04 | 0.3% | 367.16 | 101.50 | 3.617 | 61.92 | 0.3% | 0.01 | `prefix reads`
|
| 15.17 | 65,917,580.99 | 0.2% | 279.47 | 74.95 | 3.729 | 55.54 | 0.3% | 0.01 | `prefix reads`
|
||||||
| 47.74 | 20,947,676.63 | 0.5% | 998.16 | 247.43 | 4.034 | 161.64 | 0.2% | 0.01 | `range reads`
|
| 38.16 | 26,202,393.91 | 0.1% | 803.07 | 189.13 | 4.246 | 141.68 | 0.2% | 0.01 | `range reads`
|
||||||
| 23.14 | 43,207,824.89 | 0.4% | 408.18 | 121.64 | 3.356 | 70.20 | 0.3% | 0.01 | `point writes`
|
| 20.20 | 49,504,615.44 | 0.4% | 363.00 | 100.35 | 3.617 | 49.81 | 0.3% | 0.01 | `point writes`
|
||||||
| 38.02 | 26,302,115.66 | 0.1% | 709.72 | 199.70 | 3.554 | 134.26 | 0.3% | 0.01 | `prefix writes`
|
| 41.99 | 23,816,559.99 | 0.3% | 799.27 | 209.63 | 3.813 | 154.32 | 0.1% | 0.01 | `prefix writes`
|
||||||
| 44.28 | 22,583,559.17 | 0.9% | 825.19 | 233.10 | 3.540 | 141.48 | 0.2% | 0.01 | `range writes`
|
| 46.28 | 21,607,605.88 | 1.5% | 953.79 | 231.47 | 4.121 | 168.34 | 0.0% | 0.01 | `range writes`
|
||||||
| 85.50 | 11,695,990.63 | 0.5% | 1,488.16 | 455.68 | 3.266 | 289.22 | 0.1% | 0.01 | `monotonic increasing point writes`
|
| 80.99 | 12,347,449.98 | 0.9% | 1,501.97 | 406.50 | 3.695 | 281.89 | 0.1% | 0.01 | `monotonic increasing point writes`
|
||||||
| 338,388.50 | 2,955.18 | 3.3% | 4,097,087.00 | 1,809,996.00 | 2.264 | 759,645.00 | 0.0% | 0.01 | `worst case for radix tree`
|
| 318,010.00 | 3,144.56 | 1.0% | 3,994,511.50 | 1,657,831.50 | 2.409 | 805,969.50 | 0.0% | 0.01 | `worst case for radix tree`
|
||||||
| 84.84 | 11,787,313.59 | 1.4% | 1,716.02 | 440.50 | 3.896 | 271.00 | 0.0% | 0.01 | `create and destroy`
|
| 75.85 | 13,183,612.56 | 0.5% | 1,590.01 | 385.64 | 4.123 | 258.00 | 0.0% | 0.01 | `create and destroy`
|
||||||
|
|
||||||
# "Real data" test
|
# "Real data" test
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ Check: 4.47891 seconds, 364.05 MB/s, Add: 4.55599 seconds, 123.058 MB/s, Gc rati
|
|||||||
## radix tree
|
## radix tree
|
||||||
|
|
||||||
```
|
```
|
||||||
Check: 1.05813 seconds, 1540.97 MB/s, Add: 1.32071 seconds, 424.508 MB/s, Gc ratio: 42.2067%
|
Check: 0.963721 seconds, 1691.93 MB/s, Add: 1.3288 seconds, 421.924 MB/s, Gc ratio: 42.8819%
|
||||||
```
|
```
|
||||||
|
|
||||||
## hash table
|
## hash table
|
||||||
|
233
ServerBench.cpp
Normal file
233
ServerBench.cpp
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
#include <atomic>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "ConflictSet.h"
|
||||||
|
#include "third_party/nadeau.h"
|
||||||
|
|
||||||
|
std::atomic<int64_t> transactions;
|
||||||
|
|
||||||
|
constexpr int kBaseSearchDepth = 32;
|
||||||
|
constexpr int kWindowSize = 10000000;
|
||||||
|
|
||||||
|
std::basic_string<uint8_t> numToKey(int64_t num) {
|
||||||
|
std::basic_string<uint8_t> result;
|
||||||
|
result.resize(kBaseSearchDepth + sizeof(int64_t));
|
||||||
|
memset(result.data(), 0, kBaseSearchDepth);
|
||||||
|
int64_t be = __builtin_bswap64(num);
|
||||||
|
memcpy(result.data() + kBaseSearchDepth, &be, sizeof(int64_t));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void workload(weaselab::ConflictSet *cs) {
|
||||||
|
int64_t version = kWindowSize;
|
||||||
|
cs->addWrites(nullptr, 0, version);
|
||||||
|
for (;; transactions.fetch_add(1, std::memory_order_relaxed)) {
|
||||||
|
// Reads
|
||||||
|
{
|
||||||
|
auto beginK = numToKey(version - kWindowSize);
|
||||||
|
auto endK = numToKey(version - 1);
|
||||||
|
auto pointRv = version - kWindowSize + rand() % kWindowSize + 1;
|
||||||
|
auto pointK = numToKey(pointRv);
|
||||||
|
weaselab::ConflictSet::ReadRange reads[] = {
|
||||||
|
{
|
||||||
|
{pointK.data(), int(pointK.size())},
|
||||||
|
{nullptr, 0},
|
||||||
|
pointRv,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{beginK.data(), int(beginK.size())},
|
||||||
|
{endK.data(), int(endK.size())},
|
||||||
|
version - 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
weaselab::ConflictSet::Result result[sizeof(reads) / sizeof(reads[0])];
|
||||||
|
cs->check(reads, result, sizeof(reads) / sizeof(reads[0]));
|
||||||
|
// for (int i = 0; i < sizeof(reads) / sizeof(reads[0]); ++i) {
|
||||||
|
// if (result[i] != weaselab::ConflictSet::Commit) {
|
||||||
|
// fprintf(stderr, "Unexpected conflict: [%s, %s) @ %" PRId64 "\n",
|
||||||
|
// printable(reads[i].begin).c_str(),
|
||||||
|
// printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
|
// abort();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// Writes
|
||||||
|
{
|
||||||
|
weaselab::ConflictSet::WriteRange w;
|
||||||
|
auto k = numToKey(version);
|
||||||
|
w.begin.p = k.data();
|
||||||
|
w.end.len = 0;
|
||||||
|
if (version % (kWindowSize / 2) == 0) {
|
||||||
|
for (int l = 0; l <= k.size(); ++l) {
|
||||||
|
w.begin.len = l;
|
||||||
|
cs->addWrites(&w, 1, version);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.begin.len = k.size();
|
||||||
|
cs->addWrites(&w, 1, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// GC
|
||||||
|
cs->setOldestVersion(version - kWindowSize);
|
||||||
|
++version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from getaddrinfo man page
|
||||||
|
int getListenFd(const char *node, const char *service) {
|
||||||
|
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct addrinfo *result, *rp;
|
||||||
|
int sfd, s;
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||||
|
hints.ai_socktype = SOCK_STREAM; /* stream socket */
|
||||||
|
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
|
||||||
|
hints.ai_protocol = 0; /* Any protocol */
|
||||||
|
hints.ai_canonname = nullptr;
|
||||||
|
hints.ai_addr = nullptr;
|
||||||
|
hints.ai_next = nullptr;
|
||||||
|
|
||||||
|
s = getaddrinfo(node, service, &hints, &result);
|
||||||
|
if (s != 0) {
|
||||||
|
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getaddrinfo() returns a list of address structures.
|
||||||
|
Try each address until we successfully bind(2).
|
||||||
|
If socket(2) (or bind(2)) fails, we (close the socket
|
||||||
|
and) try the next address. */
|
||||||
|
|
||||||
|
for (rp = result; rp != nullptr; rp = rp->ai_next) {
|
||||||
|
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||||
|
if (sfd == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = 1;
|
||||||
|
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
||||||
|
|
||||||
|
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {
|
||||||
|
break; /* Success */
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result); /* No longer needed */
|
||||||
|
|
||||||
|
if (rp == nullptr) { /* No address succeeded */
|
||||||
|
fprintf(stderr, "Could not bind\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
int rv = listen(sfd, SOMAXCONN);
|
||||||
|
if (rv) {
|
||||||
|
perror("listen()");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP response
|
||||||
|
//
|
||||||
|
std::string_view part1 =
|
||||||
|
"HTTP/1.1 200 OK \r\nContent-type: text/plain; version=0.0.4; "
|
||||||
|
"charset=utf-8; escaping=values\r\nContent-Length: ";
|
||||||
|
// Decimal content length
|
||||||
|
std::string_view part2 = "\r\n\r\n";
|
||||||
|
// Body
|
||||||
|
|
||||||
|
double toSeconds(timeval t) {
|
||||||
|
return double(t.tv_sec) + double(t.tv_usec) * 1e-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc != 3) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int listenFd = getListenFd(argv[1], argv[2]);
|
||||||
|
|
||||||
|
weaselab::ConflictSet cs{0};
|
||||||
|
weaselab::ConflictSet::MetricsV1 *metrics;
|
||||||
|
int metricsCount;
|
||||||
|
cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
|
||||||
|
auto w = std::thread{workload, &cs};
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct sockaddr_storage peer_addr = {};
|
||||||
|
socklen_t peer_addr_len = sizeof(peer_addr);
|
||||||
|
const int connfd =
|
||||||
|
accept(listenFd, (struct sockaddr *)&peer_addr, &peer_addr_len);
|
||||||
|
|
||||||
|
std::string body;
|
||||||
|
|
||||||
|
rusage r;
|
||||||
|
getrusage(RUSAGE_SELF, &r);
|
||||||
|
body += "# HELP process_cpu_seconds_total Total user and system CPU time "
|
||||||
|
"spent in seconds.\n# TYPE process_cpu_seconds_total counter\n"
|
||||||
|
"process_cpu_seconds_total ";
|
||||||
|
body += std::to_string(toSeconds(r.ru_utime) + toSeconds(r.ru_stime));
|
||||||
|
body += "\n";
|
||||||
|
body += "# HELP process_resident_memory_bytes Resident memory size in "
|
||||||
|
"bytes.\n# TYPE process_resident_memory_bytes gauge\n"
|
||||||
|
"process_resident_memory_bytes ";
|
||||||
|
body += std::to_string(getCurrentRSS());
|
||||||
|
body += "\n";
|
||||||
|
body += "# HELP transactions_total Total number of transactions\n"
|
||||||
|
"# TYPE transactions_total counter\n"
|
||||||
|
"transactions_total ";
|
||||||
|
body += std::to_string(transactions.load(std::memory_order_relaxed));
|
||||||
|
body += "\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
|
body += "# HELP ";
|
||||||
|
body += metrics[i].name;
|
||||||
|
body += " ";
|
||||||
|
body += metrics[i].help;
|
||||||
|
body += "\n";
|
||||||
|
body += "# TYPE ";
|
||||||
|
body += metrics[i].name;
|
||||||
|
body += " ";
|
||||||
|
body += metrics[i].type == metrics[i].Counter ? "counter" : "gauge";
|
||||||
|
body += "\n";
|
||||||
|
body += metrics[i].name;
|
||||||
|
body += " ";
|
||||||
|
body += std::to_string(metrics[i].getValue());
|
||||||
|
body += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto len = std::to_string(body.size());
|
||||||
|
iovec iov[] = {
|
||||||
|
{(void *)part1.data(), part1.size()},
|
||||||
|
{(void *)len.data(), len.size()},
|
||||||
|
{(void *)part2.data(), part2.size()},
|
||||||
|
{(void *)body.data(), body.size()},
|
||||||
|
};
|
||||||
|
int written;
|
||||||
|
do {
|
||||||
|
written = writev(connfd, iov, sizeof(iov) / sizeof(iov[0]));
|
||||||
|
} while (written < 0 && errno == EINTR);
|
||||||
|
close(connfd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
fprintf(stderr, "Expected ./%s <host> <port>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/01c2a61d2b6e7fe40b38e46287afd69e01e52408
Normal file
BIN
corpus/01c2a61d2b6e7fe40b38e46287afd69e01e52408
Normal file
Binary file not shown.
BIN
corpus/027eeb2ee154aca4151298a6c31f3b80f58d3fae
Normal file
BIN
corpus/027eeb2ee154aca4151298a6c31f3b80f58d3fae
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/02ce0b03d4e732ab6e7b264ccce3ea77d3787e5f
Normal file
BIN
corpus/02ce0b03d4e732ab6e7b264ccce3ea77d3787e5f
Normal file
Binary file not shown.
BIN
corpus/03feca4da73a0c72f67bbaee135d10aec44b68ed
Normal file
BIN
corpus/03feca4da73a0c72f67bbaee135d10aec44b68ed
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/04b685b62d0428575c4b780e5aa5746a7b3c03c0
Normal file
BIN
corpus/04b685b62d0428575c4b780e5aa5746a7b3c03c0
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0752e603296a3adaec12746ba9d09a39796be8a1
Normal file
BIN
corpus/0752e603296a3adaec12746ba9d09a39796be8a1
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0838105dab8b8b9912d520ecd3910d104a3cba4e
Normal file
BIN
corpus/0838105dab8b8b9912d520ecd3910d104a3cba4e
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0879e0de6c13b47a0af9883ae76f839cd1527336
Normal file
BIN
corpus/0879e0de6c13b47a0af9883ae76f839cd1527336
Normal file
Binary file not shown.
BIN
corpus/0959377018632d4e8f772d3da38229e91918f1e0
Normal file
BIN
corpus/0959377018632d4e8f772d3da38229e91918f1e0
Normal file
Binary file not shown.
BIN
corpus/0964feb74dbe0ec93d02d17bce02f550b3dd72b1
Normal file
BIN
corpus/0964feb74dbe0ec93d02d17bce02f550b3dd72b1
Normal file
Binary file not shown.
BIN
corpus/09e805380a8b9e956017d65d48add2e0264c3613
Normal file
BIN
corpus/09e805380a8b9e956017d65d48add2e0264c3613
Normal file
Binary file not shown.
BIN
corpus/09fb712b33e82457001200373bfaba282c64647e
Normal file
BIN
corpus/09fb712b33e82457001200373bfaba282c64647e
Normal file
Binary file not shown.
BIN
corpus/0a04d2659955ca0470906f41a62f87f1c32fc3f1
Normal file
BIN
corpus/0a04d2659955ca0470906f41a62f87f1c32fc3f1
Normal file
Binary file not shown.
BIN
corpus/0a08831927d30683bb7c636b98d4f11178133550
Normal file
BIN
corpus/0a08831927d30683bb7c636b98d4f11178133550
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0c136f7f6d3f5f13c3279e9d4f1fbe96df6107fc
Normal file
BIN
corpus/0c136f7f6d3f5f13c3279e9d4f1fbe96df6107fc
Normal file
Binary file not shown.
BIN
corpus/0d65e979b921d29db29996214ba9ada06760214d
Normal file
BIN
corpus/0d65e979b921d29db29996214ba9ada06760214d
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/0efd7398293b9926eab2486d2e3bdb4fa4741bc3
Normal file
BIN
corpus/0efd7398293b9926eab2486d2e3bdb4fa4741bc3
Normal file
Binary file not shown.
BIN
corpus/0f4cc5b5da82090c3c93ace9dd99043d309969cf
Normal file
BIN
corpus/0f4cc5b5da82090c3c93ace9dd99043d309969cf
Normal file
Binary file not shown.
@@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
<EFBFBD><EFBFBD>
|
|
||||||
|
|
||||||
|
|
||||||
2
|
|
Binary file not shown.
Binary file not shown.
BIN
corpus/0f8e595cfa8b91f008d5bafa0d13187bc4692aa5
Normal file
BIN
corpus/0f8e595cfa8b91f008d5bafa0d13187bc4692aa5
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/0fa0e8462497fe8f56774c6f9569131e0d78dea1
Normal file
BIN
corpus/0fa0e8462497fe8f56774c6f9569131e0d78dea1
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/10817b7422a5e55a5507ae2cc6c79924c4e91e86
Normal file
BIN
corpus/10817b7422a5e55a5507ae2cc6c79924c4e91e86
Normal file
Binary file not shown.
BIN
corpus/10a19953b8e4e320ccac6f71ab44442ee75b1d3a
Normal file
BIN
corpus/10a19953b8e4e320ccac6f71ab44442ee75b1d3a
Normal file
Binary file not shown.
Binary file not shown.
BIN
corpus/11503ceb0f64208cd4c98ed7b6bf70a4f0a604de
Normal file
BIN
corpus/11503ceb0f64208cd4c98ed7b6bf70a4f0a604de
Normal file
Binary file not shown.
BIN
corpus/1172a9bde2deff2cbe3fcd4628d925d962991359
Normal file
BIN
corpus/1172a9bde2deff2cbe3fcd4628d925d962991359
Normal file
Binary file not shown.
BIN
corpus/11c36378ed15fb4fc147f503b5c6b6284bffbbd6
Normal file
BIN
corpus/11c36378ed15fb4fc147f503b5c6b6284bffbbd6
Normal file
Binary file not shown.
BIN
corpus/120347a5591760d3208403325dfbaf9680c8c811
Normal file
BIN
corpus/120347a5591760d3208403325dfbaf9680c8c811
Normal file
Binary file not shown.
BIN
corpus/1258c23ab656c67943a1a29fa53326f1265e3161
Normal file
BIN
corpus/1258c23ab656c67943a1a29fa53326f1265e3161
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
<EFBFBD>
|
|
BIN
corpus/130005d6d89fab7ba74ec6dcf63fc2743e90e99c
Normal file
BIN
corpus/130005d6d89fab7ba74ec6dcf63fc2743e90e99c
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/13758922ea9cacbc9e60102e6031b6bf642a9555
Normal file
BIN
corpus/13758922ea9cacbc9e60102e6031b6bf642a9555
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/14bddb239b44cc23bb5a83123f3f695f99bddee2
Normal file
BIN
corpus/14bddb239b44cc23bb5a83123f3f695f99bddee2
Normal file
Binary file not shown.
BIN
corpus/15af00b4a5e1a8994cebe6d73e81061fc0a1c83f
Normal file
BIN
corpus/15af00b4a5e1a8994cebe6d73e81061fc0a1c83f
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/183fc0b217e29efc19cc7f5502de35f318210a6d
Normal file
BIN
corpus/183fc0b217e29efc19cc7f5502de35f318210a6d
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/18d578b77571b863bbea95a471524d44918aa9be
Normal file
BIN
corpus/18d578b77571b863bbea95a471524d44918aa9be
Normal file
Binary file not shown.
BIN
corpus/18d7130f6c6ce63b91b26659568b171ab4c144e1
Normal file
BIN
corpus/18d7130f6c6ce63b91b26659568b171ab4c144e1
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1d7c8f7811dbf57cceed7e487ad641f2f081d4d5
Normal file
BIN
corpus/1d7c8f7811dbf57cceed7e487ad641f2f081d4d5
Normal file
Binary file not shown.
BIN
corpus/1dcb5faae22e59490de6347d65470be6e0721d04
Normal file
BIN
corpus/1dcb5faae22e59490de6347d65470be6e0721d04
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
corpus/1ff6ebb7ed73f269f8698ade75bfd0050969b4fe
Normal file
BIN
corpus/1ff6ebb7ed73f269f8698ade75bfd0050969b4fe
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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