Add efficient firstGeq overload for latestVersion

This commit is contained in:
2024-06-03 22:28:01 -07:00
parent ba11412584
commit b92f0ec227
5 changed files with 168 additions and 73 deletions

View File

@@ -949,9 +949,23 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl {
totalMallocBytes += mallocBytesDelta;
}
struct StepwiseFirstGeq {
const VersionedMap::Impl *map;
const weaselab::VersionedMap::Key *key;
int64_t version;
weaselab::VersionedMap::Iterator *iterator;
void begin(uint32_t root);
bool step();
void end();
};
void firstGeq(const Key *key, const int64_t *version, Iterator *iterator,
int count) const;
void firstGeq(const Key *key, Iterator *iterator, int count) const;
// State used to resume scanning and removing old entries in `addMutations`
Key continueKey;
Arena continueArena;
@@ -1222,6 +1236,67 @@ bool geq(const VersionedMap::Iterator::VersionedMutation &m,
}
}
void VersionedMap::Impl::StepwiseFirstGeq::begin(uint32_t root) {
if (iterator->impl != nullptr) {
iterator->impl->~Impl();
new (iterator->impl) Iterator::Impl();
} else {
iterator->impl = new (safe_malloc(sizeof(Iterator::Impl))) Iterator::Impl();
}
Finger &finger = iterator->impl->finger;
finger.clear();
bool ignored = false;
finger.push(root, ignored);
}
bool VersionedMap::Impl::StepwiseFirstGeq::step() {
Finger &finger = iterator->impl->finger;
auto n = finger.backNode();
if (n == 0) {
return true;
}
auto c = *key <=> map->mm.base[n];
if (c == 0) {
// No duplicates
return true;
}
finger.push(map->child<std::memory_order_acquire>(n, c > 0, version), c > 0);
return false;
}
void VersionedMap::Impl::StepwiseFirstGeq::end() {
Finger &finger = iterator->impl->finger;
if (finger.searchPathSize() > 0 && finger.backNode() == 0) {
map->move<std::memory_order_acquire, true>(finger, version);
if (finger.searchPathSize() > 0) {
assert(finger.backNode() != 0);
}
}
iterator->impl->version = version;
iterator->impl->map = map;
const Entry *prev = nullptr;
for (;;) {
if (finger.searchPathSize() == 0) {
break;
} else {
materializeMutations(iterator->impl, prev, nullptr);
for (int j = 0; j < iterator->impl->mutationCount; ++j) {
if (geq(iterator->impl->mutations[j], *key)) {
iterator->impl->mutationIndex = j;
goto loopEnd;
}
}
}
prev = iterator->impl->map->mm.base[finger.backNode()].entry;
iterator->impl->map->move<std::memory_order_acquire, true>(
finger, iterator->impl->version);
}
loopEnd:;
}
void VersionedMap::Impl::firstGeq(const weaselab::VersionedMap::Key *key,
const int64_t *version, Iterator *iterator,
int count) const {
@@ -1229,78 +1304,6 @@ void VersionedMap::Impl::firstGeq(const weaselab::VersionedMap::Key *key,
return;
}
struct StepwiseFirstGeq {
const VersionedMap::Impl *map;
const weaselab::VersionedMap::Key *key;
int64_t version;
weaselab::VersionedMap::Iterator *iterator;
void begin(RootSet::ThreadSafeHandle handle) {
if (iterator->impl != nullptr) {
iterator->impl->~Impl();
new (iterator->impl) Iterator::Impl();
} else {
iterator->impl =
new (safe_malloc(sizeof(Iterator::Impl))) Iterator::Impl();
}
uint32_t root = handle.rootForVersion(version);
Finger &finger = iterator->impl->finger;
finger.clear();
bool ignored = false;
finger.push(root, ignored);
}
bool step() {
Finger &finger = iterator->impl->finger;
auto n = finger.backNode();
if (n == 0) {
return true;
}
auto c = *key <=> map->mm.base[n];
if (c == 0) {
// No duplicates
return true;
}
finger.push(map->child<std::memory_order_acquire>(n, c > 0, version),
c > 0);
return false;
}
void end() {
Finger &finger = iterator->impl->finger;
if (finger.searchPathSize() > 0 && finger.backNode() == 0) {
map->move<std::memory_order_acquire, true>(finger, version);
if (finger.searchPathSize() > 0) {
assert(finger.backNode() != 0);
}
}
iterator->impl->version = version;
iterator->impl->map = map;
const Entry *prev = nullptr;
for (;;) {
if (finger.searchPathSize() == 0) {
break;
} else {
materializeMutations(iterator->impl, prev, nullptr);
for (int j = 0; j < iterator->impl->mutationCount; ++j) {
if (geq(iterator->impl->mutations[j], *key)) {
iterator->impl->mutationIndex = j;
goto loopEnd;
}
}
}
prev = iterator->impl->map->mm.base[finger.backNode()].entry;
iterator->impl->map->move<std::memory_order_acquire, true>(
finger, iterator->impl->version);
}
loopEnd:;
}
};
// Use stack allocation for small count
Arena arena;
constexpr int kStackAllocThreshold = 2;
@@ -1322,7 +1325,56 @@ void VersionedMap::Impl::firstGeq(const weaselab::VersionedMap::Key *key,
stepwise[i].key = &key[i];
stepwise[i].version = version[i];
stepwise[i].iterator = &iterator[i];
stepwise[i].begin(handle);
stepwise[i].begin(handle.rootForVersion(version[i]));
nextJob[i] = i + 1;
}
nextJob[count - 1] = 0;
int prevJob = count - 1;
int job = 0;
for (;;) {
if (stepwise[job].step()) {
stepwise[job].end();
if (job == prevJob) {
break;
}
nextJob[prevJob] = nextJob[job];
job = prevJob;
}
prevJob = job;
job = nextJob[job];
}
}
void VersionedMap::Impl::firstGeq(const weaselab::VersionedMap::Key *key,
Iterator *iterator, int count) const {
if (count == 0) {
return;
}
// Use stack allocation for small count
Arena arena;
constexpr int kStackAllocThreshold = 2;
StepwiseFirstGeq stepwiseStackAlloc[kStackAllocThreshold];
int nextJobStackAllocation[kStackAllocThreshold];
StepwiseFirstGeq *stepwise;
int *nextJob;
if (count <= kStackAllocThreshold) {
stepwise = stepwiseStackAlloc;
nextJob = nextJobStackAllocation;
} else {
stepwise = new (arena) StepwiseFirstGeq[count];
nextJob = new (arena) int[count];
}
const uint32_t root = roots.roots()[roots.rootCount() - 1];
assert(root == roots.getThreadSafeHandle().rootForVersion(latestVersion));
for (int i = 0; i < count; ++i) {
stepwise[i].map = this;
stepwise[i].key = &key[i];
stepwise[i].version = latestVersion;
stepwise[i].iterator = &iterator[i];
stepwise[i].begin(root);
nextJob[i] = i + 1;
}
nextJob[count - 1] = 0;
@@ -1355,6 +1407,11 @@ void VersionedMap::firstGeq(const Key *key, const int64_t *version,
impl->firstGeq(key, version, iterator, count);
}
void VersionedMap::firstGeq(const Key *key, Iterator *iterator,
int count) const {
impl->firstGeq(key, iterator, count);
}
VersionedMap::Iterator VersionedMap::begin(int64_t version) const {
VersionedMap::Iterator result;
result.impl = new (safe_malloc(sizeof(Iterator::Impl))) Iterator::Impl();