log: avoid collecting GetSerializeSize data when compact block logging is disabled #33738

pull l0rinc wants to merge 1 commits into bitcoin:master from l0rinc:l0rinc/debug-log-serialization changing 2 files +15 −9
  1. l0rinc commented at 10:57 pm on October 29, 2025: contributor

    Context

    The new accounting options introduced in #32582 can be quite heavy, and not needed when debug logging is disabled.

    Problem

    PartiallyDownloadedBlock::FillBlock() and PeerManagerImpl::SendBlockTransactions() accumulate transaction sizes for debug logging by calling GetTotalSize() in loops, which invokes expensive GetSerializeSize() serializations.

    Fixes

    Guard the size calculations with LogAcceptCategory() checks so serialization only occurs when compact block debug logging is enabled.

    Also modernized the surrounding code a bit since the change is quite trivial.

    Reproducer

    You can test the change by starting your up-to-date bitcoind node with -debug=cmpctblock and observing the compact block lines such as:

    [cmpctblock] Successfully reconstructed block 00000000000000000000b432f545625df1698f0c6e8ee9e2a04058f65baab111 with 1 txn prefilled, 1891 txn from mempool (incl at least 0 from extra pool) and 1767 txn (935772 bytes) requested

    And test the negative by adding throw ""; instead of the GetTotalSize call and starting bitcoind without the -debug=cmpctblock argument to prove that it’s not called.

     0diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
     1index 5f0217c2ba..a8a61ecf73 100644
     2--- a/src/blockencodings.cpp
     3+++ b/src/blockencodings.cpp
     4@@ -186,6 +186,7 @@ bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const
     5 
     6 ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing, bool segwit_active)
     7 {
     8+    LogInfo("PartiallyDownloadedBlock::FillBlock called");
     9     if (header.IsNull()) return READ_STATUS_INVALID;
    10 
    11     uint256 hash = header.GetHash();
    12@@ -201,7 +202,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
    13             }
    14             block.vtx[i] = vtx_missing[tx_missing_offset++];
    15             if (LogAcceptCategory(BCLog::CMPCTBLOCK, BCLog::Level::Debug)) {
    16-                tx_missing_size += block.vtx[i]->GetTotalSize();
    17+                throw "";
    18             }
    19         } else {
    20             block.vtx[i] = std::move(txn_available[i]);
    21diff --git a/src/net_processing.cpp b/src/net_processing.cpp
    22index 26a5aacaee..d879c557f1 100644
    23--- a/src/net_processing.cpp
    24+++ b/src/net_processing.cpp
    25@@ -2470,6 +2470,7 @@ uint32_t PeerManagerImpl::GetFetchFlags(const Peer& peer) const
    26 
    27 void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlock& block, const BlockTransactionsRequest& req)
    28 {
    29+    LogInfo("PeerManagerImpl::SendBlockTransactions called");
    30     BlockTransactions resp(req);
    31     [[maybe_unused]] uint32_t tx_requested_size{0};
    32     for (size_t i = 0; i < req.indexes.size(); i++) {
    33@@ -2479,7 +2480,7 @@ void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlo
    34         }
    35         resp.txn[i] = block.vtx[req.indexes[i]];
    36         if (LogAcceptCategory(BCLog::CMPCTBLOCK, BCLog::Level::Debug)) {
    37-            tx_requested_size += resp.txn[i]->GetTotalSize();
    38+            throw "";
    39         }
    40     }
    
  2. log: avoid collecting `GetSerializeSize` data when logging is disabled
    `PartiallyDownloadedBlock::FillBlock()` and `PeerManagerImpl::SendBlockTransactions()` accumulate transaction sizes for debug logging by calling `GetTotalSize()` in loops, which invokes expensive `GetSerializeSize()` serializations.
    
    Guard the size calculations with `LogAcceptCategory()` checks so serialization only occurs when compact block debug logging is enabled.
    17856343f8
  3. DrahtBot added the label Utils/log/libs on Oct 29, 2025
  4. DrahtBot commented at 10:57 pm on October 29, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/33738.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK davidgumberg
    Concept ACK TheCharlatan

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

  5. davidgumberg commented at 1:24 am on October 30, 2025: contributor
    Thanks for catching this, the PR seems correct to me, why is it in draft?
  6. TheCharlatan commented at 6:43 am on October 30, 2025: contributor
    Concept ACK
  7. in src/net_processing.cpp:2486 in 17856343f8
    2483+        if (LogAcceptCategory(BCLog::CMPCTBLOCK, BCLog::Level::Debug)) {
    2484+            tx_requested_size += resp.txn[i]->GetTotalSize();
    2485+        }
    2486     }
    2487 
    2488     LogDebug(BCLog::CMPCTBLOCK, "Peer %d sent us a GETBLOCKTXN for block %s, sending a BLOCKTXN with %u txns. (%u bytes)\n", pfrom.GetId(), block.GetHash().ToString(), resp.txn.size(), tx_requested_size);
    


    maflcko commented at 6:43 am on October 30, 2025:

    in theory this seems racy, when the logging is toggled in another thread, but this is probably good enough to ignore.

    Though, on a different note, I wonder if there are other places that could benefit from CTransaction caching. Two full hashes are cached, so caching the size should be cheap in both time and space?


    l0rinc commented at 6:48 am on October 30, 2025:

    the hash is actually eagerly calculated as well - which should also be hidden behind a similar condition, was hoping someone will bring it up :)


    there aren’t any benchmarks for this but a ton of tests failed for the throw check above, so at least it has coverage.


    davidgumberg commented at 0:28 am on October 31, 2025:

    wonder if there are other places that could benefit from CTransaction caching. Two full hashes are cached, so caching the size should be cheap in both time and space?

    I don’t think we should be caching the sizes, since there aren’t really any performance critical users of GetTotalSize(), it’s invoked by these cmpctblock logs, many RPC’s when they do pretty-printing of transactions, and the qt interface’s transaction view, but there are critical paths where CTransaction()’s are constructed.


    the hash is actually eagerly calculated as well

    As far as I can tell, CTransaction’s hashes are computed and cached on construction: https://github.com/bitcoin/bitcoin/blob/6f359695c36c13af832b0b1021dcb3a234f6f219/src/primitives/transaction.cpp#L95-L96 https://github.com/bitcoin/bitcoin/blob/6f359695c36c13af832b0b1021dcb3a234f6f219/src/primitives/transaction.h#L343-L344

    Are you suggesting that the hashes should be computed lazily when CTransaction::GetHash() is first invoked?

  8. maflcko commented at 6:43 am on October 30, 2025: member
    is there a benchmark that detects this?
  9. l0rinc marked this as ready for review on Oct 30, 2025
  10. DrahtBot requested review from TheCharlatan on Oct 31, 2025

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-10-31 06:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me