mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-22 06:26:43 +02:00
clusterlin: only start/use search when enough iterations left
This commit is contained in:
parent
bd044356ed
commit
9ad2fe7e69
@ -536,6 +536,12 @@ public:
|
||||
return m_todo.None();
|
||||
}
|
||||
|
||||
/** Count the number of remaining unlinearized transactions. */
|
||||
ClusterIndex NumRemaining() const noexcept
|
||||
{
|
||||
return m_todo.Count();
|
||||
}
|
||||
|
||||
/** Find the best (highest-feerate, smallest among those in case of a tie) ancestor set
|
||||
* among the remaining transactions. Requires !AllDone().
|
||||
*
|
||||
@ -960,10 +966,20 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
|
||||
std::vector<ClusterIndex> linearization;
|
||||
|
||||
AncestorCandidateFinder anc_finder(depgraph);
|
||||
SearchCandidateFinder src_finder(depgraph, rng_seed);
|
||||
std::optional<SearchCandidateFinder<SetType>> src_finder;
|
||||
linearization.reserve(depgraph.TxCount());
|
||||
bool optimal = true;
|
||||
|
||||
// Treat the initialization of SearchCandidateFinder as taking N^2/64 (rounded up) iterations
|
||||
// (largely due to the cost of constructing the internal sorted-by-feerate DepGraph inside
|
||||
// SearchCandidateFinder), a rough approximation based on benchmark. If we don't have that
|
||||
// many, don't start it.
|
||||
uint64_t start_iterations = (uint64_t{depgraph.TxCount()} * depgraph.TxCount() + 63) / 64;
|
||||
if (iterations_left > start_iterations) {
|
||||
iterations_left -= start_iterations;
|
||||
src_finder.emplace(depgraph, rng_seed);
|
||||
}
|
||||
|
||||
/** Chunking of what remains of the old linearization. */
|
||||
LinearizationChunking old_chunking(depgraph, old_linearization);
|
||||
|
||||
@ -976,12 +992,22 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
|
||||
auto best = anc_finder.FindCandidateSet();
|
||||
if (!best_prefix.feerate.IsEmpty() && best_prefix.feerate >= best.feerate) best = best_prefix;
|
||||
|
||||
// Invoke bounded search to update best, with up to half of our remaining iterations as
|
||||
// limit.
|
||||
uint64_t max_iterations_now = (iterations_left + 1) / 2;
|
||||
uint64_t iterations_done_now = 0;
|
||||
std::tie(best, iterations_done_now) = src_finder.FindCandidateSet(max_iterations_now, best);
|
||||
iterations_left -= iterations_done_now;
|
||||
uint64_t max_iterations_now = 0;
|
||||
if (src_finder) {
|
||||
// Treat the invocation of SearchCandidateFinder::FindCandidateSet() as costing N/4
|
||||
// up-front (rounded up) iterations (largely due to the cost of connected-component
|
||||
// splitting), a rough approximation based on benchmarks.
|
||||
uint64_t base_iterations = (anc_finder.NumRemaining() + 3) / 4;
|
||||
if (iterations_left > base_iterations) {
|
||||
// Invoke bounded search to update best, with up to half of our remaining
|
||||
// iterations as limit.
|
||||
iterations_left -= base_iterations;
|
||||
max_iterations_now = (iterations_left + 1) / 2;
|
||||
std::tie(best, iterations_done_now) = src_finder->FindCandidateSet(max_iterations_now, best);
|
||||
iterations_left -= iterations_done_now;
|
||||
}
|
||||
}
|
||||
|
||||
if (iterations_done_now == max_iterations_now) {
|
||||
optimal = false;
|
||||
@ -999,7 +1025,7 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
|
||||
// Update state to reflect best is no longer to be linearized.
|
||||
anc_finder.MarkDone(best.transactions);
|
||||
if (anc_finder.AllDone()) break;
|
||||
src_finder.MarkDone(best.transactions);
|
||||
if (src_finder) src_finder->MarkDone(best.transactions);
|
||||
if (old_chunking.NumChunksLeft() > 0) {
|
||||
old_chunking.MarkDone(best.transactions);
|
||||
}
|
||||
|
@ -458,6 +458,7 @@ FUZZ_TARGET(clusterlin_ancestor_finder)
|
||||
while (todo.Any()) {
|
||||
// Call the ancestor finder's FindCandidateSet for what remains of the graph.
|
||||
assert(!anc_finder.AllDone());
|
||||
assert(todo.Count() == anc_finder.NumRemaining());
|
||||
auto best_anc = anc_finder.FindCandidateSet();
|
||||
// Sanity check the result.
|
||||
assert(best_anc.transactions.Any());
|
||||
@ -489,6 +490,7 @@ FUZZ_TARGET(clusterlin_ancestor_finder)
|
||||
anc_finder.MarkDone(del_set);
|
||||
}
|
||||
assert(anc_finder.AllDone());
|
||||
assert(anc_finder.NumRemaining() == 0);
|
||||
}
|
||||
|
||||
static constexpr auto MAX_SIMPLE_ITERATIONS = 300000;
|
||||
@ -523,6 +525,7 @@ FUZZ_TARGET(clusterlin_search_finder)
|
||||
assert(!smp_finder.AllDone());
|
||||
assert(!exh_finder.AllDone());
|
||||
assert(!anc_finder.AllDone());
|
||||
assert(anc_finder.NumRemaining() == todo.Count());
|
||||
|
||||
// For each iteration, read an iteration count limit from the fuzz input.
|
||||
uint64_t max_iterations = 1;
|
||||
@ -605,6 +608,7 @@ FUZZ_TARGET(clusterlin_search_finder)
|
||||
assert(smp_finder.AllDone());
|
||||
assert(exh_finder.AllDone());
|
||||
assert(anc_finder.AllDone());
|
||||
assert(anc_finder.NumRemaining() == 0);
|
||||
}
|
||||
|
||||
FUZZ_TARGET(clusterlin_linearization_chunking)
|
||||
@ -775,11 +779,16 @@ FUZZ_TARGET(clusterlin_linearize)
|
||||
if (n <= 19 && iter_count > (uint64_t{1} << n)) {
|
||||
assert(optimal);
|
||||
}
|
||||
// Additionally, if the assumption of sqrt(2^k)+1 iterations per step holds, the maximum number
|
||||
// of iterations is also bounded by (2 + sqrt(2)) * (sqrt(2^n) - 1) + n, which is less than
|
||||
// (2 + sqrt(2)) * sqrt(2^n) + n. Subtracting n and squaring gives
|
||||
// (6 + 4 * sqrt(2)) * 2^n < 12 * 2^n.
|
||||
if (n <= 35 && iter_count > n && (iter_count - n) * (iter_count - n) >= uint64_t{12} << n) {
|
||||
// Additionally, if the assumption of sqrt(2^k)+1 iterations per step holds, plus ceil(k/4)
|
||||
// start-up cost per step, plus ceil(n^2/64) start-up cost overall, we can compute the upper
|
||||
// bound for a whole linearization (summing for k=1..n) using the Python expression
|
||||
// [sum((k+3)//4 + int(math.sqrt(2**k)) + 1 for k in range(1, n + 1)) + (n**2 + 63) // 64 for n in range(0, 35)]:
|
||||
static constexpr uint64_t MAX_OPTIMAL_ITERS[] = {
|
||||
0, 4, 8, 12, 18, 26, 37, 51, 70, 97, 133, 182, 251, 346, 480, 666, 927, 1296, 1815, 2545,
|
||||
3576, 5031, 7087, 9991, 14094, 19895, 28096, 39690, 56083, 79263, 112041, 158391, 223936,
|
||||
316629, 447712
|
||||
};
|
||||
if (n < std::size(MAX_OPTIMAL_ITERS) && iter_count >= MAX_OPTIMAL_ITERS[n]) {
|
||||
Assume(optimal);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user