Skip to content

Commit d9b235e

Browse files
committed
bench: Candidate finding and linearization benchmarks
Add benchmarks for known bad graphs for the purpose of search (as an upper bound on work per search iterations) and ancestor sorting (as an upper bound on linearization work with no search iterations).
1 parent 46aad9b commit d9b235e

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

src/Makefile.bench.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ bench_bench_bitcoin_SOURCES = \
2525
bench/checkblock.cpp \
2626
bench/checkblockindex.cpp \
2727
bench/checkqueue.cpp \
28+
bench/cluster_linearize.cpp \
2829
bench/crypto_hash.cpp \
2930
bench/data.cpp \
3031
bench/data.h \

src/bench/cluster_linearize.cpp

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// Copyright (c) The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <bench/bench.h>
6+
7+
#include <util/bitset.h>
8+
#include <cluster_linearize.h>
9+
10+
using namespace cluster_linearize;
11+
12+
namespace {
13+
14+
/** Construct a linear graph. These are pessimal for AncestorCandidateFinder, as they maximize
15+
* the number of ancestor set feerate updates. The best ancestor set is always the topmost
16+
* remaining transaction, whose removal requires updating all remaining transactions' ancestor
17+
* set feerates. */
18+
template<typename SetType>
19+
DepGraph<SetType> MakeLinearGraph(ClusterIndex ntx)
20+
{
21+
DepGraph<SetType> depgraph;
22+
for (ClusterIndex i = 0; i < ntx; ++i) {
23+
depgraph.AddTransaction({-int32_t(i), 1});
24+
if (i > 0) depgraph.AddDependency(i - 1, i);
25+
}
26+
return depgraph;
27+
}
28+
29+
// Construct a difficult graph. These need at least sqrt(2^(n-1)) iterations in the best
30+
// known algorithms (purely empirically determined).
31+
template<typename SetType>
32+
DepGraph<SetType> MakeHardGraph(ClusterIndex ntx)
33+
{
34+
DepGraph<SetType> depgraph;
35+
for (ClusterIndex i = 0; i < ntx; ++i) {
36+
if (ntx & 1) {
37+
// Odd cluster size.
38+
//
39+
// Mermaid diagram code for the resulting cluster for 11 transactions:
40+
// ```mermaid
41+
// graph BT
42+
// T0["T0: 1/2"];T1["T1: 14/2"];T2["T2: 6/1"];T3["T3: 5/1"];T4["T4: 7/1"];
43+
// T5["T5: 5/1"];T6["T6: 7/1"];T7["T7: 5/1"];T8["T8: 7/1"];T9["T9: 5/1"];
44+
// T10["T10: 7/1"];
45+
// T1-->T0;T1-->T2;T3-->T2;T4-->T3;T4-->T5;T6-->T5;T4-->T7;T8-->T7;T4-->T9;T10-->T9;
46+
// ```
47+
if (i == 0) {
48+
depgraph.AddTransaction({1, 2});
49+
} else if (i == 1) {
50+
depgraph.AddTransaction({14, 2});
51+
depgraph.AddDependency(0, 1);
52+
} else if (i == 2) {
53+
depgraph.AddTransaction({6, 1});
54+
depgraph.AddDependency(2, 1);
55+
} else if (i == 3) {
56+
depgraph.AddTransaction({5, 1});
57+
depgraph.AddDependency(2, 3);
58+
} else if ((i & 1) == 0) {
59+
depgraph.AddTransaction({7, 1});
60+
depgraph.AddDependency(i - 1, i);
61+
} else {
62+
depgraph.AddTransaction({5, 1});
63+
depgraph.AddDependency(i, 4);
64+
}
65+
} else {
66+
// Even cluster size.
67+
//
68+
// Mermaid diagram code for the resulting cluster for 10 transactions:
69+
// ```mermaid
70+
// graph BT
71+
// T0["T0: 1"];T1["T1: 3"];T2["T2: 1"];T3["T3: 4"];T4["T4: 0"];T5["T5: 4"];T6["T6: 0"];
72+
// T7["T7: 4"];T8["T8: 0"];T9["T9: 4"];
73+
// T1-->T0;T2-->T0;T3-->T2;T3-->T4;T5-->T4;T3-->T6;T7-->T6;T3-->T8;T9-->T8;
74+
// ```
75+
if (i == 0) {
76+
depgraph.AddTransaction({1, 1});
77+
} else if (i == 1) {
78+
depgraph.AddTransaction({3, 1});
79+
depgraph.AddDependency(0, 1);
80+
} else if (i == 2) {
81+
depgraph.AddTransaction({1, 1});
82+
depgraph.AddDependency(0, 2);
83+
} else if (i & 1) {
84+
depgraph.AddTransaction({4, 1});
85+
depgraph.AddDependency(i - 1, i);
86+
} else {
87+
depgraph.AddTransaction({0, 1});
88+
depgraph.AddDependency(i, 3);
89+
}
90+
}
91+
}
92+
return depgraph;
93+
}
94+
95+
/** Benchmark that does search-based candidate finding with 10000 iterations.
96+
*
97+
* Its goal is measuring how much time every additional search iteration in linearization costs.
98+
*/
99+
template<typename SetType>
100+
void BenchLinearizePerIterWorstCase(ClusterIndex ntx, benchmark::Bench& bench)
101+
{
102+
const auto depgraph = MakeHardGraph<SetType>(ntx);
103+
const auto iter_limit = std::min<uint64_t>(10000, uint64_t{1} << (ntx / 2 - 1));
104+
bench.batch(iter_limit).unit("iters").run([&] {
105+
SearchCandidateFinder finder(depgraph);
106+
auto [candidate, iters_performed] = finder.FindCandidateSet(iter_limit, {});
107+
assert(iters_performed == iter_limit);
108+
});
109+
}
110+
111+
/** Benchmark for linearization of a trivial linear graph using just ancestor sort.
112+
*
113+
* Its goal is measuring how much time linearization may take without any search iterations.
114+
*
115+
* If P is the resulting time of BenchLinearizePerIterWorstCase, and N is the resulting time of
116+
* BenchLinearizeNoItersWorstCase, then an invocation of Linearize with max_iterations=m should
117+
* take no more than roughly N+m*P time. This may however be an overestimate, as the worst cases
118+
* do not coincide (the ones that are worst for linearization without any search happen to be ones
119+
* that do not need many search iterations).
120+
*/
121+
template<typename SetType>
122+
void BenchLinearizeNoItersWorstCase(ClusterIndex ntx, benchmark::Bench& bench)
123+
{
124+
const auto depgraph = MakeLinearGraph<SetType>(ntx);
125+
bench.run([&] {
126+
Linearize(depgraph, /*max_iterations=*/0);
127+
});
128+
}
129+
130+
} // namespace
131+
132+
static void LinearizePerIter16TxWorstCase(benchmark::Bench& bench) { BenchLinearizePerIterWorstCase<BitSet<16>>(16, bench); }
133+
static void LinearizePerIter32TxWorstCase(benchmark::Bench& bench) { BenchLinearizePerIterWorstCase<BitSet<32>>(32, bench); }
134+
static void LinearizePerIter48TxWorstCase(benchmark::Bench& bench) { BenchLinearizePerIterWorstCase<BitSet<48>>(48, bench); }
135+
static void LinearizePerIter64TxWorstCase(benchmark::Bench& bench) { BenchLinearizePerIterWorstCase<BitSet<64>>(64, bench); }
136+
static void LinearizePerIter75TxWorstCase(benchmark::Bench& bench) { BenchLinearizePerIterWorstCase<BitSet<75>>(75, bench); }
137+
static void LinearizePerIter99TxWorstCase(benchmark::Bench& bench) { BenchLinearizePerIterWorstCase<BitSet<99>>(99, bench); }
138+
139+
static void LinearizeNoIters16TxWorstCase(benchmark::Bench& bench) { BenchLinearizeNoItersWorstCase<BitSet<16>>(16, bench); }
140+
static void LinearizeNoIters32TxWorstCase(benchmark::Bench& bench) { BenchLinearizeNoItersWorstCase<BitSet<32>>(32, bench); }
141+
static void LinearizeNoIters48TxWorstCase(benchmark::Bench& bench) { BenchLinearizeNoItersWorstCase<BitSet<48>>(48, bench); }
142+
static void LinearizeNoIters64TxWorstCase(benchmark::Bench& bench) { BenchLinearizeNoItersWorstCase<BitSet<64>>(64, bench); }
143+
static void LinearizeNoIters75TxWorstCase(benchmark::Bench& bench) { BenchLinearizeNoItersWorstCase<BitSet<75>>(75, bench); }
144+
static void LinearizeNoIters99TxWorstCase(benchmark::Bench& bench) { BenchLinearizeNoItersWorstCase<BitSet<99>>(99, bench); }
145+
146+
BENCHMARK(LinearizePerIter16TxWorstCase, benchmark::PriorityLevel::HIGH);
147+
BENCHMARK(LinearizePerIter32TxWorstCase, benchmark::PriorityLevel::HIGH);
148+
BENCHMARK(LinearizePerIter48TxWorstCase, benchmark::PriorityLevel::HIGH);
149+
BENCHMARK(LinearizePerIter64TxWorstCase, benchmark::PriorityLevel::HIGH);
150+
BENCHMARK(LinearizePerIter75TxWorstCase, benchmark::PriorityLevel::HIGH);
151+
BENCHMARK(LinearizePerIter99TxWorstCase, benchmark::PriorityLevel::HIGH);
152+
153+
BENCHMARK(LinearizeNoIters16TxWorstCase, benchmark::PriorityLevel::HIGH);
154+
BENCHMARK(LinearizeNoIters32TxWorstCase, benchmark::PriorityLevel::HIGH);
155+
BENCHMARK(LinearizeNoIters48TxWorstCase, benchmark::PriorityLevel::HIGH);
156+
BENCHMARK(LinearizeNoIters64TxWorstCase, benchmark::PriorityLevel::HIGH);
157+
BENCHMARK(LinearizeNoIters75TxWorstCase, benchmark::PriorityLevel::HIGH);
158+
BENCHMARK(LinearizeNoIters99TxWorstCase, benchmark::PriorityLevel::HIGH);

0 commit comments

Comments
 (0)