1#include <xrpl/nodestore/Database.h>
3#include <xrpl/basics/Blob.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/nodestore/NodeObject.h>
6#include <xrpl/nodestore/Types.h>
8#include <benchmark/benchmark.h>
9#include <benchmarks/libxrpl/nodestore/NodeStoreBench.h>
25constexpr std::size_t kDefaultPoolSize = 100000;
29constexpr int kReadThreads = 4;
31constexpr std::string_view kNamePrefix =
"BM_Database_";
32constexpr std::string_view kNameSeparator =
"/";
36 std::unique_ptr<DatabaseHarness> harness;
39 std::vector<uint256> missing;
40 std::vector<std::size_t> shuffle;
41 std::size_t avgPayload = 0;
62 std::string_view name;
63 std::function<void(SetupContext
const&)> setup;
64 std::function<void(IterateContext
const&)> iterate;
65 bool reportBytes =
false;
66 bool pinIterations =
false;
72 auto const seq = db.earliestLedgerSeq();
73 for (
auto const& obj : objects)
76 db.store(obj->getType(), std::move(data), obj->getHash(), seq);
85 [](SetupContext
const& ctx) {
86 ctx.rs.present =
makePool(1, ctx.poolSize);
90 [](IterateContext
const& ctx) {
91 auto& [rs, db, seq, index, poolSize] = ctx;
92 auto const& obj = rs.present[index % poolSize];
94 db.store(obj->getType(), std::move(data), obj->getHash(), seq);
97 .pinIterations =
true,
101Workload
const kFetch{
104 [](SetupContext
const& ctx) {
105 ctx.rs.present =
makePool(1, ctx.poolSize);
110 [](IterateContext
const& ctx) {
111 auto& [rs, db, seq, index, poolSize] = ctx;
112 auto obj = db.fetchNodeObject(rs.present[index % poolSize]->getHash(), seq);
113 benchmark::DoNotOptimize(obj);
119Workload
const kMissing{
121 .setup = [](SetupContext
const& ctx) { ctx.rs.missing =
makeMissingKeys(ctx.poolSize); },
123 [](IterateContext
const& ctx) {
124 auto& [rs, db, seq, index, poolSize] = ctx;
125 auto obj = db.fetchNodeObject(rs.missing[index % poolSize], seq);
126 benchmark::DoNotOptimize(obj);
133Workload
const kMixed{
136 [](SetupContext
const& ctx) {
137 ctx.rs.present =
makePool(1, ctx.poolSize);
143 [](IterateContext
const& ctx) {
144 auto& [rs, db, seq, index, poolSize] = ctx;
145 auto const pick = rs.shuffle[index % poolSize];
146 std::shared_ptr<NodeObject> obj;
149 obj = db.fetchNodeObject(rs.missing[pick], seq);
153 obj = db.fetchNodeObject(rs.present[pick]->getHash(), seq);
155 benchmark::DoNotOptimize(obj);
167 [](SetupContext
const& ctx) {
168 ctx.rs.present =
makePool(1, ctx.poolSize);
169 ctx.rs.recent =
makePool(1, ctx.poolSize, ctx.poolSize);
174 [](IterateContext
const& ctx) {
175 auto& [rs, db, seq, index, poolSize] = ctx;
176 auto const slot = index % poolSize;
177 auto const pick = rs.shuffle[slot];
179 auto historical = db.fetchNodeObject(rs.present[pick]->getHash(), seq);
180 benchmark::DoNotOptimize(historical);
182 auto recent = db.fetchNodeObject(rs.recent[pick]->getHash(), seq);
183 benchmark::DoNotOptimize(recent);
185 auto const& obj = rs.recent[slot];
187 db.store(obj->getType(), std::move(data), obj->getHash(), seq);
189 .pinIterations =
true,
196 std::string
const cfg = bc.config;
197 std::string name{kNamePrefix};
199 name += kNameSeparator;
201 auto* b = benchmark::RegisterBenchmark(name, [rs, cfg, w](benchmark::State& state) {
202 auto const poolSize =
static_cast<std::size_t
>(state.range(0));
204 auto& db = *rs->harness->db;
205 w.setup(SetupContext{.rs = *rs, .db = db, .poolSize = poolSize});
206 auto const seq = db.earliestLedgerSeq();
208 std::size_t index = 0;
213 .rs = *rs, .db = db, .seq = seq, .index = index, .poolSize = poolSize});
216 benchmark::ClobberMemory();
218 state.SetItemsProcessed(state.iterations());
221 state.SetBytesProcessed(
static_cast<std::int64_t
>(state.iterations() * rs->avgPayload));
226 b->Arg(kDefaultPoolSize);
229 b->Iterations(kDefaultPoolSize);
232[[maybe_unused]]
bool const kRegistered = [] {
233 auto const workloads = std::to_array({&kStore, &kFetch, &kMissing, &kMixed, &kWork});
236 for (
auto const* w : workloads)
237 registerWorkload(bc, *w);
Persistency layer for NodeObject.
std::vector< std::size_t > makeShuffle(std::size_t size, std::uint64_t seed)
std::vector< BackendConfig > const & backendConfigs()
void prepopulate(Backend &backend, Batch const &objects)
std::vector< uint256 > makeMissingKeys(std::size_t count)
std::vector< std::shared_ptr< NodeObject > > Batch
A batch of NodeObjects to write at once.
Batch makePool(std::uint8_t prefix, std::size_t count, std::size_t start=0)
std::size_t averagePayload(Batch const &pool)
std::vector< unsigned char > Blob
Storage for linear binary data.