Merge bitcoin/bitcoin#31164: net: Use actual memory size in receive buffer accounting
Some checks are pending
CI / test each commit (push) Waiting to run
CI / macOS 14 native, arm64, no depends, sqlite only, gui (push) Waiting to run
CI / Win64 native, VS 2022 (push) Waiting to run
CI / ASan + LSan + UBSan + integer, no depends, USDT (push) Waiting to run

d22a234ed2 net: Use actual memory size in receive buffer accounting (laanwj)
047b5e2af1 streams: add DataStream::GetMemoryUsage (laanwj)
c3a6722f34 net: Use DynamicUsage(m_type) in CSerializedNetMsg::GetMemoryUsage (laanwj)
c6594c0b14 memusage: Add DynamicUsage for std::string (laanwj)
7596282a55 memusage: Allow counting usage of vectors with different allocators (laanwj)

Pull request description:

  Add a method `CNetMessage::GetMemoryUsage` and use this for accounting of the size of the process receive queue instead of the raw message size (like we already do for the send buffer and `CSerializedNetMsg`).

  This ensures that allocation and deserialization overhead is better taken into account.

  On average, this counts about ~100 extra bytes per packet on x86_64:
  ```
  2024-10-27T09:50:12Z [net] 24 bytes -> 112 bytes
  2024-10-27T10:36:37Z [net] 61 bytes -> 176 bytes
  2024-10-27T10:36:38Z [net] 1285 bytes -> 1392 bytes
  2024-10-27T09:50:21Z [net] 43057 bytes -> 43168 bytes
  ```

ACKs for top commit:
  l0rinc:
    ACK d22a234ed2
  achow101:
    ACK d22a234ed2
  i-am-yuvi:
    ACK d22a234ed2
  danielabrozzoni:
    Light ACK d22a234ed2 - code looks good to me, but I'm not very familiar with C++ memory management specifics

Tree-SHA512: ef09707e77b67bdbc48e9464133e4fccfa5c05051c1022e81ad84f20ed41db83ac5a9b109ebdb8d38f70785c03c5d6bfe51d32dc133d49e52d1e6225f6f8e292
This commit is contained in:
Ava Chow 2024-11-06 16:01:07 -05:00
commit 564238aabf
No known key found for this signature in database
GPG key ID: 17565732E08E5E41
5 changed files with 36 additions and 9 deletions

View file

@ -15,6 +15,7 @@
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
@ -84,10 +85,22 @@ struct stl_shared_counter
size_t weak_count;
};
template<typename X>
static inline size_t DynamicUsage(const std::vector<X>& v)
template<typename T, typename Allocator>
static inline size_t DynamicUsage(const std::vector<T, Allocator>& v)
{
return MallocUsage(v.capacity() * sizeof(X));
return MallocUsage(v.capacity() * sizeof(T));
}
static inline size_t DynamicUsage(const std::string& s)
{
const char* s_ptr = reinterpret_cast<const char*>(&s);
// Don't count the dynamic memory used for string, if it resides in the
// "small string" optimization area (which stores data inside the object itself, up to some
// size; 15 bytes in modern libstdc++).
if (!std::less{}(s.data(), s_ptr) && !std::greater{}(s.data() + s.size(), s_ptr + sizeof(s))) {
return 0;
}
return MallocUsage(s.capacity());
}
template<unsigned int N, typename X, typename S, typename D>

View file

@ -121,10 +121,12 @@ std::string strSubVersion;
size_t CSerializedNetMsg::GetMemoryUsage() const noexcept
{
// Don't count the dynamic memory used for the m_type string, by assuming it fits in the
// "small string" optimization area (which stores data inside the object itself, up to some
// size; 15 bytes in modern libstdc++).
return sizeof(*this) + memusage::DynamicUsage(data);
return sizeof(*this) + memusage::DynamicUsage(m_type) + memusage::DynamicUsage(data);
}
size_t CNetMessage::GetMemoryUsage() const noexcept
{
return sizeof(*this) + memusage::DynamicUsage(m_type) + m_recv.GetMemoryUsage();
}
void CConnman::AddAddrFetch(const std::string& strDest)
@ -3772,7 +3774,7 @@ void CNode::MarkReceivedMsgsForProcessing()
for (const auto& msg : vRecvMsg) {
// vRecvMsg contains only completed CNetMessage
// the single possible partially deserialized message are held by TransportDeserializer
nSizeAdded += msg.m_raw_message_size;
nSizeAdded += msg.GetMemoryUsage();
}
LOCK(m_msg_process_queue_mutex);
@ -3789,7 +3791,7 @@ std::optional<std::pair<CNetMessage, bool>> CNode::PollMessage()
std::list<CNetMessage> msgs;
// Just take one message
msgs.splice(msgs.begin(), m_msg_process_queue, m_msg_process_queue.begin());
m_msg_process_queue_size -= msgs.front().m_raw_message_size;
m_msg_process_queue_size -= msgs.front().GetMemoryUsage();
fPauseRecv = m_msg_process_queue_size > m_recv_flood_size;
return std::make_pair(std::move(msgs.front()), !m_msg_process_queue.empty());

View file

@ -245,6 +245,9 @@ public:
CNetMessage(const CNetMessage&) = delete;
CNetMessage& operator=(CNetMessage&&) = default;
CNetMessage& operator=(const CNetMessage&) = delete;
/** Compute total memory usage of this object (own memory + any dynamic memory). */
size_t GetMemoryUsage() const noexcept;
};
/** The Transport converts one connection's sent messages to wire bytes, and received bytes back. */

View file

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/license/mit/.
#include <memusage.h>
#include <span.h>
#include <streams.h>
#include <util/fs_helpers.h>
@ -110,3 +111,8 @@ bool AutoFile::Truncate(unsigned size)
{
return ::TruncateFile(m_file, size);
}
size_t DataStream::GetMemoryUsage() const noexcept
{
return sizeof(*this) + memusage::DynamicUsage(vch);
}

View file

@ -277,6 +277,9 @@ public:
{
util::Xor(MakeWritableByteSpan(*this), MakeByteSpan(key));
}
/** Compute total memory usage of this object (own memory + any dynamic memory). */
size_t GetMemoryUsage() const noexcept;
};
template <typename IStream>