Compare commits
5 Commits
5580f9b71d
...
tagged-poi
Author | SHA1 | Date | |
---|---|---|---|
af1e2299de | |||
230e96063d | |||
f41a62471b | |||
d8f85dedc4 | |||
656939560b |
@@ -358,6 +358,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||
set_target_properties(server_bench PROPERTIES SKIP_BUILD_RPATH ON)
|
||||
|
||||
add_executable(interleaving_test InterleavingTest.cpp)
|
||||
# work around lack of musttail for gcc
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_options(interleaving_test PRIVATE -Og
|
||||
-foptimize-sibling-calls)
|
||||
endif()
|
||||
target_link_libraries(interleaving_test PRIVATE nanobench)
|
||||
endif()
|
||||
|
||||
|
164
ConflictSet.cpp
164
ConflictSet.cpp
@@ -203,6 +203,60 @@ enum Type : int8_t {
|
||||
|
||||
template <class T> struct BoundedFreeListAllocator;
|
||||
|
||||
struct TaggedNodePointer {
|
||||
TaggedNodePointer() = default;
|
||||
operator struct Node *() { return (struct Node *)(uintptr_t)p; }
|
||||
operator struct Node0 *() {
|
||||
assert(t == Type_Node0);
|
||||
return (struct Node0 *)(uintptr_t)p;
|
||||
}
|
||||
operator struct Node3 *() {
|
||||
assert(t == Type_Node3);
|
||||
return (struct Node3 *)(uintptr_t)p;
|
||||
}
|
||||
operator struct Node16 *() {
|
||||
assert(t == Type_Node16);
|
||||
return (struct Node16 *)(uintptr_t)p;
|
||||
}
|
||||
operator struct Node48 *() {
|
||||
assert(t == Type_Node48);
|
||||
return (struct Node48 *)(uintptr_t)p;
|
||||
}
|
||||
operator struct Node256 *() {
|
||||
assert(t == Type_Node256);
|
||||
return (struct Node256 *)(uintptr_t)p;
|
||||
}
|
||||
/*implicit*/ TaggedNodePointer(std::nullptr_t) : p(0), t(Type_Node0) {}
|
||||
/*implicit*/ TaggedNodePointer(Node0 *x)
|
||||
: TaggedNodePointer((struct Node *)x, Type_Node0) {}
|
||||
/*implicit*/ TaggedNodePointer(Node3 *x)
|
||||
: TaggedNodePointer((struct Node *)x, Type_Node3) {}
|
||||
/*implicit*/ TaggedNodePointer(Node16 *x)
|
||||
: TaggedNodePointer((struct Node *)x, Type_Node16) {}
|
||||
/*implicit*/ TaggedNodePointer(Node48 *x)
|
||||
: TaggedNodePointer((struct Node *)x, Type_Node48) {}
|
||||
/*implicit*/ TaggedNodePointer(Node256 *x)
|
||||
: TaggedNodePointer((struct Node *)x, Type_Node256) {}
|
||||
|
||||
bool operator!=(std::nullptr_t) { return p != 0 || t != Type_Node0; }
|
||||
bool operator==(std::nullptr_t) { return p == 0 && t == Type_Node0; }
|
||||
bool operator==(const TaggedNodePointer &) const = default;
|
||||
bool operator==(Node *n) const { return (uintptr_t)n == p; }
|
||||
Node *operator->() { return (Node *)(uintptr_t)p; }
|
||||
Type getType() { return t; }
|
||||
|
||||
TaggedNodePointer(const TaggedNodePointer &) = default;
|
||||
TaggedNodePointer &operator=(const TaggedNodePointer &) = default;
|
||||
/*implicit*/ TaggedNodePointer(Node *n);
|
||||
|
||||
private:
|
||||
TaggedNodePointer(struct Node *p, Type t) : p((uintptr_t)p), t(t) {
|
||||
assume(p != 0);
|
||||
}
|
||||
uintptr_t p : 56;
|
||||
Type t : 8;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
|
||||
/* begin section that's copied to the next node */
|
||||
@@ -228,6 +282,9 @@ private:
|
||||
int32_t partialKeyCapacity;
|
||||
};
|
||||
|
||||
TaggedNodePointer::TaggedNodePointer(Node *n)
|
||||
: TaggedNodePointer(n, n->getType()) {}
|
||||
|
||||
constexpr int kNodeCopyBegin = offsetof(Node, entry);
|
||||
constexpr int kNodeCopySize =
|
||||
offsetof(Node, parentsIndex) + sizeof(Node::parentsIndex) - kNodeCopyBegin;
|
||||
@@ -251,7 +308,7 @@ struct Node3 : Node {
|
||||
constexpr static auto kMaxNodes = 3;
|
||||
constexpr static auto kType = Type_Node3;
|
||||
|
||||
Node *children[kMaxNodes];
|
||||
TaggedNodePointer children[kMaxNodes];
|
||||
InternalVersionT childMaxVersion[kMaxNodes];
|
||||
// Sorted
|
||||
uint8_t index[kMaxNodes];
|
||||
@@ -267,7 +324,7 @@ struct Node16 : Node {
|
||||
constexpr static auto kType = Type_Node16;
|
||||
constexpr static auto kMaxNodes = 16;
|
||||
|
||||
Node *children[kMaxNodes];
|
||||
TaggedNodePointer children[kMaxNodes];
|
||||
InternalVersionT childMaxVersion[kMaxNodes];
|
||||
// Sorted
|
||||
uint8_t index[kMaxNodes];
|
||||
@@ -288,7 +345,7 @@ struct Node48 : Node {
|
||||
constexpr static int kMaxOfMaxTotalPages = kMaxNodes / kMaxOfMaxPageSize;
|
||||
|
||||
BitSet bitSet;
|
||||
Node *children[kMaxNodes];
|
||||
TaggedNodePointer children[kMaxNodes];
|
||||
InternalVersionT childMaxVersion[kMaxNodes];
|
||||
InternalVersionT maxOfMax[kMaxOfMaxTotalPages];
|
||||
uint8_t reverseIndex[kMaxNodes];
|
||||
@@ -310,7 +367,7 @@ struct Node256 : Node {
|
||||
constexpr static int kMaxOfMaxTotalPages = kMaxNodes / kMaxOfMaxPageSize;
|
||||
|
||||
BitSet bitSet;
|
||||
Node *children[kMaxNodes];
|
||||
TaggedNodePointer children[kMaxNodes];
|
||||
InternalVersionT childMaxVersion[kMaxNodes];
|
||||
InternalVersionT maxOfMax[kMaxOfMaxTotalPages];
|
||||
|
||||
@@ -888,26 +945,26 @@ int getNodeIndexExists(Node16 *self, uint8_t index) {
|
||||
}
|
||||
|
||||
// Precondition - an entry for index must exist in the node
|
||||
Node *&getChildExists(Node3 *self, uint8_t index) {
|
||||
TaggedNodePointer &getChildExists(Node3 *self, uint8_t index) {
|
||||
return self->children[getNodeIndexExists(self, index)];
|
||||
}
|
||||
// Precondition - an entry for index must exist in the node
|
||||
Node *&getChildExists(Node16 *self, uint8_t index) {
|
||||
TaggedNodePointer &getChildExists(Node16 *self, uint8_t index) {
|
||||
return self->children[getNodeIndexExists(self, index)];
|
||||
}
|
||||
// Precondition - an entry for index must exist in the node
|
||||
Node *&getChildExists(Node48 *self, uint8_t index) {
|
||||
TaggedNodePointer &getChildExists(Node48 *self, uint8_t index) {
|
||||
assert(self->bitSet.test(index));
|
||||
return self->children[self->index[index]];
|
||||
}
|
||||
// Precondition - an entry for index must exist in the node
|
||||
Node *&getChildExists(Node256 *self, uint8_t index) {
|
||||
TaggedNodePointer &getChildExists(Node256 *self, uint8_t index) {
|
||||
assert(self->bitSet.test(index));
|
||||
return self->children[index];
|
||||
}
|
||||
|
||||
// Precondition - an entry for index must exist in the node
|
||||
Node *&getChildExists(Node *self, uint8_t index) {
|
||||
TaggedNodePointer &getChildExists(Node *self, uint8_t index) {
|
||||
switch (self->getType()) {
|
||||
case Type_Node0: // GCOVR_EXCL_LINE
|
||||
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||
@@ -1038,7 +1095,7 @@ void setMaxVersion(Node *n, InternalVersionT newMax) {
|
||||
}
|
||||
}
|
||||
|
||||
Node *&getInTree(Node *n, ConflictSet::Impl *);
|
||||
TaggedNodePointer &getInTree(Node *n, ConflictSet::Impl *);
|
||||
|
||||
Node *getChild(Node0 *, uint8_t) { return nullptr; }
|
||||
Node *getChild(Node3 *self, uint8_t index) {
|
||||
@@ -1075,6 +1132,7 @@ Node *getChild(Node *self, uint8_t index) {
|
||||
struct ChildAndMaxVersion {
|
||||
Node *child;
|
||||
InternalVersionT maxVersion;
|
||||
Type childType;
|
||||
};
|
||||
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node0 *, uint8_t) { return {}; }
|
||||
@@ -1083,24 +1141,28 @@ ChildAndMaxVersion getChildAndMaxVersion(Node3 *self, uint8_t index) {
|
||||
if (i < 0) {
|
||||
return {};
|
||||
}
|
||||
return {self->children[i], self->childMaxVersion[i]};
|
||||
return {self->children[i], self->childMaxVersion[i],
|
||||
self->children[i].getType()};
|
||||
}
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node16 *self, uint8_t index) {
|
||||
int i = getNodeIndex(self, index);
|
||||
if (i < 0) {
|
||||
return {};
|
||||
}
|
||||
return {self->children[i], self->childMaxVersion[i]};
|
||||
return {self->children[i], self->childMaxVersion[i],
|
||||
self->children[i].getType()};
|
||||
}
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node48 *self, uint8_t index) {
|
||||
int i = self->index[index];
|
||||
if (i < 0) {
|
||||
return {};
|
||||
}
|
||||
return {self->children[i], self->childMaxVersion[i]};
|
||||
return {self->children[i], self->childMaxVersion[i],
|
||||
self->children[i].getType()};
|
||||
}
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node256 *self, uint8_t index) {
|
||||
return {self->children[index], self->childMaxVersion[index]};
|
||||
return {self->children[index], self->childMaxVersion[index],
|
||||
self->children[index].getType()};
|
||||
}
|
||||
|
||||
ChildAndMaxVersion getChildAndMaxVersion(Node *self, uint8_t index) {
|
||||
@@ -1245,14 +1307,15 @@ Node *getFirstChildExists(Node *self) {
|
||||
}
|
||||
}
|
||||
|
||||
void consumePartialKeyFull(Node *&self, std::span<const uint8_t> &key,
|
||||
void consumePartialKeyFull(TaggedNodePointer &self,
|
||||
std::span<const uint8_t> &key,
|
||||
InternalVersionT writeVersion, WriteContext *tls) {
|
||||
// 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;
|
||||
Node *old = self;
|
||||
// Since root cannot have a partial key
|
||||
assert(old->parent != nullptr);
|
||||
InternalVersionT oldMaxVersion = exchangeMaxVersion(old, writeVersion);
|
||||
@@ -1290,7 +1353,7 @@ void consumePartialKeyFull(Node *&self, std::span<const uint8_t> &key,
|
||||
// Consume any partial key of `self`, and update `self` and
|
||||
// `key` such that `self` is along the search path of `key`
|
||||
inline __attribute__((always_inline)) void
|
||||
consumePartialKey(Node *&self, std::span<const uint8_t> &key,
|
||||
consumePartialKey(TaggedNodePointer &self, std::span<const uint8_t> &key,
|
||||
InternalVersionT writeVersion, WriteContext *tls) {
|
||||
if (self->partialKeyLen > 0) {
|
||||
consumePartialKeyFull(self, key, writeVersion, tls);
|
||||
@@ -1301,8 +1364,10 @@ consumePartialKey(Node *&self, std::span<const uint8_t> &key,
|
||||
// such that the search path of the result + key is the same as the search path
|
||||
// of self + key before the call. Creates a node if necessary. Updates
|
||||
// `maxVersion` for result.
|
||||
Node *&getOrCreateChild(Node *&self, std::span<const uint8_t> &key,
|
||||
InternalVersionT newMaxVersion, WriteContext *tls) {
|
||||
TaggedNodePointer &getOrCreateChild(TaggedNodePointer &self,
|
||||
std::span<const uint8_t> &key,
|
||||
InternalVersionT newMaxVersion,
|
||||
WriteContext *tls) {
|
||||
|
||||
int index = key.front();
|
||||
key = key.subspan(1, key.size() - 1);
|
||||
@@ -1660,10 +1725,11 @@ void rezero(Node *n, InternalVersionT z) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void mergeWithChild(Node *&self, WriteContext *tls, ConflictSet::Impl *impl,
|
||||
Node *&dontInvalidate, Node3 *self3) {
|
||||
void mergeWithChild(TaggedNodePointer &self, WriteContext *tls,
|
||||
ConflictSet::Impl *impl, Node *&dontInvalidate,
|
||||
Node3 *self3) {
|
||||
assert(!self3->entryPresent);
|
||||
auto *child = self3->children[0];
|
||||
Node *child = self3->children[0];
|
||||
int minCapacity = self3->partialKeyLen + 1 + child->partialKeyLen;
|
||||
|
||||
if (minCapacity > child->getCapacity()) {
|
||||
@@ -1912,7 +1978,7 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
||||
goto downLeftSpine;
|
||||
}
|
||||
|
||||
auto [child, maxV] = getChildAndMaxVersion(n, remaining[0]);
|
||||
auto [child, maxV, childT] = getChildAndMaxVersion(n, remaining[0]);
|
||||
if (child == nullptr) {
|
||||
auto c = getChildGeq(n, remaining[0]);
|
||||
if (c != nullptr) {
|
||||
@@ -1982,7 +2048,7 @@ bool checkPrefixRead(Node *n, const std::span<const uint8_t> key,
|
||||
return maxVersion(n) <= readVersion;
|
||||
}
|
||||
|
||||
auto [child, maxV] = getChildAndMaxVersion(n, remaining[0]);
|
||||
auto [child, maxV, childT] = getChildAndMaxVersion(n, remaining[0]);
|
||||
if (child == nullptr) {
|
||||
auto c = getChildGeq(n, remaining[0]);
|
||||
if (c != nullptr) {
|
||||
@@ -2255,7 +2321,7 @@ bool checkMaxBetweenExclusiveImpl(Node *n, int begin, int end,
|
||||
if (!mask) {
|
||||
return true;
|
||||
}
|
||||
auto *child = self->children[std::countr_zero(mask)];
|
||||
Node *child = self->children[std::countr_zero(mask)];
|
||||
const bool firstRangeOk =
|
||||
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||
uint32_t compared = 0;
|
||||
@@ -2330,7 +2396,7 @@ bool checkMaxBetweenExclusiveImpl(Node *n, int begin, int end,
|
||||
if (!mask) {
|
||||
return true;
|
||||
}
|
||||
auto *child = self->children[std::countr_zero(mask)];
|
||||
Node *child = self->children[std::countr_zero(mask)];
|
||||
const bool firstRangeOk =
|
||||
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||
|
||||
@@ -2375,7 +2441,7 @@ bool checkMaxBetweenExclusiveImpl(Node *n, int begin, int end,
|
||||
{
|
||||
int c = self->bitSet.firstSetGeq(begin + 1);
|
||||
if (c >= 0 && c < end) {
|
||||
auto *child = self->children[self->index[c]];
|
||||
Node *child = self->children[self->index[c]];
|
||||
if (child->entryPresent && child->entry.rangeVersion > readVersion) {
|
||||
return false;
|
||||
}
|
||||
@@ -2409,7 +2475,7 @@ bool checkMaxBetweenExclusiveImpl(Node *n, int begin, int end,
|
||||
{
|
||||
int c = self->bitSet.firstSetGeq(begin + 1);
|
||||
if (c >= 0 && c < end) {
|
||||
auto *child = self->children[c];
|
||||
Node *child = self->children[c];
|
||||
if (child->entryPresent && child->entry.rangeVersion > readVersion) {
|
||||
return false;
|
||||
}
|
||||
@@ -2581,7 +2647,7 @@ bool checkRangeLeftSide(Node *n, std::span<const uint8_t> key, int prefixLen,
|
||||
}
|
||||
}
|
||||
|
||||
auto [child, maxV] = getChildAndMaxVersion(n, remaining[0]);
|
||||
auto [child, maxV, childT] = getChildAndMaxVersion(n, remaining[0]);
|
||||
if (child == nullptr) {
|
||||
auto c = getChildGeq(n, remaining[0]);
|
||||
if (c != nullptr) {
|
||||
@@ -2769,7 +2835,7 @@ bool checkRangeRead(Node *n, std::span<const uint8_t> begin,
|
||||
if (remaining.size() == 0) {
|
||||
break;
|
||||
}
|
||||
auto [child, v] = getChildAndMaxVersion(n, remaining[0]);
|
||||
auto [child, v, childT] = getChildAndMaxVersion(n, remaining[0]);
|
||||
if (child == nullptr) {
|
||||
break;
|
||||
}
|
||||
@@ -2835,8 +2901,10 @@ checkMaxBetweenExclusiveImpl<true>(Node *n, int begin, int end,
|
||||
// of the result will have `maxVersion` set to `writeVersion` as a
|
||||
// postcondition. Nodes along the search path may be invalidated. Callers must
|
||||
// ensure that the max version of the self argument is updated.
|
||||
[[nodiscard]] Node **insert(Node **self, std::span<const uint8_t> key,
|
||||
InternalVersionT writeVersion, WriteContext *tls) {
|
||||
[[nodiscard]] TaggedNodePointer *insert(TaggedNodePointer *self,
|
||||
std::span<const uint8_t> key,
|
||||
InternalVersionT writeVersion,
|
||||
WriteContext *tls) {
|
||||
|
||||
for (; key.size() != 0; ++tls->accum.insert_iterations) {
|
||||
self = &getOrCreateChild(*self, key, writeVersion, tls);
|
||||
@@ -2864,17 +2932,23 @@ void eraseTree(Node *root, WriteContext *tls) {
|
||||
} break;
|
||||
case Type_Node3: {
|
||||
auto *n3 = static_cast<Node3 *>(n);
|
||||
toFree.append(std::span<Node *>(n3->children, n3->numChildren));
|
||||
for (int i = 0; i < n3->numChildren; ++i) {
|
||||
toFree.push_back(n3->children[i]);
|
||||
}
|
||||
tls->release(n3);
|
||||
} break;
|
||||
case Type_Node16: {
|
||||
auto *n16 = static_cast<Node16 *>(n);
|
||||
toFree.append(std::span<Node *>(n16->children, n16->numChildren));
|
||||
for (int i = 0; i < n16->numChildren; ++i) {
|
||||
toFree.push_back(n16->children[i]);
|
||||
}
|
||||
tls->release(n16);
|
||||
} break;
|
||||
case Type_Node48: {
|
||||
auto *n48 = static_cast<Node48 *>(n);
|
||||
toFree.append(std::span<Node *>(n48->children, n48->numChildren));
|
||||
for (int i = 0; i < n48->numChildren; ++i) {
|
||||
toFree.push_back(n48->children[i]);
|
||||
}
|
||||
tls->release(n48);
|
||||
} break;
|
||||
case Type_Node256: {
|
||||
@@ -2890,10 +2964,10 @@ void eraseTree(Node *root, WriteContext *tls) {
|
||||
}
|
||||
}
|
||||
|
||||
void addPointWrite(Node *&root, std::span<const uint8_t> key,
|
||||
void addPointWrite(TaggedNodePointer &root, std::span<const uint8_t> key,
|
||||
InternalVersionT writeVersion, WriteContext *tls) {
|
||||
++tls->accum.point_writes;
|
||||
auto *n = *insert(&root, key, writeVersion, tls);
|
||||
auto n = *insert(&root, key, writeVersion, tls);
|
||||
if (!n->entryPresent) {
|
||||
++tls->accum.entries_inserted;
|
||||
auto *p = nextLogical(n);
|
||||
@@ -2957,7 +3031,7 @@ horizontalMax16(InternalVersionT *vs, [[maybe_unused]] InternalVersionT z) {
|
||||
}
|
||||
return max;
|
||||
#else
|
||||
uint32_t zero;
|
||||
uint32_t zero; // GCOVR_EXCL_LINE
|
||||
memcpy(&zero, &z, sizeof(zero));
|
||||
auto zeroVec = _mm512_set1_epi32(zero);
|
||||
return InternalVersionT(zero + _mm512_reduce_max_epi32(_mm512_sub_epi32(
|
||||
@@ -3011,7 +3085,7 @@ void fixupMaxVersion(Node *node, WriteContext *tls) {
|
||||
setMaxVersion(node, max);
|
||||
}
|
||||
|
||||
void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
||||
void addWriteRange(TaggedNodePointer &root, std::span<const uint8_t> begin,
|
||||
std::span<const uint8_t> end, InternalVersionT writeVersion,
|
||||
WriteContext *tls, ConflictSet::Impl *impl) {
|
||||
|
||||
@@ -3024,12 +3098,12 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
||||
++tls->accum.range_writes;
|
||||
const bool beginIsPrefix = lcp == int(begin.size());
|
||||
|
||||
Node **useAsRoot = insert(&root, begin.subspan(0, lcp), writeVersion, tls);
|
||||
auto useAsRoot = insert(&root, begin.subspan(0, lcp), writeVersion, tls);
|
||||
|
||||
begin = begin.subspan(lcp, begin.size() - lcp);
|
||||
end = end.subspan(lcp, end.size() - lcp);
|
||||
|
||||
auto *beginNode = *insert(useAsRoot, begin, writeVersion, tls);
|
||||
Node *beginNode = *insert(useAsRoot, begin, writeVersion, tls);
|
||||
addKey(beginNode);
|
||||
if (!beginNode->entryPresent) {
|
||||
++tls->accum.entries_inserted;
|
||||
@@ -3040,7 +3114,7 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
||||
}
|
||||
beginNode->entry.pointVersion = writeVersion;
|
||||
|
||||
auto *endNode = *insert(useAsRoot, end, writeVersion, tls);
|
||||
Node *endNode = *insert(useAsRoot, end, writeVersion, tls);
|
||||
addKey(endNode);
|
||||
if (!endNode->entryPresent) {
|
||||
++tls->accum.entries_inserted;
|
||||
@@ -3060,7 +3134,7 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
||||
assert(!beginNode->endOfRange);
|
||||
assert(!endNode->endOfRange);
|
||||
endNode->endOfRange = true;
|
||||
auto *iter = beginNode;
|
||||
Node *iter = beginNode;
|
||||
for (iter = nextLogical(iter); !iter->endOfRange;
|
||||
iter = erase(iter, tls, impl, /*logical*/ true)) {
|
||||
assert(!iter->endOfRange);
|
||||
@@ -3375,7 +3449,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
std::span<const uint8_t> removalKey;
|
||||
int64_t keyUpdates;
|
||||
|
||||
Node *root;
|
||||
TaggedNodePointer root;
|
||||
InternalVersionT oldestVersion;
|
||||
int64_t oldestVersionFullPrecision;
|
||||
int64_t oldestExtantVersion;
|
||||
@@ -3456,7 +3530,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
}
|
||||
};
|
||||
|
||||
Node *&getInTree(Node *n, ConflictSet::Impl *impl) {
|
||||
TaggedNodePointer &getInTree(Node *n, ConflictSet::Impl *impl) {
|
||||
return n->parent == nullptr ? impl->root
|
||||
: getChildExists(n->parent, n->parentsIndex);
|
||||
}
|
||||
|
@@ -22,9 +22,6 @@ void *stepJob(Job *j) {
|
||||
return done ? nullptr : (void *)stepJob;
|
||||
}
|
||||
|
||||
// So we can look at the disassembly more easily
|
||||
|
||||
extern "C" {
|
||||
void sequential(Job **jobs, int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
do {
|
||||
@@ -94,6 +91,87 @@ void interleaveBoundedCyclicList(Job **jobs, int count) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_attribute(musttail)
|
||||
#define MUSTTAIL __attribute__((musttail))
|
||||
#else
|
||||
#define MUSTTAIL
|
||||
#endif
|
||||
|
||||
struct Context {
|
||||
constexpr static int kConcurrent = 32;
|
||||
Job **jobs;
|
||||
Job *inProgress[kConcurrent];
|
||||
void (*continuation[kConcurrent])(Context *, int64_t prevJob, int64_t job,
|
||||
int64_t started, int64_t count);
|
||||
int nextJob[kConcurrent];
|
||||
};
|
||||
|
||||
void keepGoing(Context *context, int64_t prevJob, int64_t job, int64_t started,
|
||||
int64_t count) {
|
||||
prevJob = job;
|
||||
job = context->nextJob[job];
|
||||
MUSTTAIL return context->continuation[job](context, prevJob, job, started,
|
||||
count);
|
||||
}
|
||||
|
||||
void stepJobTailCall(Context *context, int64_t prevJob, int64_t job,
|
||||
int64_t started, int64_t count);
|
||||
|
||||
void complete(Context *context, int64_t prevJob, int64_t job, int64_t started,
|
||||
int64_t count) {
|
||||
if (started == count) {
|
||||
if (prevJob == job) {
|
||||
return;
|
||||
}
|
||||
context->nextJob[prevJob] = context->nextJob[job];
|
||||
job = prevJob;
|
||||
} else {
|
||||
context->inProgress[job] = context->jobs[started++];
|
||||
context->continuation[job] = stepJobTailCall;
|
||||
}
|
||||
prevJob = job;
|
||||
job = context->nextJob[job];
|
||||
MUSTTAIL return context->continuation[job](context, prevJob, job, started,
|
||||
count);
|
||||
}
|
||||
|
||||
void stepJobTailCall(Context *context, int64_t prevJob, int64_t job,
|
||||
int64_t started, int64_t count) {
|
||||
auto *j = context->inProgress[job];
|
||||
auto done = --(*j->input) == 0;
|
||||
#ifdef __x86_64__
|
||||
_mm_clflush(j->input);
|
||||
#endif
|
||||
if (done) {
|
||||
MUSTTAIL return complete(context, prevJob, job, started, count);
|
||||
} else {
|
||||
context->continuation[job] = stepJobTailCall;
|
||||
MUSTTAIL return keepGoing(context, prevJob, job, started, count);
|
||||
}
|
||||
}
|
||||
|
||||
void useTailCalls(Job **jobs, int count) {
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
Context context;
|
||||
context.jobs = jobs;
|
||||
int64_t started = std::min(Context::kConcurrent, count);
|
||||
for (int i = 0; i < started; i++) {
|
||||
context.inProgress[i] = jobs[i];
|
||||
context.nextJob[i] = i + 1;
|
||||
context.continuation[i] = stepJobTailCall;
|
||||
}
|
||||
context.nextJob[started - 1] = 0;
|
||||
int prevJob = started - 1;
|
||||
int job = 0;
|
||||
return context.continuation[job](&context, prevJob, job, started, count);
|
||||
}
|
||||
|
||||
void interleaveCyclicList(Job **jobs, int count) {
|
||||
auto *nextJob = (int *)alloca(sizeof(int) * count);
|
||||
|
||||
@@ -117,12 +195,11 @@ void interleaveCyclicList(Job **jobs, int count) {
|
||||
job = nextJob[job];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
ankerl::nanobench::Bench bench;
|
||||
|
||||
constexpr int kNumJobs = 100;
|
||||
constexpr int kNumJobs = 10000;
|
||||
bench.relative(true);
|
||||
|
||||
Job jobs[kNumJobs];
|
||||
@@ -140,6 +217,7 @@ int main() {
|
||||
for (auto [scheduler, name] :
|
||||
{std::make_pair(sequentialNoFuncPtr, "sequentialNoFuncPtr"),
|
||||
std::make_pair(sequential, "sequential"),
|
||||
std::make_pair(useTailCalls, "useTailCalls"),
|
||||
std::make_pair(interleaveSwapping, "interleavingSwapping"),
|
||||
std::make_pair(interleaveBoundedCyclicList,
|
||||
"interleaveBoundedCyclicList"),
|
||||
|
BIN
corpus/1d73c395d1885ce584ddc66ebea3137a89222307
Normal file
BIN
corpus/1d73c395d1885ce584ddc66ebea3137a89222307
Normal file
Binary file not shown.
BIN
corpus/33cfb152724161924444fe07e0cdb07b8b8462fc
Normal file
BIN
corpus/33cfb152724161924444fe07e0cdb07b8b8462fc
Normal file
Binary file not shown.
BIN
corpus/3dd78f16a8656e4839afb2eb4cdc7a028f4d28b8
Normal file
BIN
corpus/3dd78f16a8656e4839afb2eb4cdc7a028f4d28b8
Normal file
Binary file not shown.
BIN
corpus/9097c0a5f4a1a725413d8ac7b5a42c1c17b6eb31
Normal file
BIN
corpus/9097c0a5f4a1a725413d8ac7b5a42c1c17b6eb31
Normal file
Binary file not shown.
BIN
corpus/91bf7637038424fdff9a6fea9afc9166399284f7
Normal file
BIN
corpus/91bf7637038424fdff9a6fea9afc9166399284f7
Normal file
Binary file not shown.
BIN
corpus/a21decffaafb3e49c2f4984833f6cfe1d9d1d3ca
Normal file
BIN
corpus/a21decffaafb3e49c2f4984833f6cfe1d9d1d3ca
Normal file
Binary file not shown.
BIN
corpus/cc555c8529d6c31423947a7a4dcfdddfd713e5e7
Normal file
BIN
corpus/cc555c8529d6c31423947a7a4dcfdddfd713e5e7
Normal file
Binary file not shown.
BIN
corpus/d46a73ca220fd8462d7e07ddfd008721b682e9b1
Normal file
BIN
corpus/d46a73ca220fd8462d7e07ddfd008721b682e9b1
Normal file
Binary file not shown.
Reference in New Issue
Block a user