Partially implement addMutations
This commit is contained in:
231
VersionedMap.cpp
231
VersionedMap.cpp
@@ -322,33 +322,86 @@ private:
|
|||||||
uint32_t freeList = 0;
|
uint32_t freeList = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto operator<=>(const VersionedMap::Mutation &lhs, const Node &rhs) {
|
auto operator<=>(const VersionedMap::Key &lhs, const Node &rhs) {
|
||||||
int cl = std::min(lhs.param1Length, rhs.entry->keyLen);
|
int cl = std::min(lhs.len, rhs.entry->keyLen);
|
||||||
if (cl > 0) {
|
if (cl > 0) {
|
||||||
int c = memcmp(lhs.param1, rhs.entry->getKey(), cl);
|
int c = memcmp(lhs.p, rhs.entry->getKey(), cl);
|
||||||
if (c != 0) {
|
if (c != 0) {
|
||||||
return c <=> 0;
|
return c <=> 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lhs.param1Length <=> rhs.entry->keyLen;
|
return lhs.len <=> rhs.entry->keyLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Finger {
|
struct Finger {
|
||||||
void push(uint32_t node) { searchPath[searchPathSize_++] = node; }
|
void push(uint32_t node, bool dir) {
|
||||||
|
searchPath[searchPathSize_] = node;
|
||||||
|
direction[searchPathSize_] = dir;
|
||||||
|
++searchPathSize_;
|
||||||
|
}
|
||||||
void pop() { --searchPathSize_; }
|
void pop() { --searchPathSize_; }
|
||||||
uint32_t back() const {
|
uint32_t backNode() const {
|
||||||
assert(searchPathSize_ > 0);
|
assert(searchPathSize_ > 0);
|
||||||
return searchPath[searchPathSize_ - 1];
|
return searchPath[searchPathSize_ - 1];
|
||||||
}
|
}
|
||||||
|
bool backDirection() const {
|
||||||
|
assert(searchPathSize_ > 0);
|
||||||
|
return direction[searchPathSize_ - 1];
|
||||||
|
}
|
||||||
uint32_t searchPathSize() const { return searchPathSize_; }
|
uint32_t searchPathSize() const { return searchPathSize_; }
|
||||||
|
|
||||||
|
Finger() : searchPathSize_(0) {}
|
||||||
|
|
||||||
|
Finger(const Finger &other) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
memset(searchPath, 0, sizeof(searchPath));
|
||||||
|
memset(direction, 0, sizeof(direction));
|
||||||
|
#endif
|
||||||
|
memcpy(searchPath, other.searchPath,
|
||||||
|
other.searchPathSize_ * sizeof(searchPath[0]));
|
||||||
|
memcpy(direction, other.direction,
|
||||||
|
other.searchPathSize_ * sizeof(direction[0]));
|
||||||
|
searchPathSize_ = other.searchPathSize_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Finger &operator=(const Finger &other) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
memset(searchPath, 0, sizeof(searchPath));
|
||||||
|
memset(direction, 0, sizeof(direction));
|
||||||
|
#endif
|
||||||
|
memcpy(searchPath, other.searchPath,
|
||||||
|
other.searchPathSize_ * sizeof(searchPath[0]));
|
||||||
|
memcpy(direction, other.direction,
|
||||||
|
other.searchPathSize_ * sizeof(direction[0]));
|
||||||
|
searchPathSize_ = other.searchPathSize_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t searchPath[kPathLengthUpperBound];
|
uint32_t searchPath[kPathLengthUpperBound];
|
||||||
int searchPathSize_ = 0;
|
bool direction[kPathLengthUpperBound];
|
||||||
|
int searchPathSize_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VersionedMap::Impl {
|
struct VersionedMap::Impl {
|
||||||
|
|
||||||
|
template <std::memory_order kOrder>
|
||||||
|
void move(Finger &finger, int64_t at, bool direction) {
|
||||||
|
uint32_t c;
|
||||||
|
if (finger.backNode() != 0 &&
|
||||||
|
(c = child<kOrder>(finger.backNode(), direction, at)) != 0) {
|
||||||
|
finger.push(c, direction);
|
||||||
|
while (auto c = child<kOrder>(finger.backNode(), !direction, at) != 0) {
|
||||||
|
finger.push(c, !direction);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (finger.searchPathSize() > 1 && finger.backDirection() == true) {
|
||||||
|
finger.pop();
|
||||||
|
}
|
||||||
|
finger.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 ||
|
static_assert(kOrder == std::memory_order_acquire ||
|
||||||
@@ -436,36 +489,69 @@ struct VersionedMap::Impl {
|
|||||||
at);
|
at);
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert(const Mutation &m) {
|
struct Val {
|
||||||
|
const uint8_t *p;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Infers `val` and `clearTo` if not set
|
||||||
|
void insert(Key key, std::optional<Val> val, std::optional<bool> clearTo) {
|
||||||
Finger finger;
|
Finger finger;
|
||||||
finger.push(latestRoot);
|
bool ignored;
|
||||||
bool directionStack[kPathLengthUpperBound];
|
finger.push(latestRoot, ignored);
|
||||||
int directionStackSize = 0;
|
bool inserted;
|
||||||
|
|
||||||
// Initialize finger to the search path of `m`
|
// Initialize finger to the search path of `m`
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto n = finger.back();
|
auto n = finger.backNode();
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
|
inserted = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto c = m <=> mm.base[n];
|
auto c = key <=> mm.base[n];
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
// No duplicates
|
// No duplicates
|
||||||
|
inserted = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
finger.push(child<std::memory_order_relaxed>(n, c > 0, latestVersion));
|
finger.push(child<std::memory_order_relaxed>(n, c > 0, latestVersion),
|
||||||
directionStack[directionStackSize++] = c > 0;
|
c > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infer `val` if not set
|
||||||
|
if (!val.has_value()) {
|
||||||
|
if (inserted) {
|
||||||
|
val = {nullptr, -1};
|
||||||
|
} else {
|
||||||
|
auto *entry = mm.base[finger.backNode()].entry;
|
||||||
|
val = {entry->getVal(), entry->valLen};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infer `clearTo` if not set
|
||||||
|
if (!clearTo.has_value()) {
|
||||||
|
if (inserted) {
|
||||||
|
auto copy = finger;
|
||||||
|
move<std::memory_order_relaxed>(copy, latestVersion, true);
|
||||||
|
if (copy.searchPathSize() == 0) {
|
||||||
|
clearTo = false;
|
||||||
|
} else {
|
||||||
|
clearTo = mm.base[copy.backNode()].entry->clearTo;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearTo = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare new node
|
// Prepare new node
|
||||||
uint32_t node = newNode(latestVersion, m.param1, m.param1Length, m.param2,
|
uint32_t node =
|
||||||
m.param2Length, false /* TODO set correctly */);
|
newNode(latestVersion, key.p, key.len, val->p, val->len, *clearTo);
|
||||||
if (finger.back() != 0) {
|
if (!inserted) {
|
||||||
auto &n = mm.base[node];
|
auto &n = mm.base[node];
|
||||||
n.pointer[0] =
|
n.pointer[0] = child<std::memory_order_relaxed>(finger.backNode(), false,
|
||||||
child<std::memory_order_relaxed>(finger.back(), false, latestVersion);
|
latestVersion);
|
||||||
n.pointer[1] =
|
n.pointer[1] = child<std::memory_order_relaxed>(finger.backNode(), true,
|
||||||
child<std::memory_order_relaxed>(finger.back(), true, latestVersion);
|
latestVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate and propagate up the search path
|
// Rotate and propagate up the search path
|
||||||
@@ -475,14 +561,15 @@ struct VersionedMap::Impl {
|
|||||||
latestRoot = node;
|
latestRoot = node;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
const bool direction = finger.backDirection();
|
||||||
finger.pop();
|
finger.pop();
|
||||||
auto parent = finger.back();
|
auto parent = finger.backNode();
|
||||||
const bool direction = directionStack[--directionStackSize];
|
|
||||||
parent = update(parent, direction, node, latestVersion);
|
parent = update(parent, direction, node, latestVersion);
|
||||||
if (mm.base[node].entry->priority > mm.base[parent].entry->priority) {
|
if (inserted &&
|
||||||
|
mm.base[node].entry->priority > mm.base[parent].entry->priority) {
|
||||||
rotate(parent, latestVersion, !direction);
|
rotate(parent, latestVersion, !direction);
|
||||||
} else {
|
} else {
|
||||||
if (parent == finger.back()) {
|
if (parent == finger.backNode()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -511,6 +598,30 @@ struct VersionedMap::Impl {
|
|||||||
|
|
||||||
void printInOrderHelper(int64_t version, uint32_t node);
|
void printInOrderHelper(int64_t version, uint32_t node);
|
||||||
|
|
||||||
|
void addMutations(const Mutation *mutations, int numMutations,
|
||||||
|
int64_t version) {
|
||||||
|
assert(latestVersion < version);
|
||||||
|
latestVersion = version;
|
||||||
|
latestRoot = roots.roots()[roots.rootCount() - 1];
|
||||||
|
// TODO Improve ILP?
|
||||||
|
for (int i = 0; i < numMutations; ++i) {
|
||||||
|
const auto &m = mutations[i];
|
||||||
|
switch (m.type) {
|
||||||
|
case Set: {
|
||||||
|
insert({m.param1, m.param1Len}, {{m.param2, m.param2Len}}, {});
|
||||||
|
} break;
|
||||||
|
case Clear: {
|
||||||
|
insert({m.param1, m.param1Len}, {{nullptr, -1}}, {});
|
||||||
|
// TODO erase (param1, param2)
|
||||||
|
insert({m.param2, m.param2Len}, {}, true);
|
||||||
|
} break;
|
||||||
|
default: // GCOVR_EXCL_LINE
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roots.add(latestRoot, latestVersion);
|
||||||
|
}
|
||||||
|
|
||||||
MemManager mm;
|
MemManager mm;
|
||||||
RootSet roots;
|
RootSet roots;
|
||||||
// Only meaningful within the callstack of `addMutations`
|
// Only meaningful within the callstack of `addMutations`
|
||||||
@@ -518,6 +629,31 @@ struct VersionedMap::Impl {
|
|||||||
int64_t latestVersion = 0;
|
int64_t latestVersion = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VersionedMap::VersionedMap(int64_t version)
|
||||||
|
: impl(new(malloc(sizeof(Impl))) Impl()) {
|
||||||
|
impl->latestVersion = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
VersionedMap::~VersionedMap() {
|
||||||
|
if (impl != nullptr) {
|
||||||
|
impl->~Impl();
|
||||||
|
free(impl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VersionedMap::VersionedMap(VersionedMap &&other) noexcept {
|
||||||
|
impl = std::exchange(other.impl, nullptr);
|
||||||
|
}
|
||||||
|
VersionedMap &VersionedMap::operator=(VersionedMap &&other) noexcept {
|
||||||
|
impl = std::exchange(other.impl, nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VersionedMap::addMutations(const Mutation *mutations, int numMutations,
|
||||||
|
int64_t version) {
|
||||||
|
impl->addMutations(mutations, numMutations, version);
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== END IMPLEMENTATION ====================
|
// ==================== END IMPLEMENTATION ====================
|
||||||
|
|
||||||
// GCOVR_EXCL_START
|
// GCOVR_EXCL_START
|
||||||
@@ -556,20 +692,13 @@ void VersionedMap::Impl::printInOrderHelper(int64_t version, uint32_t node) {
|
|||||||
int main() {
|
int main() {
|
||||||
{
|
{
|
||||||
weaselab::VersionedMap::Impl impl;
|
weaselab::VersionedMap::Impl impl;
|
||||||
impl.latestRoot = impl.roots.roots()[impl.roots.rootCount() - 1];
|
weaselab::VersionedMap::Mutation m[] = {
|
||||||
impl.latestVersion = 1;
|
{(const uint8_t *)"a", nullptr, 1, 0, weaselab::VersionedMap::Set},
|
||||||
impl.insert(weaselab::VersionedMap::Mutation{
|
{(const uint8_t *)"b", nullptr, 1, 0, weaselab::VersionedMap::Set},
|
||||||
(const uint8_t *)"c", nullptr, 1, 0, weaselab::VersionedMap::Set});
|
{(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.addMutations(m, sizeof(m) / sizeof(m[0]), 1);
|
||||||
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(1);
|
||||||
impl.printInOrder(2);
|
|
||||||
impl.printInOrder(3);
|
|
||||||
impl.setOldestVersion(3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ankerl::nanobench::Bench bench;
|
ankerl::nanobench::Bench bench;
|
||||||
@@ -615,30 +744,6 @@ int main() {
|
|||||||
roots.getThreadSafeHandle().rootForVersion(i - kNumVersions / 2));
|
roots.getThreadSafeHandle().rootForVersion(i - kNumVersions / 2));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
weaselab::VersionedMap::Impl impl;
|
|
||||||
bench.run("insert fixed entry", [&]() {
|
|
||||||
++impl.latestVersion;
|
|
||||||
impl.latestRoot = impl.roots.roots()[impl.roots.rootCount() - 1];
|
|
||||||
impl.insert(weaselab::VersionedMap::Mutation{
|
|
||||||
(const uint8_t *)"a", nullptr, 1, 0, weaselab::VersionedMap::Set});
|
|
||||||
impl.roots.add(impl.latestRoot, impl.latestVersion);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
weaselab::VersionedMap::Impl impl;
|
|
||||||
bench.run("insert fresh entry", [&]() {
|
|
||||||
++impl.latestVersion;
|
|
||||||
impl.latestRoot = impl.roots.roots()[impl.roots.rootCount() - 1];
|
|
||||||
auto k = __builtin_bswap64(impl.latestVersion);
|
|
||||||
impl.insert(weaselab::VersionedMap::Mutation{
|
|
||||||
(const uint8_t *)&k, nullptr, sizeof(k), 0,
|
|
||||||
weaselab::VersionedMap::Set});
|
|
||||||
impl.roots.add(impl.latestRoot, impl.latestVersion);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -61,8 +61,8 @@ struct VersionedMap {
|
|||||||
struct Mutation {
|
struct Mutation {
|
||||||
const uint8_t *param1;
|
const uint8_t *param1;
|
||||||
const uint8_t *param2;
|
const uint8_t *param2;
|
||||||
int param1Length;
|
int param1Len;
|
||||||
int param2Length;
|
int param2Len;
|
||||||
MutationType type;
|
MutationType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user