@@ -279,6 +279,14 @@ struct SetInfo
279
279
explicit SetInfo (const DepGraph<SetType>& depgraph, const SetType& txn) noexcept :
280
280
transactions(txn), feerate(depgraph.FeeRate(txn)) {}
281
281
282
+ /* * Add a transaction to this SetInfo (which must not yet be in it). */
283
+ void Set (const DepGraph<SetType>& depgraph, ClusterIndex pos) noexcept
284
+ {
285
+ Assume (!transactions[pos]);
286
+ transactions.Set (pos);
287
+ feerate += depgraph.FeeRate (pos);
288
+ }
289
+
282
290
/* * Add the transactions of other to this SetInfo (no overlap allowed). */
283
291
SetInfo& operator |=(const SetInfo& other) noexcept
284
292
{
@@ -658,16 +666,24 @@ class SearchCandidateFinder
658
666
/* * Set of undecided transactions. This must be a subset of m_todo, and have no overlap
659
667
* with inc. The set (inc | und) must be topologically valid. */
660
668
SetType und;
669
+ /* * (Only when inc is not empty) The best feerate of any superset of inc that is also a
670
+ * subset of (inc | und), without requiring it to be topologically valid. It forms a
671
+ * conservative upper bound on how good a set this work item can give rise to. */
672
+ FeeFrac pot_feerate;
661
673
662
674
/* * Construct a new work item. */
663
- WorkItem (SetInfo<SetType>&& i, SetType&& u) noexcept :
664
- inc (std::move(i)), und(std::move(u)) {}
675
+ WorkItem (SetInfo<SetType>&& i, SetType&& u, FeeFrac&& p_f) noexcept :
676
+ inc (std::move(i)), und(std::move(u)), pot_feerate(std::move(p_f))
677
+ {
678
+ Assume (pot_feerate.IsEmpty () == inc.feerate .IsEmpty ());
679
+ }
665
680
666
681
/* * Swap two WorkItems. */
667
682
void Swap (WorkItem& other) noexcept
668
683
{
669
684
swap (inc, other.inc );
670
685
swap (und, other.und );
686
+ swap (pot_feerate, other.pot_feerate );
671
687
}
672
688
};
673
689
@@ -687,7 +703,9 @@ class SearchCandidateFinder
687
703
// processing loop below, and during the add_fn/split_fn calls, we do not need to deal
688
704
// with the best=empty case.
689
705
if (best.feerate .IsEmpty ()) best = SetInfo (m_sorted_depgraph, component);
690
- queue.emplace_back (/* inc=*/ SetInfo<SetType>{}, /* und=*/ std::move (component));
706
+ queue.emplace_back (/* inc=*/ SetInfo<SetType>{},
707
+ /* und=*/ std::move (component),
708
+ /* pot_feerate=*/ FeeFrac{});
691
709
} while (to_cover.Any ());
692
710
693
711
/* * Local copy of the iteration limit. */
@@ -700,23 +718,44 @@ class SearchCandidateFinder
700
718
* - und: the "und" value for the new work item ((inc | und) must be topological).
701
719
*/
702
720
auto add_fn = [&](SetInfo<SetType> inc, SetType und) noexcept {
721
+ /* * SetInfo object with the set whose feerate will become the new work item's
722
+ * pot_feerate. It starts off equal to inc. */
723
+ auto pot = inc;
703
724
if (!inc.feerate .IsEmpty ()) {
725
+ // Add entries to pot.
726
+ for (auto pos : und) {
727
+ // Determine if adding transaction pos to pot (ignoring topology) would improve
728
+ // it. If not, we're done updating pot. This relies on the fact that
729
+ // m_sorted_depgraph, and thus the transactions iterated over, are in decreasing
730
+ // individual feerate order.
731
+ if (!(m_sorted_depgraph.FeeRate (pos) >> pot.feerate )) break ;
732
+ pot.Set (m_sorted_depgraph, pos);
733
+ }
734
+
704
735
// If inc's feerate is better than best's, remember it as our new best.
705
736
if (inc.feerate > best.feerate ) {
706
737
best = inc;
707
738
}
739
+
740
+ // If no potential transactions exist beyond the already included ones, no
741
+ // improvement is possible anymore.
742
+ if (pot.feerate .size == inc.feerate .size ) return ;
743
+ // At this point und must be non-empty. If it were empty then pot would equal inc.
744
+ Assume (und.Any ());
708
745
} else {
709
746
Assume (inc.transactions .None ());
747
+ // If inc is empty, we just make sure there are undecided transactions left to
748
+ // split on.
749
+ if (und.None ()) return ;
710
750
}
711
751
712
- // Make sure there are undecided transactions left to split on.
713
- if (und.None ()) return ;
714
-
715
752
// Actually construct a new work item on the queue. Due to the switch to DFS when queue
716
753
// space runs out (see below), we know that no reallocation of the queue should ever
717
754
// occur.
718
755
Assume (queue.size () < queue.capacity ());
719
- queue.emplace_back (/* inc=*/ std::move (inc), /* und=*/ std::move (und));
756
+ queue.emplace_back (/* inc=*/ std::move (inc),
757
+ /* und=*/ std::move (und),
758
+ /* pot_feerate=*/ std::move (pot.feerate ));
720
759
};
721
760
722
761
/* * Internal process function. It takes an existing work item, and splits it in two: one
@@ -730,9 +769,21 @@ class SearchCandidateFinder
730
769
Assume (elem.inc .transactions .IsSubsetOf (m_todo) && elem.und .IsSubsetOf (m_todo));
731
770
// Included transactions cannot be undecided.
732
771
Assume (!elem.inc .transactions .Overlaps (elem.und ));
772
+ // If pot is empty, then so is inc.
773
+ Assume (elem.inc .feerate .IsEmpty () == elem.pot_feerate .IsEmpty ());
774
+
775
+ const ClusterIndex first = elem.und .First ();
776
+ if (!elem.inc .feerate .IsEmpty ()) {
777
+ // We can ignore any queue item whose potential feerate isn't better than the best
778
+ // seen so far.
779
+ if (elem.pot_feerate <= best.feerate ) return ;
780
+ } else {
781
+ // In case inc is empty use a simpler alternative check.
782
+ if (m_sorted_depgraph.FeeRate (first) <= best.feerate ) return ;
783
+ }
733
784
734
785
// Pick the first undecided transaction as the one to split on.
735
- const ClusterIndex split = elem. und . First () ;
786
+ const ClusterIndex split = first ;
736
787
737
788
// Add a work item corresponding to exclusion of the split transaction.
738
789
const auto & desc = m_sorted_depgraph.Descendants (split);
0 commit comments