@@ -33,6 +33,8 @@ enum class QualityLevel
33
33
{
34
34
/* * This cluster may have multiple disconnected components, which are all NEEDS_RELINEARIZE. */
35
35
NEEDS_SPLIT,
36
+ /* * This cluster may have multiple disconnected components, which are all ACCEPTABLE. */
37
+ NEEDS_SPLIT_ACCEPTABLE,
36
38
/* * This cluster has undergone changes that warrant re-linearization. */
37
39
NEEDS_RELINEARIZE,
38
40
/* * The minimal level of linearization has been performed, but it is not known to be optimal. */
@@ -79,9 +81,10 @@ class Cluster
79
81
// Generic helper functions.
80
82
81
83
/* * Whether the linearization of this Cluster can be exposed. */
82
- bool IsAcceptable () const noexcept
84
+ bool IsAcceptable (bool after_split = false ) const noexcept
83
85
{
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);
85
88
}
86
89
/* * Whether the linearization of this Cluster is optimal. */
87
90
bool IsOptimal () const noexcept
@@ -91,7 +94,8 @@ class Cluster
91
94
/* * Whether this cluster requires splitting. */
92
95
bool NeedsSplitting () const noexcept
93
96
{
94
- return m_quality == QualityLevel::NEEDS_SPLIT;
97
+ return m_quality == QualityLevel::NEEDS_SPLIT ||
98
+ m_quality == QualityLevel::NEEDS_SPLIT_ACCEPTABLE;
95
99
}
96
100
/* * Get the number of transactions in this Cluster. */
97
101
LinearizationIndex GetTxCount () const noexcept { return m_linearization.size (); }
@@ -379,26 +383,54 @@ void Cluster::ApplyRemovals(TxGraphImpl& graph, std::span<GraphIndex>& to_remove
379
383
--graph.m_txcount ;
380
384
} while (!to_remove.empty ());
381
385
386
+ auto quality = m_quality;
382
387
Assume (todo.Any ());
383
388
// Wipe from the Cluster's DepGraph (this is O(n) regardless of the number of entries
384
389
// removed, so we benefit from batching all the removals).
385
390
m_depgraph.RemoveTransactions (todo);
386
391
m_mapping.resize (m_depgraph.PositionRange ());
387
392
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);
395
415
Updated (graph);
396
416
}
397
417
398
418
bool Cluster::Split (TxGraphImpl& graph) noexcept
399
419
{
400
420
// This function can only be called when the Cluster needs splitting.
401
421
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
+ }
402
434
/* * Which positions are still left in this Cluster. */
403
435
auto todo = m_depgraph.Positions ();
404
436
/* * 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
412
444
if (first && component == todo) {
413
445
// The existing Cluster is an entire component. Leave it be, but update its quality.
414
446
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);
416
451
return false ;
417
452
}
418
453
first = false ;
@@ -424,7 +459,7 @@ bool Cluster::Split(TxGraphImpl& graph) noexcept
424
459
for (auto i : component) {
425
460
remap[i] = {new_cluster.get (), DepGraphIndex (-1 )};
426
461
}
427
- graph.InsertCluster (std::move (new_cluster), QualityLevel::NEEDS_RELINEARIZE );
462
+ graph.InsertCluster (std::move (new_cluster), new_quality );
428
463
todo -= component;
429
464
}
430
465
// Redistribute the transactions.
@@ -696,9 +731,11 @@ void TxGraphImpl::SplitAll() noexcept
696
731
{
697
732
// Before splitting all Cluster, first make sure all removals are applied.
698
733
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
+ }
702
739
}
703
740
}
704
741
@@ -1221,6 +1258,8 @@ void Cluster::SetFee(TxGraphImpl& graph, DepGraphIndex idx, int64_t fee) noexcep
1221
1258
m_depgraph.FeeRate (idx).fee = fee;
1222
1259
if (!NeedsSplitting ()) {
1223
1260
graph.SetClusterQuality (m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
1261
+ } else {
1262
+ graph.SetClusterQuality (m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);
1224
1263
}
1225
1264
Updated (graph);
1226
1265
}
0 commit comments