clusterlin: add MergeLinearizations function + fuzz test + benchmark

This commit is contained in:
Pieter Wuille 2024-05-28 21:18:52 -04:00
parent 4f8958d756
commit 04d7a04ea4
3 changed files with 103 additions and 0 deletions

View file

@ -180,6 +180,27 @@ void BenchPostLinearizeWorstCase(ClusterIndex ntx, benchmark::Bench& bench)
});
}
template<typename SetType>
void BenchMergeLinearizationsWorstCase(ClusterIndex ntx, benchmark::Bench& bench)
{
DepGraph<SetType> depgraph;
for (ClusterIndex i = 0; i < ntx; ++i) {
depgraph.AddTransaction({i, 1});
if (i) depgraph.AddDependency(0, i);
}
std::vector<ClusterIndex> lin1;
std::vector<ClusterIndex> 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<BitSet<16>>(16, bench); }
@ -210,6 +231,13 @@ static void PostLinearize64TxWorstCase(benchmark::Bench& bench) { BenchPostLinea
static void PostLinearize75TxWorstCase(benchmark::Bench& bench) { BenchPostLinearizeWorstCase<BitSet<75>>(75, bench); }
static void PostLinearize99TxWorstCase(benchmark::Bench& bench) { BenchPostLinearizeWorstCase<BitSet<99>>(99, bench); }
static void MergeLinearizations16TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<16>>(16, bench); }
static void MergeLinearizations32TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<32>>(32, bench); }
static void MergeLinearizations48TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<48>>(48, bench); }
static void MergeLinearizations64TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<64>>(64, bench); }
static void MergeLinearizations75TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<75>>(75, bench); }
static void MergeLinearizations99TxWorstCase(benchmark::Bench& bench) { BenchMergeLinearizationsWorstCase<BitSet<99>>(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);

View file

@ -985,6 +985,48 @@ void PostLinearize(const DepGraph<SetType>& depgraph, Span<ClusterIndex> 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<typename SetType>
std::vector<ClusterIndex> MergeLinearizations(const DepGraph<SetType>& depgraph, Span<const ClusterIndex> lin1, Span<const ClusterIndex> 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<ClusterIndex> 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<SetType> 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

View file

@ -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<TestBitSet> depgraph;
try {
reader >> Using<DepGraphFormatter>(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);
}