@@ -563,23 +563,60 @@ class SearchCandidateFinder
563
563
{
564
564
/* * Internal RNG. */
565
565
InsecureRandomContext m_rng;
566
- /* * Internal dependency graph for the cluster. */
567
- const DepGraph<SetType>& m_depgraph;
568
- /* * Which transactions are left to do (sorted indices). */
566
+ /* * m_sorted_to_original[i] is the original position that sorted transaction position i had. */
567
+ std::vector<ClusterIndex> m_sorted_to_original;
568
+ /* * m_original_to_sorted[i] is the sorted position original transaction position i has. */
569
+ std::vector<ClusterIndex> m_original_to_sorted;
570
+ /* * Internal dependency graph for the cluster (with transactions in decreasing individual
571
+ * feerate order). */
572
+ DepGraph<SetType> m_sorted_depgraph;
573
+ /* * Which transactions are left to do (indices in m_sorted_depgraph's order). */
569
574
SetType m_todo;
570
575
576
+ /* * Given a set of transactions with sorted indices, get their original indices. */
577
+ SetType SortedToOriginal (const SetType& arg) const noexcept
578
+ {
579
+ SetType ret;
580
+ for (auto pos : arg) ret.Set (m_sorted_to_original[pos]);
581
+ return ret;
582
+ }
583
+
584
+ /* * Given a set of transactions with original indices, get their sorted indices. */
585
+ SetType OriginalToSorted (const SetType& arg) const noexcept
586
+ {
587
+ SetType ret;
588
+ for (auto pos : arg) ret.Set (m_original_to_sorted[pos]);
589
+ return ret;
590
+ }
591
+
571
592
public:
572
593
/* * Construct a candidate finder for a graph.
573
594
*
574
595
* @param[in] depgraph Dependency graph for the to-be-linearized cluster.
575
596
* @param[in] rng_seed A random seed to control the search order.
576
597
*
577
- * Complexity: O(1 ).
598
+ * Complexity: O(N^2) where N=depgraph.Count( ).
578
599
*/
579
- SearchCandidateFinder (const DepGraph<SetType>& depgraph LIFETIMEBOUND , uint64_t rng_seed) noexcept :
600
+ SearchCandidateFinder (const DepGraph<SetType>& depgraph, uint64_t rng_seed) noexcept :
580
601
m_rng (rng_seed),
581
- m_depgraph (depgraph),
582
- m_todo (SetType::Fill(depgraph.TxCount())) {}
602
+ m_sorted_to_original (depgraph.TxCount()),
603
+ m_original_to_sorted (depgraph.TxCount()),
604
+ m_todo (SetType::Fill(depgraph.TxCount()))
605
+ {
606
+ // Determine reordering mapping, by sorting by decreasing feerate.
607
+ std::iota (m_sorted_to_original.begin (), m_sorted_to_original.end (), ClusterIndex{0 });
608
+ std::sort (m_sorted_to_original.begin (), m_sorted_to_original.end (), [&](auto a, auto b) {
609
+ auto feerate_cmp = depgraph.FeeRate (a) <=> depgraph.FeeRate (b);
610
+ if (feerate_cmp == 0 ) return a < b;
611
+ return feerate_cmp > 0 ;
612
+ });
613
+ // Compute reverse mapping.
614
+ for (ClusterIndex i = 0 ; i < depgraph.TxCount (); ++i) {
615
+ m_original_to_sorted[m_sorted_to_original[i]] = i;
616
+ }
617
+ // Compute reordered dependency graph.
618
+ m_sorted_depgraph = DepGraph (depgraph, m_original_to_sorted);
619
+ }
583
620
584
621
/* * Check whether any unlinearized transactions remain. */
585
622
bool AllDone () const noexcept
@@ -608,6 +645,9 @@ class SearchCandidateFinder
608
645
{
609
646
Assume (!AllDone ());
610
647
648
+ // Convert the provided best to internal sorted indices.
649
+ best.transactions = OriginalToSorted (best.transactions );
650
+
611
651
/* * Type for work queue items. */
612
652
struct WorkItem
613
653
{
@@ -641,12 +681,12 @@ class SearchCandidateFinder
641
681
// span multiple components.
642
682
auto to_cover = m_todo;
643
683
do {
644
- auto component = m_depgraph .FindConnectedComponent (to_cover);
684
+ auto component = m_sorted_depgraph .FindConnectedComponent (to_cover);
645
685
to_cover -= component;
646
686
// If best is not provided, set it to the first component, so that during the work
647
687
// processing loop below, and during the add_fn/split_fn calls, we do not need to deal
648
688
// with the best=empty case.
649
- if (best.feerate .IsEmpty ()) best = SetInfo (m_depgraph , component);
689
+ if (best.feerate .IsEmpty ()) best = SetInfo (m_sorted_depgraph , component);
650
690
queue.emplace_back (/* inc=*/ SetInfo<SetType>{}, /* und=*/ std::move (component));
651
691
} while (to_cover.Any ());
652
692
@@ -695,13 +735,13 @@ class SearchCandidateFinder
695
735
const ClusterIndex split = elem.und .First ();
696
736
697
737
// Add a work item corresponding to exclusion of the split transaction.
698
- const auto & desc = m_depgraph .Descendants (split);
738
+ const auto & desc = m_sorted_depgraph .Descendants (split);
699
739
add_fn (/* inc=*/ elem.inc ,
700
740
/* und=*/ elem.und - desc);
701
741
702
742
// Add a work item corresponding to inclusion of the split transaction.
703
- const auto anc = m_depgraph .Ancestors (split) & m_todo;
704
- add_fn (/* inc=*/ elem.inc .Add (m_depgraph , anc),
743
+ const auto anc = m_sorted_depgraph .Ancestors (split) & m_todo;
744
+ add_fn (/* inc=*/ elem.inc .Add (m_sorted_depgraph , anc),
705
745
/* und=*/ elem.und - anc);
706
746
707
747
// Account for the performed split.
@@ -744,7 +784,9 @@ class SearchCandidateFinder
744
784
split_fn (std::move (elem));
745
785
}
746
786
747
- // Return the found best set and the number of iterations performed.
787
+ // Return the found best set (converted to the original transaction indices), and the
788
+ // number of iterations performed.
789
+ best.transactions = SortedToOriginal (best.transactions );
748
790
return {std::move (best), max_iterations - iterations_left};
749
791
}
750
792
@@ -754,9 +796,10 @@ class SearchCandidateFinder
754
796
*/
755
797
void MarkDone (const SetType& done) noexcept
756
798
{
757
- Assume (done.Any ());
758
- Assume (done.IsSubsetOf (m_todo));
759
- m_todo -= done;
799
+ const auto done_sorted = OriginalToSorted (done);
800
+ Assume (done_sorted.Any ());
801
+ Assume (done_sorted.IsSubsetOf (m_todo));
802
+ m_todo -= done_sorted;
760
803
}
761
804
};
762
805
0 commit comments