diff --git a/src/test/fuzz/txgraph.cpp b/src/test/fuzz/txgraph.cpp index 00001034718..fbf22bfc4c3 100644 --- a/src/test/fuzz/txgraph.cpp +++ b/src/test/fuzz/txgraph.cpp @@ -1012,6 +1012,21 @@ FUZZ_TARGET(txgraph) } assert(!top_sim.IsOversized()); break; + } else if (command-- == 0) { + // GetMainMemoryUsage(). + auto usage = real->GetMainMemoryUsage(); + // Test stability. + if (alt) { + auto usage2 = real->GetMainMemoryUsage(); + assert(usage == usage2); + } + // Only empty graphs have 0 memory usage. + if (main_sim.GetTransactionCount() == 0) { + assert(usage == 0); + } else { + assert(usage > 0); + } + break; } } } diff --git a/src/txgraph.cpp b/src/txgraph.cpp index a2392bc8cd6..41a59b5b759 100644 --- a/src/txgraph.cpp +++ b/src/txgraph.cpp @@ -629,6 +629,8 @@ public: std::unique_ptr GetBlockBuilder() noexcept final; std::pair, FeePerWeight> GetWorstMainChunk() noexcept final; + size_t GetMainMemoryUsage() noexcept final; + void SanityCheck() const final; }; @@ -2980,6 +2982,21 @@ std::vector TxGraphImpl::Trim() noexcept return ret; } +size_t TxGraphImpl::GetMainMemoryUsage() noexcept +{ + // Make sure splits/merges are applied, as memory usage may not be representative otherwise. + SplitAll(/*up_to_level=*/0); + ApplyDependencies(/*level=*/0); + // Compute memory usage + size_t usage = /* From clusters */ + m_main_clusterset.m_cluster_usage + + /* From Entry objects. */ + sizeof(Entry) * m_main_clusterset.m_txcount + + /* From the chunk index. */ + memusage::DynamicUsage(m_main_chunkindex); + return usage; +} + } // namespace TxGraph::Ref::~Ref() diff --git a/src/txgraph.h b/src/txgraph.h index 20ea97ac5e4..1d8d31a5427 100644 --- a/src/txgraph.h +++ b/src/txgraph.h @@ -202,6 +202,13 @@ public: * graph must not be oversized. If the graph is empty, {{}, FeePerWeight{}} is returned. */ virtual std::pair, FeePerWeight> GetWorstMainChunk() noexcept = 0; + /** Get the approximate memory usage for this object, just counting the main graph. If a + * staging graph is present, return a number corresponding to memory usage after + * AbortStaging() would be called. BlockBuilders' memory usage, memory usage of internally + * queued operations, and memory due to temporary caches, is not included here. Can always be + * called. */ + virtual size_t GetMainMemoryUsage() noexcept = 0; + /** Perform an internal consistency check on this object. */ virtual void SanityCheck() const = 0;