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/check.h>
#include <util/feefrac.h>
#include <util/vector.h>
#include <compare>
#include <memory>
@ -312,6 +313,8 @@ private:
ChunkIndex m_main_chunkindex;
/** Number of index-observing objects in existence (BlockBuilderImpls). */
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.
* 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;
/** Find which Clusters in main conflict with ones in staging. */
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.
@ -584,6 +591,37 @@ public:
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
{
auto& entry = m_entries[idx];
@ -606,11 +644,7 @@ void TxGraphImpl::ClearLocator(int level, GraphIndex idx) noexcept
--m_staging_clusterset->m_txcount;
}
}
if (level == 0 && entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
Assume(m_main_chunkindex_observers == 0);
m_main_chunkindex.erase(entry.m_main_chunkindex_iterator);
entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
}
if (level == 0) ClearChunkData(entry);
}
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.
for (DepGraphIndex idx : m_linearization) {
auto& entry = graph.m_entries[m_mapping[idx]];
if (m_level == 0 && entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end()) {
// Destroy any potential ChunkData prior to modifying the Cluster (as that could
// Discard any potential ChunkData prior to modifying the Cluster (as that could
// invalidate its ordering).
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();
}
if (m_level == 0) graph.ClearChunkData(entry);
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
@ -651,9 +681,7 @@ void Cluster::Updated(TxGraphImpl& graph) noexcept
chunk.transactions.Reset(idx);
if (chunk.transactions.None()) {
// Last transaction in the chunk.
auto [it, inserted] = graph.m_main_chunkindex.emplace(graph_idx, chunk_count);
Assume(inserted);
entry.m_main_chunkindex_iterator = it;
graph.CreateChunkData(graph_idx, chunk_count);
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
// merged Cluster later anyway).
auto& entry = graph.m_entries[idx];
if (m_level == 0 && entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end()) {
// Destroy any potential ChunkData prior to modifying the Cluster (as that could
// Discard any potential ChunkData prior to modifying the Cluster (as that could
// invalidate its ordering).
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();
}
if (m_level == 0) graph.ClearChunkData(entry);
entry.m_locator[m_level].SetPresent(this, new_pos);
}
// 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;
}
// 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
// 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