Skip to content

Commit fdcf67d

Browse files
committed
Merge bitcoin/bitcoin#33157: cluster mempool: control/optimize TxGraph memory usage
023cd5a txgraph: add SingletonClusterImpl (mem optimization) (Pieter Wuille) e346250 txgraph: give Clusters a range of intended tx counts (preparation) (Pieter Wuille) e93b0f0 txgraph: abstract out creation of empty Clusters (refactor) (Pieter Wuille) 6baf126 txgraph: comment fixes (doc fix) (Pieter Wuille) 726b995 txgraph: make Cluster an abstract class (refactor) (Pieter Wuille) 2602d89 txgraph: avoid accessing other Cluster internals (refactor) (Pieter Wuille) 04c808a txgraph: expose memory usage estimate function (feature) (Pieter Wuille) 7680bb8 txgraph: keep track of Cluster memory usage (preparation) (Pieter Wuille) 4ba562e txgraph: keep data structures compact (mem optimization) (Pieter Wuille) bb5cb22 depgraph: add memory usage control (feature) (Pieter Wuille) b1637a9 txgraph: avoid holes in DepGraph positions (mem optimization) (Pieter Wuille) 2b1d302 txgraph: move some sanity checks from Cluster to TxGraphImpl (refactor) (Pieter Wuille) d40302f txgraph: Make level of Cluster implicit (optimization) (Pieter Wuille) Pull request description: Part of #30289. This adds a few optimizations to reduce `TxGraph`'s memory usage, and makes sure that dynamic memory it uses doesn't linger after shrinking clusters. Finally, it exposes a function `GetMainMemoryUsage()` to compute `TxGraph`'s approximate memory usage. It makes the `Cluster` type abstract, with two instances (`SingletonClusterImpl` for 1-transaction clusters, and `GenericClusterImpl` for others). On my 64-bit system, I obtain the following numbers: * `SingletonClusterImpl`: 48 bytes, plus 16 bytes malloc overhead in its `unique_ptr`, plus 8-byte pointer in `m_clusters` * `GenericClusterImpl`: 104 bytes, plus 16 bytes malloc overhead in its `unique_ptr`, plus 8-byte pointer in `m_clusters`, plus 72 bytes malloc overhead inside its vectors and `DepGraph`, plus 40 bytes per transaction in those. * `TxGraphImpl::Entry`: 72 bytes per transaction * `TxGraphImpl::ChunkData`: 8 bytes, plus 56 bytes in `std::set` overhead + malloc overhead, all per chunk. * `TxGraph::Ref`: 16 bytes per transaction This overall amounts to 200 bytes per cluster, plus 64 bytes per chunk, plus 128 bytes per transaction, but only 224 bytes overall per singleton cluster. ACKs for top commit: l0rinc: code review reACK 023cd5a instagibbs: reACK 023cd5a ismaelsadeeq: reACK 023cd5a 🚢 glozow: reACK 023cd5a Tree-SHA512: c957b27f47318be7c25d71453df2ae9d4e7bf21dab13b6e5e975cca122a221a99b15c584872491225785d276a9165f090675ee0f4460a2775bd3271933e3b246
2 parents 6c4fe40 + 023cd5a commit fdcf67d

File tree

5 files changed

+847
-261
lines changed

5 files changed

+847
-261
lines changed

src/cluster_linearize.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <utility>
1313
#include <vector>
1414

15+
#include <memusage.h>
1516
#include <random.h>
1617
#include <span.h>
1718
#include <util/feefrac.h>
@@ -332,6 +333,17 @@ class DepGraph
332333
}
333334
return true;
334335
}
336+
337+
/** Reduce memory usage if possible. No observable effect. */
338+
void Compact() noexcept
339+
{
340+
entries.shrink_to_fit();
341+
}
342+
343+
size_t DynamicMemoryUsage() const noexcept
344+
{
345+
return memusage::DynamicUsage(entries);
346+
}
335347
};
336348

337349
/** A set of transactions together with their aggregate feerate. */

src/test/fuzz/cluster_linearize.cpp

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -452,63 +452,72 @@ FUZZ_TARGET(clusterlin_depgraph_sim)
452452
}
453453
};
454454

