clusterlin: use feerate-sorted depgraph in SearchCandidateFinder

This is a requirement for a future commit, which will rely on quickly iterating
over transaction sets in decreasing individual feerate order.
This commit is contained in:
Pieter Wuille 2024-08-08 21:42:30 -04:00
parent b80e6dfe78
commit 9e43e4ce10

View file

@ -563,23 +563,60 @@ class SearchCandidateFinder
{ {
/** Internal RNG. */ /** Internal RNG. */
InsecureRandomContext m_rng; InsecureRandomContext m_rng;
/** Internal dependency graph for the cluster. */ /** m_sorted_to_original[i] is the original position that sorted transaction position i had. */
const DepGraph<SetType>& m_depgraph; std::vector<ClusterIndex> m_sorted_to_original;
/** Which transactions are left to do (sorted indices). */ /** m_original_to_sorted[i] is the sorted position original transaction position i has. */
std::vector<ClusterIndex> m_original_to_sorted;
/** Internal dependency graph for the cluster (with transactions in decreasing individual
* feerate order). */
DepGraph<SetType> m_sorted_depgraph;
/** Which transactions are left to do (indices in m_sorted_depgraph's order). */
SetType m_todo; SetType m_todo;
/** Given a set of transactions with sorted indices, get their original indices. */
SetType SortedToOriginal(const SetType& arg) const noexcept
{
SetType ret;
for (auto pos : arg) ret.Set(m_sorted_to_original[pos]);
return ret;
}
/** Given a set of transactions with original indices, get their sorted indices. */
SetType OriginalToSorted(const SetType& arg) const noexcept
{
SetType ret;
for (auto pos : arg) ret.Set(m_original_to_sorted[pos]);
return ret;
}
public: public:
/** Construct a candidate finder for a graph. /** Construct a candidate finder for a graph.
* *
* @param[in] depgraph Dependency graph for the to-be-linearized cluster. * @param[in] depgraph Dependency graph for the to-be-linearized cluster.
* @param[in] rng_seed A random seed to control the search order. * @param[in] rng_seed A random seed to control the search order.
* *
* Complexity: O(1). * Complexity: O(N^2) where N=depgraph.Count().
*/ */
SearchCandidateFinder(const DepGraph<SetType>& depgraph LIFETIMEBOUND, uint64_t rng_seed) noexcept : SearchCandidateFinder(const DepGraph<SetType>& depgraph, uint64_t rng_seed) noexcept :
m_rng(rng_seed), m_rng(rng_seed),
m_depgraph(depgraph), m_sorted_to_original(depgraph.TxCount()),
m_todo(SetType::Fill(depgraph.TxCount())) {} m_original_to_sorted(depgraph.TxCount()),
m_todo(SetType::Fill(depgraph.TxCount()))
{
// Determine reordering mapping, by sorting by decreasing feerate.
std::iota(m_sorted_to_original.begin(), m_sorted_to_original.end(), ClusterIndex{0});
std::sort(m_sorted_to_original.begin(), m_sorted_to_original.end(), [&](auto a, auto b) {
auto feerate_cmp = depgraph.FeeRate(a) <=> depgraph.FeeRate(b);
if (feerate_cmp == 0) return a < b;
return feerate_cmp > 0;
});
// Compute reverse mapping.
for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
m_original_to_sorted[m_sorted_to_original[i]] = i;
}
// Compute reordered dependency graph.
m_sorted_depgraph = DepGraph(depgraph, m_original_to_sorted);
}
/** Check whether any unlinearized transactions remain. */ /** Check whether any unlinearized transactions remain. */
bool AllDone() const noexcept bool AllDone() const noexcept
@ -608,6 +645,9 @@ public:
{ {
Assume(!AllDone()); Assume(!AllDone());
// Convert the provided best to internal sorted indices.
best.transactions = OriginalToSorted(best.transactions);
/** Type for work queue items. */ /** Type for work queue items. */
struct WorkItem struct WorkItem
{ {
@ -641,12 +681,12 @@ public:
// span multiple components. // span multiple components.
auto to_cover = m_todo; auto to_cover = m_todo;
do { do {
auto component = m_depgraph.FindConnectedComponent(to_cover); auto component = m_sorted_depgraph.FindConnectedComponent(to_cover);
to_cover -= component; to_cover -= component;
// If best is not provided, set it to the first component, so that during the work // If best is not provided, set it to the first component, so that during the work
// processing loop below, and during the add_fn/split_fn calls, we do not need to deal // processing loop below, and during the add_fn/split_fn calls, we do not need to deal
// with the best=empty case. // with the best=empty case.
if (best.feerate.IsEmpty()) best = SetInfo(m_depgraph, component); if (best.feerate.IsEmpty()) best = SetInfo(m_sorted_depgraph, component);
queue.emplace_back(/*inc=*/SetInfo<SetType>{}, /*und=*/std::move(component)); queue.emplace_back(/*inc=*/SetInfo<SetType>{}, /*und=*/std::move(component));
} while (to_cover.Any()); } while (to_cover.Any());
@ -695,13 +735,13 @@ public:
const ClusterIndex split = elem.und.First(); const ClusterIndex split = elem.und.First();
// Add a work item corresponding to exclusion of the split transaction. // Add a work item corresponding to exclusion of the split transaction.
const auto& desc = m_depgraph.Descendants(split); const auto& desc = m_sorted_depgraph.Descendants(split);
add_fn(/*inc=*/elem.inc, add_fn(/*inc=*/elem.inc,
/*und=*/elem.und - desc); /*und=*/elem.und - desc);
// Add a work item corresponding to inclusion of the split transaction. // Add a work item corresponding to inclusion of the split transaction.
const auto anc = m_depgraph.Ancestors(split) & m_todo; const auto anc = m_sorted_depgraph.Ancestors(split) & m_todo;
add_fn(/*inc=*/elem.inc.Add(m_depgraph, anc), add_fn(/*inc=*/elem.inc.Add(m_sorted_depgraph, anc),
/*und=*/elem.und - anc); /*und=*/elem.und - anc);
// Account for the performed split. // Account for the performed split.
@ -744,7 +784,9 @@ public:
split_fn(std::move(elem)); split_fn(std::move(elem));
} }
// Return the found best set and the number of iterations performed. // Return the found best set (converted to the original transaction indices), and the
// number of iterations performed.
best.transactions = SortedToOriginal(best.transactions);
return {std::move(best), max_iterations - iterations_left}; return {std::move(best), max_iterations - iterations_left};
} }
@ -754,9 +796,10 @@ public:
*/ */
void MarkDone(const SetType& done) noexcept void MarkDone(const SetType& done) noexcept
{ {
Assume(done.Any()); const auto done_sorted = OriginalToSorted(done);
Assume(done.IsSubsetOf(m_todo)); Assume(done_sorted.Any());
m_todo -= done; Assume(done_sorted.IsSubsetOf(m_todo));
m_todo -= done_sorted;
} }
}; };