Some checks failed
weaselab/conflict-set/pipeline/head There was a failure building this commit
Now we can right benchmarks against one api, and just change the library path
277 lines
6.9 KiB
C++
277 lines
6.9 KiB
C++
#include "ConflictSet.h"
|
|
#include "Internal.h"
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
#if SHOW_MEMORY
|
|
void showMemory(const ConflictSet &cs);
|
|
#endif
|
|
|
|
#define ANKERL_NANOBENCH_IMPLEMENT
|
|
#include "third_party/nanobench.h"
|
|
|
|
constexpr int kNumKeys = 1000000;
|
|
|
|
constexpr int kOpsPerTx = 100;
|
|
|
|
constexpr int kPrefixLen = 0;
|
|
|
|
constexpr int kMvccWindow = 100000;
|
|
|
|
std::span<const uint8_t> makeKey(Arena &arena, int index) {
|
|
|
|
auto result =
|
|
std::span<uint8_t>{new (arena) uint8_t[4 + kPrefixLen], 4 + kPrefixLen};
|
|
index = __builtin_bswap32(index);
|
|
memset(result.data(), 0, kPrefixLen);
|
|
memcpy(result.data() + kPrefixLen, &index, 4);
|
|
|
|
return result;
|
|
}
|
|
|
|
ConflictSet::ReadRange singleton(Arena &arena, std::span<const uint8_t> key) {
|
|
auto r =
|
|
std::span<uint8_t>(new (arena) uint8_t[key.size() + 1], key.size() + 1);
|
|
memcpy(r.data(), key.data(), key.size());
|
|
r[key.size()] = 0;
|
|
return {key.data(), int(key.size()), r.data(), int(r.size())};
|
|
}
|
|
|
|
ConflictSet::ReadRange prefixRange(Arena &arena, std::span<const uint8_t> key) {
|
|
int index;
|
|
for (index = key.size() - 1; index >= 0; index--)
|
|
if ((key[index]) != 255)
|
|
break;
|
|
|
|
// Must not be called with a string that consists only of zero or more '\xff'
|
|
// bytes.
|
|
if (index < 0) {
|
|
assert(false);
|
|
}
|
|
|
|
auto r = std::span<uint8_t>(new (arena) uint8_t[index + 1], index + 1);
|
|
memcpy(r.data(), key.data(), index + 1);
|
|
r[r.size() - 1]++;
|
|
return {key.data(), int(key.size()), r.data(), int(r.size())};
|
|
}
|
|
|
|
void benchConflictSet() {
|
|
ankerl::nanobench::Bench bench;
|
|
ConflictSet cs{0};
|
|
|
|
bench.batch(kOpsPerTx);
|
|
bench.minEpochIterations(10000);
|
|
|
|
int64_t version = 0;
|
|
|
|
// Populate conflict set
|
|
Arena arena;
|
|
{
|
|
std::vector<ConflictSet::WriteRange> writes;
|
|
writes.reserve(kNumKeys);
|
|
for (int i = 0; i < kNumKeys; ++i) {
|
|
auto key = makeKey(arena, i);
|
|
ConflictSet::WriteRange w;
|
|
auto r = singleton(arena, key);
|
|
w.begin.p = r.begin.p;
|
|
w.begin.len = r.begin.len;
|
|
w.end.p = r.end.p;
|
|
w.end.len = 0;
|
|
w.writeVersion = version + 1;
|
|
writes.push_back(w);
|
|
}
|
|
cs.addWrites(writes.data(), writes.size());
|
|
++version;
|
|
}
|
|
|
|
// I don't know why std::less didn't work /shrug
|
|
struct Less {
|
|
bool operator()(const std::span<const uint8_t> &lhs,
|
|
const std::span<const uint8_t> &rhs) const {
|
|
return lhs < rhs;
|
|
}
|
|
};
|
|
auto points = set<std::span<const uint8_t>, Less>(arena);
|
|
|
|
while (points.size() < kOpsPerTx * 2 + 1) {
|
|
// TODO don't use rand?
|
|
points.insert(makeKey(arena, rand() % kNumKeys));
|
|
}
|
|
|
|
// Make short-circuiting non-trivial
|
|
{
|
|
std::vector<ConflictSet::WriteRange> writes;
|
|
auto iter = points.begin();
|
|
++iter; // Complement of the set we'll be reading with range reads. Almost.
|
|
for (int i = 0; i < kOpsPerTx; ++i) {
|
|
auto begin = *iter++;
|
|
auto end = *iter++;
|
|
ConflictSet::WriteRange w;
|
|
w.begin.p = begin.data();
|
|
w.begin.len = begin.size();
|
|
w.end.p = end.data();
|
|
w.end.len = end.size();
|
|
w.writeVersion = version + 1;
|
|
writes.push_back(w);
|
|
}
|
|
++version;
|
|
cs.addWrites(writes.data(), kOpsPerTx);
|
|
}
|
|
|
|
{
|
|
std::vector<ConflictSet::ReadRange> reads;
|
|
auto iter = points.begin();
|
|
for (int i = 0; i < kOpsPerTx; ++i) {
|
|
auto r = singleton(arena, *iter);
|
|
r.end.len = 0;
|
|
r.readVersion = version - 1;
|
|
reads.push_back(r);
|
|
++iter;
|
|
}
|
|
|
|
auto *results = new (arena) ConflictSet::Result[kOpsPerTx];
|
|
|
|
bench.run("point reads",
|
|
[&]() { cs.check(reads.data(), results, kOpsPerTx); });
|
|
}
|
|
|
|
{
|
|
std::vector<ConflictSet::ReadRange> reads;
|
|
auto iter = points.begin();
|
|
for (int i = 0; i < kOpsPerTx; ++i) {
|
|
auto r = prefixRange(arena, *iter);
|
|
r.readVersion = version - 1;
|
|
reads.push_back(r);
|
|
++iter;
|
|
}
|
|
|
|
auto *results = new (arena) ConflictSet::Result[kOpsPerTx];
|
|
|
|
bench.run("prefix reads",
|
|
[&]() { cs.check(reads.data(), results, kOpsPerTx); });
|
|
}
|
|
|
|
{
|
|
std::vector<ConflictSet::ReadRange> reads;
|
|
auto iter = points.begin();
|
|
for (int i = 0; i < kOpsPerTx; ++i) {
|
|
auto begin = *iter++;
|
|
auto end = *iter++;
|
|
ConflictSet::ReadRange r;
|
|
r.begin.p = begin.data();
|
|
r.begin.len = begin.size();
|
|
r.end.p = end.data();
|
|
r.end.len = end.size();
|
|
r.readVersion = version - 1;
|
|
reads.push_back(r);
|
|
}
|
|
|
|
auto *results = new (arena) ConflictSet::Result[kOpsPerTx];
|
|
|
|
bench.run("range reads",
|
|
[&]() { cs.check(reads.data(), results, kOpsPerTx); });
|
|
}
|
|
|
|
{
|
|
std::vector<ConflictSet::WriteRange> writes;
|
|
auto iter = points.begin();
|
|
for (int i = 0; i < kOpsPerTx; ++i) {
|
|
ConflictSet::WriteRange w;
|
|
auto r = singleton(arena, *iter);
|
|
w.begin.p = r.begin.p;
|
|
w.begin.len = r.begin.len;
|
|
w.end.p = r.end.p;
|
|
w.end.len = 0;
|
|
writes.push_back(w);
|
|
++iter;
|
|
}
|
|
|
|
while (version < kMvccWindow) {
|
|
auto v = ++version;
|
|
writes[0].writeVersion = v;
|
|
cs.addWrites(writes.data(), 1);
|
|
}
|
|
|
|
bench.run("point writes", [&]() {
|
|
auto v = ++version;
|
|
for (auto &w : writes) {
|
|
w.writeVersion = v;
|
|
}
|
|
cs.addWrites(writes.data(), writes.size());
|
|
cs.setOldestVersion(version - kMvccWindow);
|
|
});
|
|
}
|
|
|
|
{
|
|
std::vector<ConflictSet::WriteRange> writes;
|
|
auto iter = points.begin();
|
|
for (int i = 0; i < kOpsPerTx; ++i) {
|
|
ConflictSet::WriteRange w;
|
|
auto r = prefixRange(arena, *iter);
|
|
w.begin.p = r.begin.p;
|
|
w.begin.len = r.begin.len;
|
|
w.end.p = r.end.p;
|
|
w.end.len = r.end.len;
|
|
writes.push_back(w);
|
|
++iter;
|
|
}
|
|
|
|
bench.run("prefix writes", [&]() {
|
|
auto v = ++version;
|
|
for (auto &w : writes) {
|
|
w.writeVersion = v;
|
|
}
|
|
cs.addWrites(writes.data(), writes.size());
|
|
cs.setOldestVersion(version - kMvccWindow);
|
|
});
|
|
}
|
|
|
|
{
|
|
std::vector<ConflictSet::WriteRange> writes;
|
|
auto iter = points.begin();
|
|
for (int i = 0; i < kOpsPerTx; ++i) {
|
|
auto begin = *iter++;
|
|
auto end = *iter++;
|
|
ConflictSet::WriteRange w;
|
|
w.begin.p = begin.data();
|
|
w.begin.len = begin.size();
|
|
w.end.p = end.data();
|
|
w.end.len = end.size();
|
|
writes.push_back(w);
|
|
}
|
|
|
|
bench.run("range writes", [&]() {
|
|
auto v = ++version;
|
|
for (auto &w : writes) {
|
|
w.writeVersion = v;
|
|
}
|
|
cs.addWrites(writes.data(), writes.size());
|
|
cs.setOldestVersion(version - kMvccWindow);
|
|
});
|
|
}
|
|
|
|
bench.batch(1);
|
|
|
|
{
|
|
bench.run("monotonic increasing point writes", [&]() {
|
|
auto v = ++version;
|
|
ConflictSet::WriteRange w;
|
|
|
|
uint8_t b[9];
|
|
b[8] = 0;
|
|
auto x = __builtin_bswap64(version);
|
|
memcpy(b, &x, 8);
|
|
|
|
w.writeVersion = v;
|
|
w.begin.p = b;
|
|
w.begin.len = 8;
|
|
w.end.len = 0;
|
|
w.end.p = b;
|
|
cs.addWrites(&w, 1);
|
|
cs.setOldestVersion(version - kMvccWindow);
|
|
});
|
|
}
|
|
}
|
|
|
|
int main(void) { benchConflictSet(); } |