mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 23:09:44 -04:00
txgraph: Special-case removal of tail of cluster (Optimization)
When transactions are removed from the tail of a cluster, we know the existing linearization remains acceptable (if it already was), but may just need splitting and postlinearization, so special case these into separate quality levels.
This commit is contained in:
parent
5801e0fb2b
commit
36dd5edca5
1 changed files with 54 additions and 15 deletions
|
@ -33,6 +33,8 @@ enum class QualityLevel
|
||||||
{
|
{
|
||||||
/** This cluster may have multiple disconnected components, which are all NEEDS_RELINEARIZE. */
|
/** This cluster may have multiple disconnected components, which are all NEEDS_RELINEARIZE. */
|
||||||
NEEDS_SPLIT,
|
NEEDS_SPLIT,
|
||||||
|
/** This cluster may have multiple disconnected components, which are all ACCEPTABLE. */
|
||||||
|
NEEDS_SPLIT_ACCEPTABLE,
|
||||||
/** This cluster has undergone changes that warrant re-linearization. */
|
/** This cluster has undergone changes that warrant re-linearization. */
|
||||||
NEEDS_RELINEARIZE,
|
NEEDS_RELINEARIZE,
|
||||||
/** The minimal level of linearization has been performed, but it is not known to be optimal. */
|
/** The minimal level of linearization has been performed, but it is not known to be optimal. */
|
||||||
|
@ -79,9 +81,10 @@ public:
|
||||||
// Generic helper functions.
|
// Generic helper functions.
|
||||||
|
|
||||||
/** Whether the linearization of this Cluster can be exposed. */
|
/** Whether the linearization of this Cluster can be exposed. */
|
||||||
bool IsAcceptable() const noexcept
|
bool IsAcceptable(bool after_split = false) const noexcept
|
||||||
{
|
{
|
||||||
return m_quality == QualityLevel::ACCEPTABLE || m_quality == QualityLevel::OPTIMAL;
|
return m_quality == QualityLevel::ACCEPTABLE || m_quality == QualityLevel::OPTIMAL ||
|
||||||
|
(after_split && m_quality == QualityLevel::NEEDS_SPLIT_ACCEPTABLE);
|
||||||
}
|
}
|
||||||
/** Whether the linearization of this Cluster is optimal. */
|
/** Whether the linearization of this Cluster is optimal. */
|
||||||
bool IsOptimal() const noexcept
|
bool IsOptimal() const noexcept
|
||||||
|
@ -91,7 +94,8 @@ public:
|
||||||
/** Whether this cluster requires splitting. */
|
/** Whether this cluster requires splitting. */
|
||||||
bool NeedsSplitting() const noexcept
|
bool NeedsSplitting() const noexcept
|
||||||
{
|
{
|
||||||
return m_quality == QualityLevel::NEEDS_SPLIT;
|
return m_quality == QualityLevel::NEEDS_SPLIT ||
|
||||||
|
m_quality == QualityLevel::NEEDS_SPLIT_ACCEPTABLE;
|
||||||
}
|
}
|
||||||
/** Get the number of transactions in this Cluster. */
|
/** Get the number of transactions in this Cluster. */
|
||||||
LinearizationIndex GetTxCount() const noexcept { return m_linearization.size(); }
|
LinearizationIndex GetTxCount() const noexcept { return m_linearization.size(); }
|
||||||
|
@ -379,19 +383,35 @@ void Cluster::ApplyRemovals(TxGraphImpl& graph, std::span<GraphIndex>& to_remove
|
||||||
--graph.m_txcount;
|
--graph.m_txcount;
|
||||||
} while(!to_remove.empty());
|
} while(!to_remove.empty());
|
||||||
|
|
||||||
|
auto quality = m_quality;
|
||||||
Assume(todo.Any());
|
Assume(todo.Any());
|
||||||
// Wipe from the Cluster's DepGraph (this is O(n) regardless of the number of entries
|
// Wipe from the Cluster's DepGraph (this is O(n) regardless of the number of entries
|
||||||
// removed, so we benefit from batching all the removals).
|
// removed, so we benefit from batching all the removals).
|
||||||
m_depgraph.RemoveTransactions(todo);
|
m_depgraph.RemoveTransactions(todo);
|
||||||
m_mapping.resize(m_depgraph.PositionRange());
|
m_mapping.resize(m_depgraph.PositionRange());
|
||||||
|
|
||||||
// Filter removals out of m_linearization.
|
// First remove all removals at the end of the linearization.
|
||||||
|
while (!m_linearization.empty() && todo[m_linearization.back()]) {
|
||||||
|
todo.Reset(m_linearization.back());
|
||||||
|
m_linearization.pop_back();
|
||||||
|
}
|
||||||
|
if (todo.None()) {
|
||||||
|
// If no further removals remain, and thus all removals were at the end, we may be able
|
||||||
|
// to leave the cluster at a better quality level.
|
||||||
|
if (IsAcceptable(/*after_split=*/true)) {
|
||||||
|
quality = QualityLevel::NEEDS_SPLIT_ACCEPTABLE;
|
||||||
|
} else {
|
||||||
|
quality = QualityLevel::NEEDS_SPLIT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If more removals remain, filter those out of m_linearization.
|
||||||
m_linearization.erase(std::remove_if(
|
m_linearization.erase(std::remove_if(
|
||||||
m_linearization.begin(),
|
m_linearization.begin(),
|
||||||
m_linearization.end(),
|
m_linearization.end(),
|
||||||
[&](auto pos) { return todo[pos]; }), m_linearization.end());
|
[&](auto pos) { return todo[pos]; }), m_linearization.end());
|
||||||
|
quality = QualityLevel::NEEDS_SPLIT;
|
||||||
graph.SetClusterQuality(m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);
|
}
|
||||||
|
graph.SetClusterQuality(m_quality, m_setindex, quality);
|
||||||
Updated(graph);
|
Updated(graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,6 +419,18 @@ bool Cluster::Split(TxGraphImpl& graph) noexcept
|
||||||
{
|
{
|
||||||
// This function can only be called when the Cluster needs splitting.
|
// This function can only be called when the Cluster needs splitting.
|
||||||
Assume(NeedsSplitting());
|
Assume(NeedsSplitting());
|
||||||
|
// Determine the new quality the split-off Clusters will have.
|
||||||
|
QualityLevel new_quality = IsAcceptable(/*after_split=*/true) ? QualityLevel::ACCEPTABLE
|
||||||
|
: QualityLevel::NEEDS_RELINEARIZE;
|
||||||
|
// If we're going to produce ACCEPTABLE clusters (i.e., when in NEEDS_SPLIT_ACCEPTABLE), we
|
||||||
|
// need to post-linearize to make sure the split-out versions are all connected (as
|
||||||
|
// connectivity may have changed by removing part of the cluster). This could be done on each
|
||||||
|
// resulting split-out cluster separately, but it is simpler to do it once up front before
|
||||||
|
// splitting. This step is not necessary if the resulting clusters are NEEDS_RELINEARIZE, as
|
||||||
|
// they will be post-linearized anyway in MakeAcceptable().
|
||||||
|
if (new_quality == QualityLevel::ACCEPTABLE) {
|
||||||
|
PostLinearize(m_depgraph, m_linearization);
|
||||||
|
}
|
||||||
/** Which positions are still left in this Cluster. */
|
/** Which positions are still left in this Cluster. */
|
||||||
auto todo = m_depgraph.Positions();
|
auto todo = m_depgraph.Positions();
|
||||||
/** Mapping from transaction positions in this Cluster to the Cluster where it ends up, and
|
/** 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
|
||||||
if (first && component == todo) {
|
if (first && component == todo) {
|
||||||
// The existing Cluster is an entire component. Leave it be, but update its quality.
|
// The existing Cluster is an entire component. Leave it be, but update its quality.
|
||||||
Assume(todo == m_depgraph.Positions());
|
Assume(todo == m_depgraph.Positions());
|
||||||
graph.SetClusterQuality(m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
|
graph.SetClusterQuality(m_quality, m_setindex, new_quality);
|
||||||
|
// If this made the quality ACCEPTABLE or OPTIMAL, we need to compute and cache its
|
||||||
|
// chunking.
|
||||||
|
Updated(graph);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
|
@ -424,7 +459,7 @@ bool Cluster::Split(TxGraphImpl& graph) noexcept
|
||||||
for (auto i : component) {
|
for (auto i : component) {
|
||||||
remap[i] = {new_cluster.get(), DepGraphIndex(-1)};
|
remap[i] = {new_cluster.get(), DepGraphIndex(-1)};
|
||||||
}
|
}
|
||||||
graph.InsertCluster(std::move(new_cluster), QualityLevel::NEEDS_RELINEARIZE);
|
graph.InsertCluster(std::move(new_cluster), new_quality);
|
||||||
todo -= component;
|
todo -= component;
|
||||||
}
|
}
|
||||||
// Redistribute the transactions.
|
// Redistribute the transactions.
|
||||||
|
@ -696,10 +731,12 @@ void TxGraphImpl::SplitAll() noexcept
|
||||||
{
|
{
|
||||||
// Before splitting all Cluster, first make sure all removals are applied.
|
// Before splitting all Cluster, first make sure all removals are applied.
|
||||||
ApplyRemovals();
|
ApplyRemovals();
|
||||||
auto& queue = m_clusters[int(QualityLevel::NEEDS_SPLIT)];
|
for (auto quality : {QualityLevel::NEEDS_SPLIT, QualityLevel::NEEDS_SPLIT_ACCEPTABLE}) {
|
||||||
|
auto& queue = m_clusters[int(quality)];
|
||||||
while (!queue.empty()) {
|
while (!queue.empty()) {
|
||||||
Split(*queue.back().get());
|
Split(*queue.back().get());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TxGraphImpl::GroupClusters() noexcept
|
void TxGraphImpl::GroupClusters() noexcept
|
||||||
|
@ -1221,6 +1258,8 @@ void Cluster::SetFee(TxGraphImpl& graph, DepGraphIndex idx, int64_t fee) noexcep
|
||||||
m_depgraph.FeeRate(idx).fee = fee;
|
m_depgraph.FeeRate(idx).fee = fee;
|
||||||
if (!NeedsSplitting()) {
|
if (!NeedsSplitting()) {
|
||||||
graph.SetClusterQuality(m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
|
graph.SetClusterQuality(m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
|
||||||
|
} else {
|
||||||
|
graph.SetClusterQuality(m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);
|
||||||
}
|
}
|
||||||
Updated(graph);
|
Updated(graph);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue