aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPieter Wuille <pieter@wuille.net>2024-08-08 21:42:30 -0400
committerPieter Wuille <pieter@wuille.net>2024-09-12 15:15:36 -0400
commit9e43e4ce109e98a1ea3f54bbb4de86bc1b92ae4f (patch)
treeef9c758f918773b77e97e9569d5a0dae1ed4eed1 /src
parentb80e6dfe780b3678bb41f2d9d63816f097529b2e (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/cluster_linearize.h75
1 files changed, 59 insertions, 16 deletions
diff --git a/src/cluster_linearize.h b/src/cluster_linearize.h
index 7e74460c7e..ed6abfa4a3 100644
--- a/src/cluster_linearize.h
+++ b/src/cluster_linearize.h
@@ -563,23 +563,60 @@ class SearchCandidateFinder
{
/** Internal RNG. */
InsecureRandomContext m_rng;
- /** Internal dependency graph for the cluster. */
- const DepGraph<SetType>& m_depgraph;
- /** Which transactions are left to do (sorted indices). */
+ /** m_sorted_to_original[i] is the original position that sorted transaction position i had. */
+ std::vector<ClusterIndex> m_sorted_to_original;
+ /** 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;
+ /** 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:
/** Construct a candidate finder for a graph.
*
* @param[in] depgraph Dependency graph for the to-be-linearized cluster.
* @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_depgraph(depgraph),
- m_todo(SetType::Fill(depgraph.TxCount())) {}
+ m_sorted_to_original(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. */
bool AllDone() const noexcept
@@ -608,6 +645,9 @@ public:
{
Assume(!AllDone());
+ // Convert the provided best to internal sorted indices.
+ best.transactions = OriginalToSorted(best.transactions);
+
/** Type for work queue items. */
struct WorkItem
{
@@ -641,12 +681,12 @@ public:
// span multiple components.
auto to_cover = m_todo;
do {
- auto component = m_depgraph.FindConnectedComponent(to_cover);
+ auto component = m_sorted_depgraph.FindConnectedComponent(to_cover);
to_cover -= component;
// 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
// 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));
} while (to_cover.Any());
@@ -695,13 +735,13 @@ public:
const ClusterIndex split = elem.und.First();
// 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,
/*und=*/elem.und - desc);
// Add a work item corresponding to inclusion of the split transaction.
- const auto anc = m_depgraph.Ancestors(split) & m_todo;
- add_fn(/*inc=*/elem.inc.Add(m_depgraph, anc),
+ const auto anc = m_sorted_depgraph.Ancestors(split) & m_todo;
+ add_fn(/*inc=*/elem.inc.Add(m_sorted_depgraph, anc),
/*und=*/elem.und - anc);
// Account for the performed split.
@@ -744,7 +784,9 @@ public:
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};
}
@@ -754,9 +796,10 @@ public:
*/
void MarkDone(const SetType& done) noexcept
{
- Assume(done.Any());
- Assume(done.IsSubsetOf(m_todo));
- m_todo -= done;
+ const auto done_sorted = OriginalToSorted(done);
+ Assume(done_sorted.Any());
+ Assume(done_sorted.IsSubsetOf(m_todo));
+ m_todo -= done_sorted;
}
};