mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
Merge 77ebad9651
into c5e44a0435
This commit is contained in:
commit
025b0784fe
3 changed files with 41 additions and 22 deletions
|
@ -24,9 +24,6 @@
|
||||||
namespace memusage
|
namespace memusage
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Compute the total memory used by allocating alloc bytes. */
|
|
||||||
static size_t MallocUsage(size_t alloc);
|
|
||||||
|
|
||||||
/** Dynamic memory usage for built-in types is zero. */
|
/** Dynamic memory usage for built-in types is zero. */
|
||||||
static inline size_t DynamicUsage(const int8_t& v) { return 0; }
|
static inline size_t DynamicUsage(const int8_t& v) { return 0; }
|
||||||
static inline size_t DynamicUsage(const uint8_t& v) { return 0; }
|
static inline size_t DynamicUsage(const uint8_t& v) { return 0; }
|
||||||
|
@ -48,19 +45,21 @@ template<typename X> static inline size_t DynamicUsage(const X * const &v) { ret
|
||||||
* application data structures require more accurate inner accounting, they should
|
* application data structures require more accurate inner accounting, they should
|
||||||
* iterate themselves, or use more efficient caching + updating on modification.
|
* iterate themselves, or use more efficient caching + updating on modification.
|
||||||
*/
|
*/
|
||||||
|
static constexpr size_t MallocUsage(size_t alloc)
|
||||||
static inline size_t MallocUsage(size_t alloc)
|
|
||||||
{
|
{
|
||||||
// Measured on libc6 2.19 on Linux.
|
if (alloc == 0) return 0;
|
||||||
if (alloc == 0) {
|
|
||||||
return 0;
|
#if defined(__arm__) || SIZE_MAX == UINT64_MAX
|
||||||
} else if (sizeof(void*) == 8) {
|
constexpr size_t min_alloc{9};
|
||||||
return ((alloc + 31) >> 4) << 4;
|
#else
|
||||||
} else if (sizeof(void*) == 4) {
|
constexpr size_t min_alloc{0};
|
||||||
return ((alloc + 15) >> 3) << 3;
|
#endif
|
||||||
} else {
|
constexpr size_t overhead{sizeof(size_t)};
|
||||||
assert(0);
|
constexpr size_t step{alignof(std::max_align_t)};
|
||||||
}
|
// step should be a nonzero power of 2 (exactly one bit set)
|
||||||
|
static_assert(step > 0 && (step & (step - 1)) == 0);
|
||||||
|
|
||||||
|
return (std::max(min_alloc, alloc) + overhead + (step - 1)) & ~(step - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// STL data structures
|
// STL data structures
|
||||||
|
@ -177,23 +176,33 @@ static inline size_t DynamicUsage(const std::list<X>& l)
|
||||||
return MallocUsage(sizeof(list_node<X>)) * l.size();
|
return MallocUsage(sizeof(list_node<X>)) * l.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Empirically, an std::unordered_map node has two pointers (likely
|
||||||
|
// forward and backward pointers) on some platforms (Windows and macOS),
|
||||||
|
// so be conservative in estimating memory usage by assuming this is
|
||||||
|
// the case for all platforms.
|
||||||
template<typename X>
|
template<typename X>
|
||||||
struct unordered_node : private X
|
struct unordered_node : private X
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
void* ptr;
|
void* ptr;
|
||||||
|
void* ptr2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The memory used by an unordered_set or unordered map is the sum of the
|
||||||
|
// sizes of the individual nodes (which are separately allocated) plus
|
||||||
|
// the size of the bucket array (which is a single allocation).
|
||||||
|
// Empirically, each element of the bucket array consists of two pointers
|
||||||
|
// on some platforms (Windows and macOS), so be conservative.
|
||||||
template<typename X, typename Y>
|
template<typename X, typename Y>
|
||||||
static inline size_t DynamicUsage(const std::unordered_set<X, Y>& s)
|
static inline size_t DynamicUsage(const std::unordered_set<X, Y>& s)
|
||||||
{
|
{
|
||||||
return MallocUsage(sizeof(unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
|
return MallocUsage(sizeof(unordered_node<X>)) * s.size() + MallocUsage(2 * sizeof(void*) * s.bucket_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename X, typename Y, typename Z>
|
template<typename X, typename Y, typename Z>
|
||||||
static inline size_t DynamicUsage(const std::unordered_map<X, Y, Z>& m)
|
static inline size_t DynamicUsage(const std::unordered_map<X, Y, Z>& m)
|
||||||
{
|
{
|
||||||
return MallocUsage(sizeof(unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
|
return MallocUsage(sizeof(unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(2 * sizeof(void*) * m.bucket_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Key, class T, class Hash, class Pred, std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
|
template <class Key, class T, class Hash, class Pred, std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
|
||||||
|
@ -212,7 +221,7 @@ static inline size_t DynamicUsage(const std::unordered_map<Key,
|
||||||
size_t estimated_list_node_size = MallocUsage(sizeof(void*) * 3);
|
size_t estimated_list_node_size = MallocUsage(sizeof(void*) * 3);
|
||||||
size_t usage_resource = estimated_list_node_size * pool_resource->NumAllocatedChunks();
|
size_t usage_resource = estimated_list_node_size * pool_resource->NumAllocatedChunks();
|
||||||
size_t usage_chunks = MallocUsage(pool_resource->ChunkSizeBytes()) * pool_resource->NumAllocatedChunks();
|
size_t usage_chunks = MallocUsage(pool_resource->ChunkSizeBytes()) * pool_resource->NumAllocatedChunks();
|
||||||
return usage_resource + usage_chunks + MallocUsage(sizeof(void*) * m.bucket_count());
|
return usage_resource + usage_chunks + MallocUsage(2 * sizeof(void*) * m.bucket_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace memusage
|
} // namespace memusage
|
||||||
|
|
|
@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
|
||||||
// (prevector<28, unsigned char>) when assigned 56 bytes of data per above.
|
// (prevector<28, unsigned char>) when assigned 56 bytes of data per above.
|
||||||
//
|
//
|
||||||
// See also: Coin::DynamicMemoryUsage().
|
// See also: Coin::DynamicMemoryUsage().
|
||||||
constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64;
|
constexpr size_t COIN_SIZE{64};
|
||||||
|
|
||||||
auto print_view_mem_usage = [](CCoinsViewCache& view) {
|
auto print_view_mem_usage = [](CCoinsViewCache& view) {
|
||||||
BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());
|
BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());
|
||||||
|
|
|
@ -57,16 +57,17 @@ def fill_mempool(test_framework, node, *, tx_sync_fun=None):
|
||||||
# Generate UTXOs to flood the mempool
|
# Generate UTXOs to flood the mempool
|
||||||
# 1 to create a tx initially that will be evicted from the mempool later
|
# 1 to create a tx initially that will be evicted from the mempool later
|
||||||
# 75 transactions each with a fee rate higher than the previous one
|
# 75 transactions each with a fee rate higher than the previous one
|
||||||
|
# 1 to create a final middle-sized tx to evict a filler tx and create room (if needed)
|
||||||
ephemeral_miniwallet = MiniWallet(node, tag_name="fill_mempool_ephemeral_wallet")
|
ephemeral_miniwallet = MiniWallet(node, tag_name="fill_mempool_ephemeral_wallet")
|
||||||
test_framework.generate(ephemeral_miniwallet, 1 + num_of_batches * tx_batch_size)
|
test_framework.generate(ephemeral_miniwallet, 2 + num_of_batches * tx_batch_size)
|
||||||
|
|
||||||
# Mine enough blocks so that the UTXOs are allowed to be spent
|
# Mine enough blocks so that the UTXOs are allowed to be spent
|
||||||
test_framework.generate(node, COINBASE_MATURITY - 1)
|
test_framework.generate(node, COINBASE_MATURITY - 1)
|
||||||
|
|
||||||
# Get all UTXOs up front to ensure none of the transactions spend from each other, as that may
|
# Get all UTXOs up front to ensure none of the transactions spend from each other, as that may
|
||||||
# change their effective feerate and thus the order in which they are selected for eviction.
|
# change their effective feerate and thus the order in which they are selected for eviction.
|
||||||
confirmed_utxos = [ephemeral_miniwallet.get_utxo(confirmed_only=True) for _ in range(num_of_batches * tx_batch_size + 1)]
|
confirmed_utxos = [ephemeral_miniwallet.get_utxo(confirmed_only=True) for _ in range(num_of_batches * tx_batch_size + 2)]
|
||||||
assert_equal(len(confirmed_utxos), num_of_batches * tx_batch_size + 1)
|
assert_equal(len(confirmed_utxos), num_of_batches * tx_batch_size + 2)
|
||||||
|
|
||||||
test_framework.log.debug("Create a mempool tx that will be evicted")
|
test_framework.log.debug("Create a mempool tx that will be evicted")
|
||||||
tx_to_be_evicted_id = ephemeral_miniwallet.send_self_transfer(
|
tx_to_be_evicted_id = ephemeral_miniwallet.send_self_transfer(
|
||||||
|
@ -92,6 +93,14 @@ def fill_mempool(test_framework, node, *, tx_sync_fun=None):
|
||||||
send_batch(fee)
|
send_batch(fee)
|
||||||
tx_sync_fun() if tx_sync_fun else test_framework.sync_mempools() # sync after all evictions
|
tx_sync_fun() if tx_sync_fun else test_framework.sync_mempools() # sync after all evictions
|
||||||
|
|
||||||
|
# If the mempool is almost full (<10k usage bytes left), submit one extra middle-sized tx,
|
||||||
|
# in order to evict a large filler tx and leave some room; this will enable tests to submit
|
||||||
|
# txs with just the mempoolminfee without immediate eviction ("mempool full" error).
|
||||||
|
if node.getmempoolinfo()['usage'] >= 4_990_000:
|
||||||
|
ephemeral_miniwallet.send_self_transfer(from_node=node, fee=num_of_batches * (base_fee / 2),
|
||||||
|
utxo_to_spend=confirmed_utxos.pop(0), target_vsize = 32500)
|
||||||
|
assert_greater_than(4_990_000, node.getmempoolinfo()['usage'])
|
||||||
|
|
||||||
test_framework.log.debug("The tx should be evicted by now")
|
test_framework.log.debug("The tx should be evicted by now")
|
||||||
# The number of transactions created should be greater than the ones present in the mempool
|
# The number of transactions created should be greater than the ones present in the mempool
|
||||||
assert_greater_than(tx_batch_size * num_of_batches, len(node.getrawmempool()))
|
assert_greater_than(tx_batch_size * num_of_batches, len(node.getrawmempool()))
|
||||||
|
@ -101,6 +110,7 @@ def fill_mempool(test_framework, node, *, tx_sync_fun=None):
|
||||||
test_framework.log.debug("Check that mempoolminfee is larger than minrelaytxfee")
|
test_framework.log.debug("Check that mempoolminfee is larger than minrelaytxfee")
|
||||||
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
|
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
|
||||||
assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
|
assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
|
||||||
|
test_framework.sync_mempools()
|
||||||
|
|
||||||
def tx_in_orphanage(node, tx: CTransaction) -> bool:
|
def tx_in_orphanage(node, tx: CTransaction) -> bool:
|
||||||
"""Returns true if the transaction is in the orphanage."""
|
"""Returns true if the transaction is in the orphanage."""
|
||||||
|
|
Loading…
Add table
Reference in a new issue