@@ -33,6 +33,8 @@ enum class QualityLevel
3333{
3434 /* * This cluster may have multiple disconnected components, which are all NEEDS_RELINEARIZE. */
3535 NEEDS_SPLIT,
36+ /* * This cluster may have multiple disconnected components, which are all ACCEPTABLE. */
37+ NEEDS_SPLIT_ACCEPTABLE,
3638 /* * This cluster has undergone changes that warrant re-linearization. */
3739 NEEDS_RELINEARIZE,
3840 /* * The minimal level of linearization has been performed, but it is not known to be optimal. */
@@ -79,9 +81,10 @@ class Cluster
7981 // Generic helper functions.
8082
8183 /* * Whether the linearization of this Cluster can be exposed. */
82- bool IsAcceptable () const noexcept
84+ bool IsAcceptable (bool after_split = false ) const noexcept
8385 {
84- return m_quality == QualityLevel::ACCEPTABLE || m_quality == QualityLevel::OPTIMAL;
86+ return m_quality == QualityLevel::ACCEPTABLE || m_quality == QualityLevel::OPTIMAL ||
87+ (after_split && m_quality == QualityLevel::NEEDS_SPLIT_ACCEPTABLE);
8588 }
8689 /* * Whether the linearization of this Cluster is optimal. */
8790 bool IsOptimal () const noexcept
@@ -91,7 +94,8 @@ class Cluster
9194 /* * Whether this cluster requires splitting. */
9295 bool NeedsSplitting () const noexcept
9396 {
94- return m_quality == QualityLevel::NEEDS_SPLIT;
97+ return m_quality == QualityLevel::NEEDS_SPLIT ||
98+ m_quality == QualityLevel::NEEDS_SPLIT_ACCEPTABLE;
9599 }
96100 /* * Get the number of transactions in this Cluster. */
97101 LinearizationIndex GetTxCount () const noexcept { return m_linearization.size (); }
@@ -379,26 +383,54 @@ void Cluster::ApplyRemovals(TxGraphImpl& graph, std::span<GraphIndex>& to_remove
379383 --graph.m_txcount ;
380384 } while (!to_remove.empty ());
381385
386+ auto quality = m_quality;
382387 Assume (todo.Any ());
383388 // Wipe from the Cluster's DepGraph (this is O(n) regardless of the number of entries
384389 // removed, so we benefit from batching all the removals).
385390 m_depgraph.RemoveTransactions (todo);
386391 m_mapping.resize (m_depgraph.PositionRange ());
387392
388- // Filter removals out of m_linearization.
389- m_linearization.erase (std::remove_if (
390- m_linearization.begin (),
391- m_linearization.end (),
392- [&](auto pos) { return todo[pos]; }), m_linearization.end ());
393-
394- graph.SetClusterQuality (m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);
393+ // First remove all removals at the end of the linearization.
394+ while (!m_linearization.empty () && todo[m_linearization.back ()]) {
395+ todo.Reset (m_linearization.back ());
396+ m_linearization.pop_back ();
397+ }
398+ if (todo.None ()) {
399+ // If no further removals remain, and thus all removals were at the end, we may be able
400+ // to leave the cluster at a better quality level.
401+ if (IsAcceptable (/* after_split=*/ true )) {
402+ quality = QualityLevel::NEEDS_SPLIT_ACCEPTABLE;
403+ } else {
404+ quality = QualityLevel::NEEDS_SPLIT;
405+ }
406+ } else {
407+ // If more removals remain, filter those out of m_linearization.
408+ m_linearization.erase (std::remove_if (
409+ m_linearization.begin (),
410+ m_linearization.end (),
411+ [&](auto pos) { return todo[pos]; }), m_linearization.end ());
412+ quality = QualityLevel::NEEDS_SPLIT;
413+ }
414+ graph.SetClusterQuality (m_quality, m_setindex, quality);
395415 Updated (graph);
396416}
397417
398418bool Cluster::Split (TxGraphImpl& graph) noexcept
399419{
400420 // This function can only be called when the Cluster needs splitting.
401421 Assume (NeedsSplitting ());
422+ // Determine the new quality the split-off Clusters will have.
423+ QualityLevel new_quality = IsAcceptable (/* after_split=*/ true ) ? QualityLevel::ACCEPTABLE
424+ : QualityLevel::NEEDS_RELINEARIZE;
425+ // If we're going to produce ACCEPTABLE clusters (i.e., when in NEEDS_SPLIT_ACCEPTABLE), we
426+ // need to post-linearize to make sure the split-out versions are all connected (as
427+ // connectivity may have changed by removing part of the cluster). This could be done on each
428+ // resulting split-out cluster separately, but it is simpler to do it once up front before
429+ // splitting. This step is not necessary if the resulting clusters are NEEDS_RELINEARIZE, as
430+ // they will be post-linearized anyway in MakeAcceptable().
431+ if (new_quality == QualityLevel::ACCEPTABLE) {
432+ PostLinearize (m_depgraph, m_linearization);
433+ }
402434 /* * Which positions are still left in this Cluster. */
403435 auto todo = m_depgraph.Positions ();
404436 /* * Mapping from transaction positions in this Cluster to the Cluster where it ends up, and
@@ -412,7 +444,10 @@ bool Cluster::Split(TxGraphImpl& graph) noexcept
412444 if (first && component == todo) {
413445 // The existing Cluster is an entire component. Leave it be, but update its quality.
414446 Assume (todo == m_depgraph.Positions ());
415- graph.SetClusterQuality (m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
447+ graph.SetClusterQuality (m_quality, m_setindex, new_quality);
448+ // If this made the quality ACCEPTABLE or OPTIMAL, we need to compute and cache its
449+ // chunking.
450+ Updated (graph);
416451 return false ;
417452 }
418453 first = false ;
@@ -424,7 +459,7 @@ bool Cluster::Split(TxGraphImpl& graph) noexcept
424459 for (auto i : component) {
425460 remap[i] = {new_cluster.get (), DepGraphIndex (-1 )};
426461 }
427- graph.InsertCluster (std::move (new_cluster), QualityLevel::NEEDS_RELINEARIZE );
462+ graph.InsertCluster (std::move (new_cluster), new_quality );
428463 todo -= component;
429464 }
430465 // Redistribute the transactions.
@@ -696,9 +731,11 @@ void TxGraphImpl::SplitAll() noexcept
696731{
697732 // Before splitting all Cluster, first make sure all removals are applied.
698733 ApplyRemovals ();
699- auto & queue = m_clusters[int (QualityLevel::NEEDS_SPLIT)];
700- while (!queue.empty ()) {
701- Split (*queue.back ().get ());
734+ for (auto quality : {QualityLevel::NEEDS_SPLIT, QualityLevel::NEEDS_SPLIT_ACCEPTABLE}) {
735+ auto & queue = m_clusters[int (quality)];
736+ while (!queue.empty ()) {
737+ Split (*queue.back ().get ());
738+ }
702739 }
703740}
704741
@@ -1221,6 +1258,8 @@ void Cluster::SetFee(TxGraphImpl& graph, DepGraphIndex idx, int64_t fee) noexcep
12211258 m_depgraph.FeeRate (idx).fee = fee;
12221259 if (!NeedsSplitting ()) {
12231260 graph.SetClusterQuality (m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
1261+ } else {
1262+ graph.SetClusterQuality (m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);
12241263 }
12251264 Updated (graph);
12261265}
0 commit comments