Implement insert
This commit is contained in:
110
VersionedMap.cpp
110
VersionedMap.cpp
@@ -59,6 +59,11 @@ void munmapSafe(void *ptr, size_t size) {
|
||||
|
||||
namespace weaselab {
|
||||
|
||||
// 96 is enough for an entire search path in a tree with a size that
|
||||
// overflows int. See
|
||||
// https://en.wikipedia.org/wiki/Random_binary_tree#The_longest_path
|
||||
constexpr int kPathLengthUpperBound = 96;
|
||||
|
||||
struct Entry {
|
||||
int64_t insertVersion;
|
||||
int keyLen;
|
||||
@@ -216,7 +221,9 @@ struct MemManager {
|
||||
void gc(const uint32_t *roots, int numRoots, int64_t oldestVersion) {
|
||||
// Calculate reachable set
|
||||
BitSet reachable{next};
|
||||
uint32_t stack[1000]; // Much more than bound imposed by max height of tree
|
||||
// Each node has at most 3 children and nodes along the search path aren't
|
||||
// in the stack, so we need 2 * kPathLengthUpperBound
|
||||
uint32_t stack[2 * kPathLengthUpperBound];
|
||||
int stackIndex = 0;
|
||||
auto tryPush = [&](uint32_t p) {
|
||||
if (!reachable.set(p)) {
|
||||
@@ -391,11 +398,37 @@ private:
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
auto operator<=>(const VersionedMap::Mutation &lhs, const Node &rhs) {
|
||||
int cl = std::min(lhs.param1Length, rhs.entry->keyLen);
|
||||
if (cl > 0) {
|
||||
int c = memcmp(lhs.param1, rhs.entry->getKey(), cl);
|
||||
if (c != 0) {
|
||||
return c <=> 0;
|
||||
}
|
||||
}
|
||||
return lhs.param1Length <=> rhs.entry->keyLen;
|
||||
}
|
||||
|
||||
struct Finger {
|
||||
void push(uint32_t node) { searchPath[searchPathSize_++] = node; }
|
||||
void pop() { --searchPathSize_; }
|
||||
uint32_t back() const {
|
||||
assert(searchPathSize_ > 0);
|
||||
return searchPath[searchPathSize_ - 1];
|
||||
}
|
||||
uint32_t searchPathSize() const { return searchPathSize_; }
|
||||
|
||||
private:
|
||||
uint32_t searchPath[kPathLengthUpperBound];
|
||||
int searchPathSize_ = 0;
|
||||
};
|
||||
|
||||
struct VersionedMap::Impl {
|
||||
|
||||
template <std::memory_order kOrder>
|
||||
uint32_t child(uint32_t node, bool which, int64_t at) {
|
||||
static_assert(kOrder == std::memory_order_acquire || kOrder == std::memory_order_relaxed);
|
||||
static_assert(kOrder == std::memory_order_acquire ||
|
||||
kOrder == std::memory_order_relaxed);
|
||||
auto &n = mm.base[node];
|
||||
if (n.updated.load(kOrder) && n.updateVersion <= at &&
|
||||
which == n.replacedPointer) {
|
||||
@@ -469,6 +502,60 @@ struct VersionedMap::Impl {
|
||||
at);
|
||||
}
|
||||
|
||||
void insert(const Mutation &m) {
|
||||
Finger finger;
|
||||
finger.push(latestRoot);
|
||||
bool directionStack[kPathLengthUpperBound];
|
||||
int directionStackSize = 0;
|
||||
|
||||
// Initialize finger to the search path of `m`
|
||||
for (;;) {
|
||||
auto n = finger.back();
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
auto c = m <=> mm.base[n];
|
||||
if (c == 0) {
|
||||
// No duplicates
|
||||
break;
|
||||
}
|
||||
finger.push(child<std::memory_order_relaxed>(n, c > 0, latestVersion));
|
||||
directionStack[directionStackSize++] = c > 0;
|
||||
}
|
||||
|
||||
// Prepare new node
|
||||
uint32_t node = newNode(latestVersion, m.param1, m.param1Length, m.param2,
|
||||
m.param2Length, false /* TODO set correctly */);
|
||||
if (finger.back() != 0) {
|
||||
auto &n = mm.base[node];
|
||||
n.pointer[0] =
|
||||
child<std::memory_order_relaxed>(finger.back(), false, latestVersion);
|
||||
n.pointer[1] =
|
||||
child<std::memory_order_relaxed>(finger.back(), true, latestVersion);
|
||||
}
|
||||
|
||||
// Rotate and propagate up the search path
|
||||
for (;;) {
|
||||
if (finger.searchPathSize() == 1) {
|
||||
// Made it to the root
|
||||
latestRoot = node;
|
||||
break;
|
||||
}
|
||||
finger.pop();
|
||||
auto parent = finger.back();
|
||||
const bool direction = directionStack[--directionStackSize];
|
||||
parent = update(parent, latestVersion, direction, node);
|
||||
if (mm.base[node].entry->priority > mm.base[parent].entry->priority) {
|
||||
rotate(parent, latestVersion, direction);
|
||||
} else {
|
||||
if (parent == finger.back()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t newNode(int64_t version, const uint8_t *key, int keyLen,
|
||||
const uint8_t *val, int valLen, bool clearTo) {
|
||||
auto result = mm.allocate();
|
||||
@@ -513,6 +600,9 @@ struct VersionedMap::Impl {
|
||||
|
||||
MemManager mm;
|
||||
RootSet roots;
|
||||
// Only meaningful within the callstack of `addMutations`
|
||||
uint32_t latestRoot;
|
||||
int64_t latestVersion = 0;
|
||||
};
|
||||
} // namespace weaselab
|
||||
|
||||
@@ -523,19 +613,21 @@ int main() {
|
||||
|
||||
{
|
||||
weaselab::VersionedMap::Impl impl;
|
||||
impl.roots.add(impl.newNode(1, (const uint8_t *)"a", 1, nullptr, 0, true),
|
||||
1);
|
||||
impl.roots.add(impl.newNode(2, (const uint8_t *)"b", 1, nullptr, -1, false),
|
||||
2);
|
||||
impl.roots.add(impl.newNode(3, (const uint8_t *)"c", 1, nullptr, -1, false),
|
||||
3);
|
||||
impl.latestRoot = impl.roots.roots()[impl.roots.rootCount() - 1];
|
||||
impl.latestVersion = 1;
|
||||
impl.insert(weaselab::VersionedMap::Mutation{
|
||||
(const uint8_t *)"c", nullptr, 1, 0, weaselab::VersionedMap::Set});
|
||||
impl.insert(weaselab::VersionedMap::Mutation{
|
||||
(const uint8_t *)"b", nullptr, 1, 0, weaselab::VersionedMap::Set});
|
||||
impl.insert(weaselab::VersionedMap::Mutation{
|
||||
(const uint8_t *)"a", nullptr, 1, 0, weaselab::VersionedMap::Set});
|
||||
impl.roots.add(impl.latestRoot, impl.latestVersion);
|
||||
impl.printInOrder(0);
|
||||
impl.printInOrder(1);
|
||||
impl.printInOrder(2);
|
||||
impl.printInOrder(3);
|
||||
impl.setOldestVersion(3);
|
||||
}
|
||||
return 0;
|
||||
|
||||
ankerl::nanobench::Bench bench;
|
||||
bench.minEpochIterations(5000);
|
||||
|
Reference in New Issue
Block a user