diff --git a/src/txgraph.cpp b/src/txgraph.cpp index 6d04ba808b5..48a18312838 100644 --- a/src/txgraph.cpp +++ b/src/txgraph.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -322,6 +323,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 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. @@ -441,6 +444,10 @@ public: void ClearLocator(int level, GraphIndex index) noexcept; /** Find which Clusters in main conflict with ones in staging. */ std::vector 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. @@ -594,6 +601,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]; @@ -616,11 +654,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 @@ -628,13 +662,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 - // 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(); - } + // Discard any potential ChunkData prior to modifying the Cluster (as that could + // invalidate its ordering). + 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 @@ -661,9 +691,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; } } @@ -922,13 +950,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 - // 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(); - } + // Discard any potential ChunkData prior to modifying the Cluster (as that could + // invalidate its ordering). + 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. @@ -1171,6 +1195,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