455+
auto last_compaction_pos{real.PositionRange()};
456+
455457
LIMITED_WHILE(provider.remaining_bytes() > 0, 1000) {
456-
uint8_t command = provider.ConsumeIntegral<uint8_t>();
457-
if (num_tx_sim == 0 || ((command % 3) <= 0 && num_tx_sim < TestBitSet::Size())) {
458-
// AddTransaction.
459-
auto fee = provider.ConsumeIntegralInRange<int64_t>(-0x8000000000000, 0x7ffffffffffff);
460-
auto size = provider.ConsumeIntegralInRange<int32_t>(1, 0x3fffff);
461-
FeeFrac feerate{fee, size};
462-
// Apply to DepGraph.
463-
auto idx = real.AddTransaction(feerate);
464-
// Verify that the returned index is correct.
465-
assert(!sim[idx].has_value());
466-
for (DepGraphIndex i = 0; i < TestBitSet::Size(); ++i) {
467-
if (!sim[i].has_value()) {
468-
assert(idx == i);
469-
break;
458+
int command = provider.ConsumeIntegral<uint8_t>() % 4;
459+
while (true) {
460+
// Iterate decreasing command until an applicable branch is found.
461+
if (num_tx_sim < TestBitSet::Size() && command-- == 0) {
462+
// AddTransaction.
463+
auto fee = provider.ConsumeIntegralInRange<int64_t>(-0x8000000000000, 0x7ffffffffffff);
464+
auto size = provider.ConsumeIntegralInRange<int32_t>(1, 0x3fffff);
465+
FeeFrac feerate{fee, size};
466+
// Apply to DepGraph.
467+
auto idx = real.AddTransaction(feerate);
468+
// Verify that the returned index is correct.
469+
assert(!sim[idx].has_value());
470+
for (DepGraphIndex i = 0; i < TestBitSet::Size(); ++i) {
471+
if (!sim[i].has_value()) {
472+
assert(idx == i);
473+
break;
474+
}
470475
}
471-
}
472-
// Update sim.
473-
sim[idx] = {feerate, TestBitSet::Singleton(idx)};
474-
++num_tx_sim;
475-
continue;
476-
}
477-
if ((command % 3) <= 1 && num_tx_sim > 0) {
478-
// AddDependencies.
479-
DepGraphIndex child = idx_fn();
480-
auto parents = subset_fn();
481-
// Apply to DepGraph.
482-
real.AddDependencies(parents, child);
483-
// Apply to sim.
484-
sim[child]->second |= parents;
485-
continue;
486-
}
487-
if (num_tx_sim > 0) {
488-
// Remove transactions.
489-
auto del = set_fn();
490-
// Propagate all ancestry information before deleting anything in the simulation (as
491-
// intermediary transactions may be deleted which impact connectivity).
492-
anc_update_fn();
493-
// Compare the state of the transactions being deleted.
494-
for (auto i : del) check_fn(i);
495-
// Apply to DepGraph.
496-
real.RemoveTransactions(del);
497-
// Apply to sim.
498-
for (DepGraphIndex i = 0; i < sim.size(); ++i) {
499-
if (sim[i].has_value()) {
500-
if (del[i]) {
501-
--num_tx_sim;
502-
sim[i] = std::nullopt;
503-
} else {
504-
sim[i]->second -= del;
476+
// Update sim.
477+
sim[idx] = {feerate, TestBitSet::Singleton(idx)};
478+
++num_tx_sim;
479+
break;
480+
} else if (num_tx_sim > 0 && command-- == 0) {
481+
// AddDependencies.
482+
DepGraphIndex child = idx_fn();
483+
auto parents = subset_fn();
484+
// Apply to DepGraph.
485+
real.AddDependencies(parents, child);
486+
// Apply to sim.
487+
sim[child]->second |= parents;
488+
break;
489+
} else if (num_tx_sim > 0 && command-- == 0) {
490+
// Remove transactions.
491+
auto del = set_fn();
492+
// Propagate all ancestry information before deleting anything in the simulation (as
493+
// intermediary transactions may be deleted which impact connectivity).
494+
anc_update_fn();
495+
// Compare the state of the transactions being deleted.
496+
for (auto i : del) check_fn(i);
497+
// Apply to DepGraph.
498+
real.RemoveTransactions(del);
499+
// Apply to sim.
500+
for (DepGraphIndex i = 0; i < sim.size(); ++i) {
501+
if (sim[i].has_value()) {
502+
if (del[i]) {
503+
--num_tx_sim;
504+
sim[i] = std::nullopt;
505+
} else {
506+
sim[i]->second -= del;
507+
}
505508
}
506509
}
510+
break;
511+
} else if (command-- == 0) {
512+
// Compact.
513+
const size_t mem_before{real.DynamicMemoryUsage()};
514+
real.Compact();
515+
const size_t mem_after{real.DynamicMemoryUsage()};
516+
assert(real.PositionRange() < last_compaction_pos ? mem_after < mem_before : mem_after <= mem_before);
517+
last_compaction_pos = real.PositionRange();
518+
break;
507519
}
508-
continue;
509520
}
510-
// This should be unreachable (one of the 3 above actions should always be possible).
511-
assert(false);
512521
}
513522

514523
// Compare the real obtained depgraph against the simulation.

src/test/fuzz/txgraph.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,21 @@ FUZZ_TARGET(txgraph)
10121012
}
10131013
assert(!top_sim.IsOversized());
10141014
break;
1015+
} else if (command-- == 0) {
1016+
// GetMainMemoryUsage().
1017+
auto usage = real->GetMainMemoryUsage();
1018+
// Test stability.
1019+
if (alt) {
1020+
auto usage2 = real->GetMainMemoryUsage();
1021+
assert(usage == usage2);
1022+
}
1023+
// Only empty graphs have 0 memory usage.
1024+
if (main_sim.GetTransactionCount() == 0) {
1025+
assert(usage == 0);
1026+
} else {
1027+
assert(usage > 0);
1028+
}
1029+
break;
10151030
}
10161031
}
10171032
}

0 commit comments

Comments
 (0)