@@ -663,23 +663,27 @@ class SearchCandidateFinder
663663 }
664664};
665665
666- /* * Find a linearization for a cluster.
666+ /* * Find or improve a linearization for a cluster.
667667 *
668668 * @param[in] depgraph Dependency graph of the cluster to be linearized.
669669 * @param[in] max_iterations Upper bound on the number of optimization steps that will be done.
670670 * @param[in] rng_seed A random number seed to control search order. This prevents peers
671671 * from predicting exactly which clusters would be hard for us to
672672 * linearize.
673+ * @param[in] old_linearization An existing linearization for the cluster (which must be
674+ * topologically valid), or empty.
673675 * @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.
675678 * - A boolean indicating whether the result is guaranteed to be
676679 * optimal.
677680 *
678681 * Complexity: O(N * min(max_iterations + N, 2^N)) where N=depgraph.TxCount().
679682 */
680683template <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
682685{
686+ Assume (old_linearization.empty () || old_linearization.size () == depgraph.TxCount ());
683687 if (depgraph.TxCount () == 0 ) return {{}, true };
684688
685689 uint64_t iterations_left = max_iterations;
@@ -690,9 +694,17 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
690694 linearization.reserve (depgraph.TxCount ());
691695 bool optimal = true ;
692696
697+ /* * Chunking of what remains of the old linearization. */
698+ LinearizationChunking old_chunking (depgraph, old_linearization);
699+
693700 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.
695706 auto best = anc_finder.FindCandidateSet ();
707+ if (!best_prefix.feerate .IsEmpty () && best_prefix.feerate >= best.feerate ) best = best_prefix;
696708
697709 // Invoke bounded search to update best, with up to half of our remaining iterations as
698710 // limit.
@@ -703,6 +715,12 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
703715
704716 if (iterations_done_now == max_iterations_now) {
705717 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+ }
706724 }
707725
708726 // Add to output in topological order.
@@ -712,6 +730,9 @@ std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& de
712730 anc_finder.MarkDone (best.transactions );
713731 if (anc_finder.AllDone ()) break ;
714732 src_finder.MarkDone (best.transactions );
733+ if (old_chunking.NumChunksLeft () > 0 ) {
734+ old_chunking.MarkDone (best.transactions );
735+ }
715736 }
716737
717738 return {std::move (linearization), optimal};
0 commit comments