Skip to content

Commit 04d7a04

Browse files
committed
clusterlin: add MergeLinearizations function + fuzz test + benchmark
1 parent 4f8958d commit 04d7a04

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

src/bench/cluster_linearize.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,27 @@ void BenchPostLinearizeWorstCase(ClusterIndex ntx, benchmark::Bench& bench)
180180
});
181181
}
182182

183+
template<typename SetType>
184+
void BenchMergeLinearizationsWorstCase(ClusterIndex ntx, benchmark::Bench& bench)
185+
{
186+
DepGraph<SetType> depgraph;
187+
for (ClusterIndex i = 0; i < ntx; ++i) {
188+
depgraph.AddTransaction({i, 1});
189+
if (i) depgraph.AddDependency(0, i);
190+
}
191+
std::vector<ClusterIndex> lin1;
192+
std::vector<ClusterIndex> lin2;
193+
lin1.push_back(0);
194+
lin2.push_back(0);
195+
for (ClusterIndex i = 1; i < ntx; ++i) {
196+
lin1.push_back(i);
197+
lin2.push_back(ntx - i);
198+
}
199+
bench.run([&] {
200+
MergeLinearizations(depgraph, lin1, lin2);
201+
});
202+
}
203+
183204
} // namespace
184205

185206
static void LinearizePerIter16TxWorstCase(benchmark::Bench& bench) { BenchLinearizePerIterWorstCase<BitSet<16>>(16, bench); }
@@ -210,6 +231,13 @@ static void PostLinearize64TxWorstCase(benchmark::Bench& bench) { BenchPostLinea
210231
static void PostLinearize75TxWorstCase(benchmark::Bench& bench) { BenchPostLinearizeWorstCase<BitSet<75>>(75, bench); }
211232
static void PostLinearize99TxWorstCase(benchmark::Bench& bench) { BenchPostLinearizeWorstCase<BitSet<99>>(99, bench); }
212233

234+
static void MergeLinearizations16TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<16>>(16, bench); }
235+
static void MergeLinearizations32TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<32>>(32, bench); }
236+
static void MergeLinearizations48TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<48>>(48, bench); }
237+
static void MergeLinearizations64TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<64>>(64, bench); }
238+
static void MergeLinearizations75TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<75>>(75, bench); }
239+
static void MergeLinearizations99TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<99>>(99, bench); }
240+
213241
BENCHMARK(LinearizePerIter16TxWorstCase, benchmark::PriorityLevel::HIGH);
214242
BENCHMARK(LinearizePerIter32TxWorstCase, benchmark::PriorityLevel::HIGH);
215243
BENCHMARK(LinearizePerIter48TxWorstCase, benchmark::PriorityLevel::HIGH);
@@ -237,3 +265,10 @@ BENCHMARK(PostLinearize48TxWorstCase, benchmark::PriorityLevel::HIGH);
237265
BENCHMARK(PostLinearize64TxWorstCase, benchmark::PriorityLevel::HIGH);
238266
BENCHMARK(PostLinearize75TxWorstCase, benchmark::PriorityLevel::HIGH);
239267
BENCHMARK(PostLinearize99TxWorstCase, benchmark::PriorityLevel::HIGH);
268+
269+
BENCHMARK(MergeLinearizations16TxWorstCase, benchmark::PriorityLevel::HIGH);
270+
BENCHMARK(MergeLinearizations32TxWorstCase, benchmark::PriorityLevel::HIGH);
271+
BENCHMARK(MergeLinearizations48TxWorstCase, benchmark::PriorityLevel::HIGH);
272+
BENCHMARK(MergeLinearizations64TxWorstCase, benchmark::PriorityLevel::HIGH);
273+
BENCHMARK(MergeLinearizations75TxWorstCase, benchmark::PriorityLevel::HIGH);
274+
BENCHMARK(MergeLinearizations99TxWorstCase, benchmark::PriorityLevel::HIGH);

src/cluster_linearize.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,48 @@ void PostLinearize(const DepGraph<SetType>& depgraph, Span<ClusterIndex> lineari
985985
}
986986
}
987987

988+
/** Merge two linearizations for the same cluster into one that is as good as both.
989+
*
990+
* Complexity: O(N^2) where N=depgraph.TxCount(); O(N) if both inputs are identical.
991+
*/
992+
template<typename SetType>
993+
std::vector<ClusterIndex> MergeLinearizations(const DepGraph<SetType>& depgraph, Span<const ClusterIndex> lin1, Span<const ClusterIndex> lin2)
994+
{
995+
Assume(lin1.size() == depgraph.TxCount());
996+
Assume(lin2.size() == depgraph.TxCount());
997+
998+
/** Chunkings of what remains of both input linearizations. */
999+
LinearizationChunking chunking1(depgraph, lin1), chunking2(depgraph, lin2);
1000+
/** Output linearization. */
1001+
std::vector<ClusterIndex> ret;
1002+
if (depgraph.TxCount() == 0) return ret;
1003+
ret.reserve(depgraph.TxCount());
1004+
1005+
while (true) {
1006+
// As long as we are not done, both linearizations must have chunks left.
1007+
Assume(chunking1.NumChunksLeft() > 0);
1008+
Assume(chunking2.NumChunksLeft() > 0);
1009+
// Find the set to output by taking the best remaining chunk, and then intersecting it with
1010+
// prefixes of remaining chunks of the other linearization.
1011+
SetInfo<SetType> best;
1012+
const auto& lin1_firstchunk = chunking1.GetChunk(0);
1013+
const auto& lin2_firstchunk = chunking2.GetChunk(0);
1014+
if (lin2_firstchunk.feerate >> lin1_firstchunk.feerate) {
1015+
best = chunking1.IntersectPrefixes(lin2_firstchunk);
1016+
} else {
1017+
best = chunking2.IntersectPrefixes(lin1_firstchunk);
1018+
}
1019+
// Append the result to the output and mark it as done.
1020+
depgraph.AppendTopo(ret, best.transactions);
1021+
chunking1.MarkDone(best.transactions);
1022+
if (chunking1.NumChunksLeft() == 0) break;
1023+
chunking2.MarkDone(best.transactions);
1024+
}
1025+
1026+
Assume(ret.size() == depgraph.TxCount());
1027+
return ret;
1028+
}
1029+
9881030
} // namespace cluster_linearize
9891031

9901032
#endif // BITCOIN_CLUSTER_LINEARIZE_H

src/test/fuzz/cluster_linearize.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,3 +929,29 @@ FUZZ_TARGET(clusterlin_postlinearize_moved_leaf)
929929
auto cmp = CompareChunks(new_chunking, old_chunking);
930930
assert(cmp >= 0);
931931
}
932+
933+
FUZZ_TARGET(clusterlin_merge)
934+
{
935+
// Construct an arbitrary graph from the fuzz input.
936+
SpanReader reader(buffer);
937+
DepGraph<TestBitSet> depgraph;
938+
try {
939+
reader >> Using<DepGraphFormatter>(depgraph);
940+
} catch (const std::ios_base::failure&) {}
941+
942+
// Retrieve two linearizations from the fuzz input.
943+
auto lin1 = ReadLinearization(depgraph, reader);
944+
auto lin2 = ReadLinearization(depgraph, reader);
945+
946+
// Merge the two.
947+
auto lin_merged = MergeLinearizations(depgraph, lin1, lin2);
948+
949+
// Compute chunkings and compare.
950+
auto chunking1 = ChunkLinearization(depgraph, lin1);
951+
auto chunking2 = ChunkLinearization(depgraph, lin2);
952+
auto chunking_merged = ChunkLinearization(depgraph, lin_merged);
953+
auto cmp1 = CompareChunks(chunking_merged, chunking1);
954+
assert(cmp1 >= 0);
955+
auto cmp2 = CompareChunks(chunking_merged, chunking2);
956+
assert(cmp2 >= 0);
957+
}

0 commit comments

Comments
 (0)