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 {
|
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 {
|
struct Entry {
|
||||||
int64_t insertVersion;
|
int64_t insertVersion;
|
||||||
int keyLen;
|
int keyLen;
|
||||||
@@ -216,7 +221,9 @@ struct MemManager {
|
|||||||
void gc(const uint32_t *roots, int numRoots, int64_t oldestVersion) {
|
void gc(const uint32_t *roots, int numRoots, int64_t oldestVersion) {
|
||||||
// Calculate reachable set
|
// Calculate reachable set
|
||||||
BitSet reachable{next};
|
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;
|
int stackIndex = 0;
|
||||||
auto tryPush = [&](uint32_t p) {
|
auto tryPush = [&](uint32_t p) {
|
||||||
if (!reachable.set(p)) {
|
if (!reachable.set(p)) {
|
||||||
@@ -391,11 +398,37 @@ private:
|
|||||||
uint32_t end;
|
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 {
|
struct VersionedMap::Impl {
|
||||||
|
|
||||||
template <std::memory_order kOrder>
|
template <std::memory_order kOrder>
|
||||||
uint32_t child(uint32_t node, bool which, int64_t at) {
|
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];
|
auto &n = mm.base[node];
|
||||||
if (n.updated.load(kOrder) && n.updateVersion <= at &&
|
if (n.updated.load(kOrder) && n.updateVersion <= at &&
|
||||||
which == n.replacedPointer) {
|
which == n.replacedPointer) {
|
||||||
@@ -469,6 +502,60 @@ struct VersionedMap::Impl {
|
|||||||
at);
|
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,
|
uint32_t newNode(int64_t version, const uint8_t *key, int keyLen,
|
||||||
const uint8_t *val, int valLen, bool clearTo) {
|
const uint8_t *val, int valLen, bool clearTo) {
|
||||||
auto result = mm.allocate();
|
auto result = mm.allocate();
|
||||||
@@ -513,6 +600,9 @@ struct VersionedMap::Impl {
|
|||||||
|
|
||||||
MemManager mm;
|
MemManager mm;
|
||||||
RootSet roots;
|
RootSet roots;
|
||||||
|
// Only meaningful within the callstack of `addMutations`
|
||||||
|
uint32_t latestRoot;
|
||||||
|
int64_t latestVersion = 0;
|
||||||
};
|
};
|
||||||
} // namespace weaselab
|
} // namespace weaselab
|
||||||
|
|
||||||
@@ -523,19 +613,21 @@ int main() {
|
|||||||
|
|
||||||
{
|
{
|
||||||
weaselab::VersionedMap::Impl impl;
|
weaselab::VersionedMap::Impl impl;
|
||||||
impl.roots.add(impl.newNode(1, (const uint8_t *)"a", 1, nullptr, 0, true),
|
impl.latestRoot = impl.roots.roots()[impl.roots.rootCount() - 1];
|
||||||
1);
|
impl.latestVersion = 1;
|
||||||
impl.roots.add(impl.newNode(2, (const uint8_t *)"b", 1, nullptr, -1, false),
|
impl.insert(weaselab::VersionedMap::Mutation{
|
||||||
2);
|
(const uint8_t *)"c", nullptr, 1, 0, weaselab::VersionedMap::Set});
|
||||||
impl.roots.add(impl.newNode(3, (const uint8_t *)"c", 1, nullptr, -1, false),
|
impl.insert(weaselab::VersionedMap::Mutation{
|
||||||
3);
|
(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(0);
|
||||||
impl.printInOrder(1);
|
impl.printInOrder(1);
|
||||||
impl.printInOrder(2);
|
impl.printInOrder(2);
|
||||||
impl.printInOrder(3);
|
impl.printInOrder(3);
|
||||||
impl.setOldestVersion(3);
|
impl.setOldestVersion(3);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
|
||||||
ankerl::nanobench::Bench bench;
|
ankerl::nanobench::Bench bench;
|
||||||
bench.minEpochIterations(5000);
|
bench.minEpochIterations(5000);
|
||||||
|
Reference in New Issue
Block a user