txgraph: Reuse discarded chunkindex entries (optimization)

This commit is contained in:
Pieter Wuille 2024-12-02 13:33:41 -05:00
parent 82054fa25f
commit e3d3ad9723

View file

@ -9,6 +9,7 @@
#include <util/bitset.h> #include <util/bitset.h>
#include <util/check.h> #include <util/check.h>
#include <util/feefrac.h> #include <util/feefrac.h>
#include <util/vector.h>
#include <compare> #include <compare>
#include <memory> #include <memory>
@ -312,6 +313,8 @@ private:
ChunkIndex m_main_chunkindex; ChunkIndex m_main_chunkindex;
/** Number of index-observing objects in existence (BlockBuilderImpls). */ /** Number of index-observing objects in existence (BlockBuilderImpls). */
size_t m_main_chunkindex_observers{0}; size_t m_main_chunkindex_observers{0};
/** Cache of discarded ChunkIndex node handles to re-use, avoiding additional allocation. */
std::vector<ChunkIndex::node_type> m_main_chunkindex_discarded;
/** A Locator that describes whether, where, and in which Cluster an Entry appears. /** A Locator that describes whether, where, and in which Cluster an Entry appears.
* Every Entry has MAX_LEVELS locators, as it may appear in one Cluster per level. * Every Entry has MAX_LEVELS locators, as it may appear in one Cluster per level.
@ -431,6 +434,10 @@ public:
void ClearLocator(int level, GraphIndex index) noexcept; void ClearLocator(int level, GraphIndex index) noexcept;
/** Find which Clusters in main conflict with ones in staging. */ /** Find which Clusters in main conflict with ones in staging. */
std::vector<Cluster*> GetConflicts() const noexcept; std::vector<Cluster*> GetConflicts() const noexcept;
/** Clear an Entry's ChunkData. */
void ClearChunkData(Entry& entry) noexcept;
/** Give an Entry a ChunkData object. */
void CreateChunkData(GraphIndex idx, LinearizationIndex chunk_count) noexcept;
// Functions for handling Refs. // Functions for handling Refs.
@ -584,6 +591,37 @@ public:
void Skip() noexcept final; void Skip() noexcept final;
}; };
void TxGraphImpl::ClearChunkData(Entry& entry) noexcept
{
if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
Assume(m_main_chunkindex_observers == 0);
// If the Entry has a non-empty m_main_chunkindex_iterator, extract it, and move the handle
// to the cache of discarded chunkindex entries.
m_main_chunkindex_discarded.emplace_back(m_main_chunkindex.extract(entry.m_main_chunkindex_iterator));
entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
}
}
void TxGraphImpl::CreateChunkData(GraphIndex idx, LinearizationIndex chunk_count) noexcept
{
auto& entry = m_entries[idx];
if (!m_main_chunkindex_discarded.empty()) {
// Reuse an discarded node handle.
auto& node = m_main_chunkindex_discarded.back().value();
node.m_graph_index = idx;
node.m_chunk_count = chunk_count;
auto insert_result = m_main_chunkindex.insert(std::move(m_main_chunkindex_discarded.back()));
Assume(insert_result.inserted);
entry.m_main_chunkindex_iterator = insert_result.position;
m_main_chunkindex_discarded.pop_back();
} else {
// Construct a new entry.
auto emplace_result = m_main_chunkindex.emplace(idx, chunk_count);
Assume(emplace_result.second);
entry.m_main_chunkindex_iterator = emplace_result.first;
}
}
void TxGraphImpl::ClearLocator(int level, GraphIndex idx) noexcept void TxGraphImpl::ClearLocator(int level, GraphIndex idx) noexcept
{ {
auto& entry = m_entries[idx]; auto& entry = m_entries[idx];
@ -606,11 +644,7 @@ void TxGraphImpl::ClearLocator(int level, GraphIndex idx) noexcept
--m_staging_clusterset->m_txcount; --m_staging_clusterset->m_txcount;
} }
} }
if (level == 0 && entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) { if (level == 0) ClearChunkData(entry);
Assume(m_main_chunkindex_observers == 0);
m_main_chunkindex.erase(entry.m_main_chunkindex_iterator);
entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
}
} }
void Cluster::Updated(TxGraphImpl& graph) noexcept void Cluster::Updated(TxGraphImpl& graph) noexcept
@ -618,13 +652,9 @@ void Cluster::Updated(TxGraphImpl& graph) noexcept
// Update all the Locators for this Cluster's Entry objects. // Update all the Locators for this Cluster's Entry objects.
for (DepGraphIndex idx : m_linearization) { for (DepGraphIndex idx : m_linearization) {
auto& entry = graph.m_entries[m_mapping[idx]]; auto& entry = graph.m_entries[m_mapping[idx]];
if (m_level == 0 && entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end()) { // Discard any potential ChunkData prior to modifying the Cluster (as that could
// Destroy any potential ChunkData prior to modifying the Cluster (as that could // invalidate its ordering).
// invalidate its ordering). if (m_level == 0) graph.ClearChunkData(entry);
Assume(graph.m_main_chunkindex_observers == 0);
graph.m_main_chunkindex.erase(entry.m_main_chunkindex_iterator);
entry.m_main_chunkindex_iterator = graph.m_main_chunkindex.end();
}
entry.m_locator[m_level].SetPresent(this, idx); entry.m_locator[m_level].SetPresent(this, idx);
} }
// If this is for the main graph (level = 0), and the Cluster's quality is ACCEPTABLE or // If this is for the main graph (level = 0), and the Cluster's quality is ACCEPTABLE or
@ -651,9 +681,7 @@ void Cluster::Updated(TxGraphImpl& graph) noexcept
chunk.transactions.Reset(idx); chunk.transactions.Reset(idx);
if (chunk.transactions.None()) { if (chunk.transactions.None()) {
// Last transaction in the chunk. // Last transaction in the chunk.
auto [it, inserted] = graph.m_main_chunkindex.emplace(graph_idx, chunk_count); graph.CreateChunkData(graph_idx, chunk_count);
Assume(inserted);
entry.m_main_chunkindex_iterator = it;
break; break;
} }
} }
@ -912,13 +940,9 @@ void Cluster::Merge(TxGraphImpl& graph, Cluster& other) noexcept
// feerates, as Updated() will be invoked by Cluster::ApplyDependencies on the resulting // feerates, as Updated() will be invoked by Cluster::ApplyDependencies on the resulting
// merged Cluster later anyway). // merged Cluster later anyway).
auto& entry = graph.m_entries[idx]; auto& entry = graph.m_entries[idx];
if (m_level == 0 && entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end()) { // Discard any potential ChunkData prior to modifying the Cluster (as that could
// Destroy any potential ChunkData prior to modifying the Cluster (as that could // invalidate its ordering).
// invalidate its ordering). if (m_level == 0) graph.ClearChunkData(entry);
Assume(graph.m_main_chunkindex_observers == 0);
graph.m_main_chunkindex.erase(entry.m_main_chunkindex_iterator);
entry.m_main_chunkindex_iterator = graph.m_main_chunkindex.end();
}
entry.m_locator[m_level].SetPresent(this, new_pos); entry.m_locator[m_level].SetPresent(this, new_pos);
} }
// Purge the other Cluster, now that everything has been moved. // Purge the other Cluster, now that everything has been moved.
@ -1161,6 +1185,9 @@ void TxGraphImpl::Compact() noexcept
if (!m_staging_clusterset->m_removed.empty()) return; if (!m_staging_clusterset->m_removed.empty()) return;
} }
// Release memory used by discarded ChunkData index entries.
ClearShrink(m_main_chunkindex_discarded);
// Sort the GraphIndexes that need to be cleaned up. They are sorted in reverse, so the last // Sort the GraphIndexes that need to be cleaned up. They are sorted in reverse, so the last
// ones get processed first. This means earlier-processed GraphIndexes will not cause moving of // ones get processed first. This means earlier-processed GraphIndexes will not cause moving of
// later-processed ones during the "swap with end of m_entries" step below (which might // later-processed ones during the "swap with end of m_entries" step below (which might