From 04d7a04ea426dd0a69b61e3b887867b0277d84d1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 28 May 2024 21:18:52 -0400 Subject: [PATCH] clusterlin: add MergeLinearizations function + fuzz test + benchmark --- src/bench/cluster_linearize.cpp | 35 ++++++++++++++++++++++++ src/cluster_linearize.h | 42 +++++++++++++++++++++++++++++ src/test/fuzz/cluster_linearize.cpp | 26 ++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/src/bench/cluster_linearize.cpp b/src/bench/cluster_linearize.cpp index 30c7ecef01a..269648f4e2f 100644 --- a/src/bench/cluster_linearize.cpp +++ b/src/bench/cluster_linearize.cpp @@ -180,6 +180,27 @@ void BenchPostLinearizeWorstCase(ClusterIndex ntx, benchmark::Bench& bench) }); } +template +void BenchMergeLinearizationsWorstCase(ClusterIndex ntx, benchmark::Bench& bench) +{ + DepGraph depgraph; + for (ClusterIndex i = 0; i < ntx; ++i) { + depgraph.AddTransaction({i, 1}); + if (i) depgraph.AddDependency(0, i); + } + std::vector lin1; + std::vector lin2; + lin1.push_back(0); + lin2.push_back(0); + for (ClusterIndex i = 1; i < ntx; ++i) { + lin1.push_back(i); + lin2.push_back(ntx - i); + } + bench.run([&] { + MergeLinearizations(depgraph, lin1, lin2); + }); +} + } // namespace static void LinearizePerIter16TxWorstCase(benchmark::Bench& bench) { BenchLinearizePerIterWorstCase>(16, bench); } @@ -210,6 +231,13 @@ static void PostLinearize64TxWorstCase(benchmark::Bench& bench) { BenchPostLinea static void PostLinearize75TxWorstCase(benchmark::Bench& bench) { BenchPostLinearizeWorstCase>(75, bench); } static void PostLinearize99TxWorstCase(benchmark::Bench& bench) { BenchPostLinearizeWorstCase>(99, bench); } +static void MergeLinearizations16TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase>(16, bench); } +static void MergeLinearizations32TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase>(32, bench); } +static void MergeLinearizations48TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase>(48, bench); } +static void MergeLinearizations64TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase>(64, bench); } +static void MergeLinearizations75TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase>(75, bench); } +static void MergeLinearizations99TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase>(99, bench); } + BENCHMARK(LinearizePerIter16TxWorstCase, benchmark::PriorityLevel::HIGH); BENCHMARK(LinearizePerIter32TxWorstCase, benchmark::PriorityLevel::HIGH); BENCHMARK(LinearizePerIter48TxWorstCase, benchmark::PriorityLevel::HIGH); @@ -237,3 +265,10 @@ BENCHMARK(PostLinearize48TxWorstCase, benchmark::PriorityLevel::HIGH); BENCHMARK(PostLinearize64TxWorstCase, benchmark::PriorityLevel::HIGH); BENCHMARK(PostLinearize75TxWorstCase, benchmark::PriorityLevel::HIGH); BENCHMARK(PostLinearize99TxWorstCase, benchmark::PriorityLevel::HIGH); + +BENCHMARK(MergeLinearizations16TxWorstCase, benchmark::PriorityLevel::HIGH); +BENCHMARK(MergeLinearizations32TxWorstCase, benchmark::PriorityLevel::HIGH); +BENCHMARK(MergeLinearizations48TxWorstCase, benchmark::PriorityLevel::HIGH); +BENCHMARK(MergeLinearizations64TxWorstCase, benchmark::PriorityLevel::HIGH); +BENCHMARK(MergeLinearizations75TxWorstCase, benchmark::PriorityLevel::HIGH); +BENCHMARK(MergeLinearizations99TxWorstCase, benchmark::PriorityLevel::HIGH); diff --git a/src/cluster_linearize.h b/src/cluster_linearize.h index 1e02d9fc3b6..ced90c7bd20 100644 --- a/src/cluster_linearize.h +++ b/src/cluster_linearize.h @@ -985,6 +985,48 @@ void PostLinearize(const DepGraph& depgraph, Span lineari } } +/** Merge two linearizations for the same cluster into one that is as good as both. + * + * Complexity: O(N^2) where N=depgraph.TxCount(); O(N) if both inputs are identical. + */ +template +std::vector MergeLinearizations(const DepGraph& depgraph, Span lin1, Span lin2) +{ + Assume(lin1.size() == depgraph.TxCount()); + Assume(lin2.size() == depgraph.TxCount()); + + /** Chunkings of what remains of both input linearizations. */ + LinearizationChunking chunking1(depgraph, lin1), chunking2(depgraph, lin2); + /** Output linearization. */ + std::vector ret; + if (depgraph.TxCount() == 0) return ret; + ret.reserve(depgraph.TxCount()); + + while (true) { + // As long as we are not done, both linearizations must have chunks left. + Assume(chunking1.NumChunksLeft() > 0); + Assume(chunking2.NumChunksLeft() > 0); + // Find the set to output by taking the best remaining chunk, and then intersecting it with + // prefixes of remaining chunks of the other linearization. + SetInfo best; + const auto& lin1_firstchunk = chunking1.GetChunk(0); + const auto& lin2_firstchunk = chunking2.GetChunk(0); + if (lin2_firstchunk.feerate >> lin1_firstchunk.feerate) { + best = chunking1.IntersectPrefixes(lin2_firstchunk); + } else { + best = chunking2.IntersectPrefixes(lin1_firstchunk); + } + // Append the result to the output and mark it as done. + depgraph.AppendTopo(ret, best.transactions); + chunking1.MarkDone(best.transactions); + if (chunking1.NumChunksLeft() == 0) break; + chunking2.MarkDone(best.transactions); + } + + Assume(ret.size() == depgraph.TxCount()); + return ret; +} + } // namespace cluster_linearize #endif // BITCOIN_CLUSTER_LINEARIZE_H diff --git a/src/test/fuzz/cluster_linearize.cpp b/src/test/fuzz/cluster_linearize.cpp index 2412db5c1b7..2dfdfbb41de 100644 --- a/src/test/fuzz/cluster_linearize.cpp +++ b/src/test/fuzz/cluster_linearize.cpp @@ -929,3 +929,29 @@ FUZZ_TARGET(clusterlin_postlinearize_moved_leaf) auto cmp = CompareChunks(new_chunking, old_chunking); assert(cmp >= 0); } + +FUZZ_TARGET(clusterlin_merge) +{ + // Construct an arbitrary graph from the fuzz input. + SpanReader reader(buffer); + DepGraph depgraph; + try { + reader >> Using(depgraph); + } catch (const std::ios_base::failure&) {} + + // Retrieve two linearizations from the fuzz input. + auto lin1 = ReadLinearization(depgraph, reader); + auto lin2 = ReadLinearization(depgraph, reader); + + // Merge the two. + auto lin_merged = MergeLinearizations(depgraph, lin1, lin2); + + // Compute chunkings and compare. + auto chunking1 = ChunkLinearization(depgraph, lin1); + auto chunking2 = ChunkLinearization(depgraph, lin2); + auto chunking_merged = ChunkLinearization(depgraph, lin_merged); + auto cmp1 = CompareChunks(chunking_merged, chunking1); + assert(cmp1 >= 0); + auto cmp2 = CompareChunks(chunking_merged, chunking2); + assert(cmp2 >= 0); +}