refactor: call the benchmark's comparison the legacy format
The README, a comment in :Reflect and tests/Benchmark named the project whose transaction log this library replaces. That name means nothing to people using the library, so the benchmark's reference implementation is now the `Legacy` namespace, its column reads `legacy ms`, and the prose describes it as the hand-rolled iostream format Crafter.DB replaces. Timings and logic are unchanged; the suite still passes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
bfd0cc261b
commit
eb720058f5
3 changed files with 33 additions and 33 deletions
|
|
@ -1,9 +1,9 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
// Crafter.DB against a cleaned-up copy of Nostro's hand-rolled format
|
||||
// Crafter.DB against a cleaned-up copy of the legacy format it replaces
|
||||
// (iostream, uint16-prefixed strings inline, linear scans) on the five
|
||||
// operations Nostro performs. Prints a table; never fails. Runs on 100k
|
||||
// operations that log served. Prints a table; never fails. Runs on 100k
|
||||
// rows so a full suite stays fast; raise `rowCount` for a serious run.
|
||||
|
||||
import Crafter.DB;
|
||||
|
|
@ -49,8 +49,8 @@ namespace {
|
|||
return rows;
|
||||
}
|
||||
|
||||
// --- the Nostro format, cleaned up -----------------------------------------
|
||||
namespace Nostro {
|
||||
// --- the legacy format, cleaned up -----------------------------------------
|
||||
namespace Legacy {
|
||||
void WriteString(std::ofstream& file, std::string_view str) {
|
||||
const std::uint16_t size = static_cast<std::uint16_t>(str.size());
|
||||
file.write(reinterpret_cast<const char*>(&size), sizeof(size));
|
||||
|
|
@ -139,7 +139,7 @@ namespace {
|
|||
|
||||
struct Line {
|
||||
std::string name;
|
||||
double nostro;
|
||||
double legacy;
|
||||
double crafter;
|
||||
std::string note;
|
||||
};
|
||||
|
|
@ -149,7 +149,7 @@ int main() {
|
|||
const fs::path dir = fs::temp_directory_path() / "crafter-db-benchmark";
|
||||
fs::remove_all(dir);
|
||||
fs::create_directories(dir);
|
||||
const fs::path nostroPath = dir / "nostro.bin";
|
||||
const fs::path legacyPath = dir / "legacy.bin";
|
||||
const fs::path crafterPath = dir / "crafter.cdb";
|
||||
const std::vector<Transaction> rows = MakeRows();
|
||||
const std::string_view wantedIban = rows[rowCount / 2].iban;
|
||||
|
|
@ -160,84 +160,84 @@ int main() {
|
|||
|
||||
// append
|
||||
{
|
||||
const double nostro = Millis([&] { Nostro::Save(nostroPath, rows); });
|
||||
const double legacy = Millis([&] { Legacy::Save(legacyPath, rows); });
|
||||
const double crafter = Millis([&] {
|
||||
DB::Table<Transaction> table(crafterPath);
|
||||
table.Append(rows);
|
||||
table.Flush();
|
||||
});
|
||||
lines.push_back({ std::format("append {} rows", rowCount), nostro, crafter, "incl. open, Crafter.DB incl. fdatasync" });
|
||||
lines.push_back({ std::format("append {} rows", rowCount), legacy, crafter, "incl. open, Crafter.DB incl. fdatasync" });
|
||||
}
|
||||
|
||||
// last timestamp (1000 opens each)
|
||||
{
|
||||
const double nostro = Millis([&] { for (std::int32_t i = 0; i < 1000; i++) sink += Nostro::LastTime(nostroPath).time_since_epoch().count() & 1; });
|
||||
const double legacy = Millis([&] { for (std::int32_t i = 0; i < 1000; i++) sink += Legacy::LastTime(legacyPath).time_since_epoch().count() & 1; });
|
||||
const double crafter = Millis([&] {
|
||||
for (std::int32_t i = 0; i < 1000; i++) {
|
||||
const DB::Table<Transaction> table(crafterPath, { .readOnly = true });
|
||||
sink += table.Back()[&Transaction::timePoint].time_since_epoch().count() & 1;
|
||||
}
|
||||
});
|
||||
lines.push_back({ "last timestamp (1000x open+read)", nostro, crafter, "open dominates both" });
|
||||
lines.push_back({ "last timestamp (1000x open+read)", legacy, crafter, "open dominates both" });
|
||||
}
|
||||
|
||||
DB::Table<Transaction> table(crafterPath, { .readOnly = true });
|
||||
|
||||
// load all
|
||||
{
|
||||
const double nostro = Millis([&] { sink += Nostro::LoadAll(nostroPath).size(); });
|
||||
const double legacy = Millis([&] { sink += Legacy::LoadAll(legacyPath).size(); });
|
||||
const double crafter = Millis([&] { sink += table.Materialize().size(); });
|
||||
double zeroCopy = Millis([&] {
|
||||
std::int64_t total = 0;
|
||||
for (DB::Row<Transaction> row : table) total += row[&Transaction::amount] + static_cast<std::int64_t>(row[&Transaction::iban].Length());
|
||||
sink += static_cast<std::size_t>(total & 1);
|
||||
});
|
||||
lines.push_back({ "load all (materialised)", nostro, crafter, std::format("zero-copy scan of two columns: {:.2f} ms", zeroCopy) });
|
||||
lines.push_back({ "load all (materialised)", legacy, crafter, std::format("zero-copy scan of two columns: {:.2f} ms", zeroCopy) });
|
||||
}
|
||||
|
||||
// time range (10% of rows)
|
||||
{
|
||||
std::size_t nostroCount = 0;
|
||||
std::size_t legacyCount = 0;
|
||||
std::size_t crafterCount = 0;
|
||||
const double nostro = Millis([&] { nostroCount = Nostro::LoadRange(nostroPath, from, to).size(); });
|
||||
const double legacy = Millis([&] { legacyCount = Legacy::LoadRange(legacyPath, from, to).size(); });
|
||||
const double crafter = Millis([&] { crafterCount = table.Range<&Transaction::timePoint>(from, to).Materialize().size(); });
|
||||
lines.push_back({ "time range (10% of rows, materialised)", nostro, crafter, std::format("{} / {} rows", nostroCount, crafterCount) });
|
||||
lines.push_back({ "time range (10% of rows, materialised)", legacy, crafter, std::format("{} / {} rows", legacyCount, crafterCount) });
|
||||
}
|
||||
|
||||
// iban ==
|
||||
{
|
||||
std::size_t nostroCount = 0;
|
||||
std::size_t legacyCount = 0;
|
||||
std::size_t keyCount = 0;
|
||||
std::size_t exprCount = 0;
|
||||
std::size_t viewCount = 0;
|
||||
const double nostro = Millis([&] { nostroCount = Nostro::LoadByIban(nostroPath, wantedIban).size(); });
|
||||
const double legacy = Millis([&] { legacyCount = Legacy::LoadByIban(legacyPath, wantedIban).size(); });
|
||||
const double withKey = Millis([&] {
|
||||
const DB::Key key(wantedIban);
|
||||
keyCount = table.Where([&](DB::Row<Transaction> r) { return r[&Transaction::iban] == key; }).Count();
|
||||
});
|
||||
const double withExpression = Millis([&] { exprCount = table.Where(iban == wantedIban).Count(); });
|
||||
const double withView = Millis([&] { viewCount = table.Where([&](DB::Row<Transaction> r) { return r[&Transaction::iban] == wantedIban; }).Count(); });
|
||||
lines.push_back({ "iban == x (lambda + Key)", nostro, withKey, std::format("{} / {} rows, {:.1f} ns/row", nostroCount, keyCount, withKey * 1e6 / rowCount) });
|
||||
lines.push_back({ "iban == x (expression)", nostro, withExpression, std::format("{} rows, {:.1f} ns/row", exprCount, withExpression * 1e6 / rowCount) });
|
||||
lines.push_back({ "iban == x (lambda + string_view)", nostro, withView, std::format("{} rows, {:.1f} ns/row", viewCount, withView * 1e6 / rowCount) });
|
||||
lines.push_back({ "iban == x (lambda + Key)", legacy, withKey, std::format("{} / {} rows, {:.1f} ns/row", legacyCount, keyCount, withKey * 1e6 / rowCount) });
|
||||
lines.push_back({ "iban == x (expression)", legacy, withExpression, std::format("{} rows, {:.1f} ns/row", exprCount, withExpression * 1e6 / rowCount) });
|
||||
lines.push_back({ "iban == x (lambda + string_view)", legacy, withView, std::format("{} rows, {:.1f} ns/row", viewCount, withView * 1e6 / rowCount) });
|
||||
}
|
||||
|
||||
// range + iban
|
||||
{
|
||||
std::size_t nostroCount = 0;
|
||||
std::size_t legacyCount = 0;
|
||||
std::size_t crafterCount = 0;
|
||||
const double nostro = Millis([&] {
|
||||
for (const Transaction& tr : Nostro::LoadRange(nostroPath, from, to)) nostroCount += tr.iban == wantedIban ? 1 : 0;
|
||||
const double legacy = Millis([&] {
|
||||
for (const Transaction& tr : Legacy::LoadRange(legacyPath, from, to)) legacyCount += tr.iban == wantedIban ? 1 : 0;
|
||||
});
|
||||
const double crafter = Millis([&] { crafterCount = table.Where(timePoint >= from && timePoint <= to && iban == wantedIban).Count(); });
|
||||
lines.push_back({ "time range + iban == x", nostro, crafter, std::format("{} / {} rows", nostroCount, crafterCount) });
|
||||
lines.push_back({ "time range + iban == x", legacy, crafter, std::format("{} / {} rows", legacyCount, crafterCount) });
|
||||
}
|
||||
|
||||
std::println("{:<42} {:>12} {:>12} {:>8} {}", "operation", "nostro ms", "crafter ms", "speedup", "note");
|
||||
std::println("{:<42} {:>12} {:>12} {:>8} {}", "operation", "legacy ms", "crafter ms", "speedup", "note");
|
||||
for (const Line& line : lines) {
|
||||
std::println("{:<42} {:>12.3f} {:>12.3f} {:>7.1f}x {}", line.name, line.nostro, line.crafter, line.nostro / std::max(line.crafter, 0.0001), line.note);
|
||||
std::println("{:<42} {:>12.3f} {:>12.3f} {:>7.1f}x {}", line.name, line.legacy, line.crafter, line.legacy / std::max(line.crafter, 0.0001), line.note);
|
||||
}
|
||||
std::println("files: nostro {} bytes, crafter {} bytes", fs::file_size(nostroPath), fs::file_size(crafterPath));
|
||||
std::println("files: legacy {} bytes, crafter {} bytes", fs::file_size(legacyPath), fs::file_size(crafterPath));
|
||||
std::println("(sink {})", sink & 1);
|
||||
|
||||
fs::remove_all(dir);
|
||||
|
|
|
|||
Loading…
Reference in a new issue