@@ -663,23 +663,27 @@ class SearchCandidateFinder
663
663
}
664
664
};
665
665
666
- /* * Find a linearization for a cluster.
666
+ /* * Find or improve a linearization for a cluster.
667
667
*
668
668
* @param[in] depgraph Dependency graph of the cluster to be linearized.
669
669
* @param[in] max_iterations Upper bound on the number of optimization steps that will be done.
670
670
* @param[in] rng_seed A random number seed to control search order. This prevents peers
671
671
* from predicting exactly which clusters would be hard for us to
672
672
* linearize.
673
+ * @param[in] old_linearization An existing linearization for the cluster (which must be
674
+ * topologically valid), or empty.
673
675
* @return A pair of:
674
- * - The resulting linearization.
676
+ * - The resulting linearization. It is guaranteed to be at least as
677
+ * good (in the feerate diagram sense) as old_linearization.
675
678
* - A boolean indicating whether the result is guaranteed to be
676
679
* optimal.
677
680
*
678
681
* Complexity: O(N * min(max_iterations + N, 2^N)) where N=depgraph.TxCount().
679
682
*/
680
683
template <typename SetType>
681
- std::pair<std::vector<ClusterIndex>, bool > Linearize (const DepGraph<SetType>& depgraph, uint64_t max_iterations, uint64_t rng_seed) noexcept
684
+ std::pair<std::vector<ClusterIndex>, bool > Linearize (const DepGraph<SetType>& depgraph, uint64_t max_iterations, uint64_t rng_seed, Span< const ClusterIndex> old_linearization = {} ) noexcept
682
685
{
686
+ Assume (old_linearization.empty () || old_linearization.size () == depgraph.TxCount ());
683
687
if (depgraph.TxCount () == 0 ) return {{}, true };
684
688
685
689
uint64_t iterations_left = max_iterations;
@@ -690,9 +694,17 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
690
694
linearization.reserve (depgraph.TxCount ());
691
695
bool optimal = true ;
692
696
697
+ /* * Chunking of what remains of the old linearization. */
698
+ LinearizationChunking old_chunking (depgraph, old_linearization);
699
+
693
700
while (true ) {
694
- // Initialize best as the best remaining ancestor set.
701
+ // Find the highest-feerate prefix of the remainder of old_linearization.
702
+ SetInfo<SetType> best_prefix;
703
+ if (old_chunking.NumChunksLeft ()) best_prefix = old_chunking.GetChunk (0 );
704
+
705
+ // Then initialize best to be either the best remaining ancestor set, or the first chunk.
695
706
auto best = anc_finder.FindCandidateSet ();
707
+ if (!best_prefix.feerate .IsEmpty () && best_prefix.feerate >= best.feerate ) best = best_prefix;
696
708
697
709
// Invoke bounded search to update best, with up to half of our remaining iterations as
698
710
// limit.
@@ -703,6 +715,12 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
703
715
704
716
if (iterations_done_now == max_iterations_now) {
705
717
optimal = false ;
718
+ // If the search result is not (guaranteed to be) optimal, run intersections to make
719
+ // sure we don't pick something that makes us unable to reach further diagram points
720
+ // of the old linearization.
721
+ if (old_chunking.NumChunksLeft () > 0 ) {
722
+ best = old_chunking.Intersect (best);
723
+ }
706
724
}
707
725
708
726
// Add to output in topological order.
@@ -712,6 +730,9 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
712
730
anc_finder.MarkDone (best.transactions );
713
731
if (anc_finder.AllDone ()) break ;
714
732
src_finder.MarkDone (best.transactions );
733
+ if (old_chunking.NumChunksLeft () > 0 ) {
734
+ old_chunking.MarkDone (best.transactions );
735
+ }
715
736
}
716
737
717
738
return {std::move (linearization), optimal};
0 commit comments