Skip to content

Conversation

sipa
Copy link
Member

@sipa sipa commented Sep 11, 2023

It appears that invoking v = {}; for an std::vector<...> v is equivalent to v.clear(), which does not release its allocated memory. There are a number of places in the codebase where it appears to be used for that purpose however (mostly written by me). Replace those with std::vector<...>{}.swap(v); (using a helper function ClearShrink in util/vector.h).

To explain what is going on: v = {...}; is equivalent in general to v.operator=({...});. For many types, the {} is converted to the type of v, and then assigned to v - which for std::vector would ordinarily have the effect of clearing its memory (constructing a new empty vector, and then move-assigning it to v). However, since std::vector<T> has an operator=(std::initializer_list<T>) defined, it has precedence (since no implicit conversion is needed), and with an empty list, that is equivalent to clear().

I did consider using v = std::vector<T>{}; as replacement for v = {}; instances where memory releasing is desired, but it appears that it does not actually work universally either. V{}.swap(v); does.

@DrahtBot
Copy link
Contributor

DrahtBot commented Sep 11, 2023

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

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK stickies-v, theStack, ajtowns
Concept ACK theuni, hebasto

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #27596 (assumeutxo (2) by jamesob)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@sipa sipa force-pushed the 202309_really_no_memory branch from dedb59d to 92fedcc Compare September 11, 2023 18:53
@sipa sipa force-pushed the 202309_really_no_memory branch from 92fedcc to eb07773 Compare September 11, 2023 19:06
@theuni
Copy link
Member

theuni commented Sep 11, 2023

Concept ACK on the msgs, but do we really want to release all mem all the time for the others? Without the shrinks, we essentially keep a high-water mark for memory where future allocations are unnecessary.

I poked quickly at m_send_buffer for in MarkBytesSent for example, and it seems like that means constant reallocation now, no?

@sipa
Copy link
Member Author

sipa commented Sep 11, 2023

@theuni For m_send_buffer, m_recv_buffer, and m_recv_decode_buffer it's not unreasonable to e.g. permit a certain lingering memory usage. E.g. if the capacity is under 64 KiB, don't wipe.

I considered that before, but wasn't sure it'd be worth the discussion. We're already doing several allocations per P2P message already in various places, some of which will require more refactoring to get rid of, so one more or less probably doesn't matter that much.

For this PR, I wanted to just make it do what it was intending to do already.

@hebasto
Copy link
Member

hebasto commented Sep 11, 2023

Concept ACK.

I'm curious, does this change have an observable effect on peak memory usage?

@sipa
Copy link
Member Author

sipa commented Sep 11, 2023

@hebasto Not right now, as V2Transport isn't hooked up to anything. But once it is, without this PR, after sending a 4 MB message you'd permanently keep 4 MB allocated for that connection (and similarly for the receive side).

@hebasto
Copy link
Member

hebasto commented Sep 11, 2023

FWIW, shrink_to_fit:

... is a non-binding request to reduce capacity() to size(). It depends on the implementation whether the request is fulfilled.

@sipa
Copy link
Member Author

sipa commented Sep 11, 2023

@hebasto I'm aware. But it appears to work better in practice than v = std::vector<T>{};.

I guess we could also use the combination, v = std::vector<T>{}; v.shrink_to_fit();.

v.swap(std::vector<T>{}); is also an option, possibly followed by v.shrink_to_fit();.

@hebasto
Copy link
Member

hebasto commented Sep 11, 2023

v.swap(std::vector<T>{}); is also an option, possibly followed by v.shrink_to_fit();.

I was thinking just about the same :)

@sipa sipa force-pushed the 202309_really_no_memory branch from 9a92fda to 8332d52 Compare September 12, 2023 12:07
@ajtowns
Copy link
Contributor

ajtowns commented Sep 13, 2023

Replace those with v.clear(); v.shrink_to_fit();.

PR description needs updating?

Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code review ACK 43627af

Found two more instances where ClearShrink seems appropriate:

@sipa sipa force-pushed the 202309_really_no_memory branch from 43627af to 3fcd7fc Compare September 13, 2023 11:20
@sipa
Copy link
Member Author

sipa commented Sep 13, 2023

@stickies-v

Found two more instances where ClearShrink seems appropriate:

The headers variable here is just a local variable that will go out of scope when the received headers are processed; there is no long-term memory usage we're concerned about. I believe the intent of the = {}; there is just to clear the vector (so headers don't get processed a second time).

  • void SetNull() { stack.clear(); stack.shrink_to_fit(); }
    (but probably not worth adding in this pull as it's consensus code)

Yeah, not going to touch that in this PR.

Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 3fcd7fc

The headers variable here is just a local variable that will go out of scope...

Thanks, I looked at the callstacks and have come to the same conclusion.

Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code-review ACK 3fcd7fc

Question out of curiosity: does the C++17 standard guarantee that default-constructed (i.e. new and empty) std::vectors have an initial capacity of zero? If not (as e.g. suggested by https://stackoverflow.com/a/23415582 or https://tylerayoung.com/2020/08/20/default-capacity-growth-rate-of-c-stdvector/ -- not sure what C++ version specifically they are talking about though), then strictly speaking there'd be no guaranteed way to clear the memory that is not implementation-dependent, it seems.

@sipa
Copy link
Member Author

sipa commented Sep 14, 2023

Question out of curiosity: does the C++17 standard guarantee that default-constructed (i.e. new and empty) std::vectors have an initial capacity of zero? If not (as e.g. suggested by https://stackoverflow.com/a/23415582 or https://tylerayoung.com/2020/08/20/default-capacity-growth-rate-of-c-stdvector/ -- not sure what C++ version specifically they are talking about though), then strictly speaking there'd be no guaranteed way to clear the memory that is not implementation-dependent, it seems.

I skimmed over the C++17 spec, but can't find anything about capacity of newly-constructed vectors. I don't think we need to care about this though; as that SO link says, I think it'd be nonsensical for an implementation to allocate anything for an empty newly-constructed vector.

@fanquake fanquake requested a review from ajtowns September 14, 2023 10:34
@ajtowns
Copy link
Contributor

ajtowns commented Sep 15, 2023

utACK 3fcd7fc

@DrahtBot DrahtBot removed the request for review from ajtowns September 15, 2023 02:43
@fanquake fanquake merged commit 8ef6729 into bitcoin:master Sep 15, 2023
fanquake pushed a commit to fanquake/bitcoin that referenced this pull request Sep 15, 2023
@fanquake
Copy link
Member

Backported (partial) to 25.x in #28487.

Copy link
Member

@hebasto hebasto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post-merge ACK 3fcd7fc.

luke-jr added a commit to luke-jr/bitcoin that referenced this pull request Sep 16, 2023
Frank-GER pushed a commit to syscoin/syscoin that referenced this pull request Sep 19, 2023
@sipa sipa mentioned this pull request Sep 19, 2023
43 tasks
fanquake pushed a commit to fanquake/bitcoin that referenced this pull request Sep 26, 2023
fanquake pushed a commit to fanquake/bitcoin that referenced this pull request Sep 26, 2023
fanquake pushed a commit to fanquake/bitcoin that referenced this pull request Sep 26, 2023
fanquake pushed a commit to fanquake/bitcoin that referenced this pull request Oct 2, 2023
fanquake added a commit that referenced this pull request Oct 4, 2023
45a5fcb http: bugfix: track closed connection (stickies-v)
752a456 http: log connection instead of request count (stickies-v)
ae86ada http: refactor: use encapsulated HTTPRequestTracker (stickies-v)
f31899d gui: macOS, make appMenuBar part of the main app window (furszy)
64ffa94 gui: macOS, do not process dock icon actions during shutdown (furszy)
e270f3f depends: fix unusable memory_resource in macos qt build (fanquake)
a668394 build, macos: Fix `qt` package build with new Xcode 15 linker (Hennadii Stepanov)
b3517cb test: Test loading wallets with conflicts without a chain (Andrew Chow)
d63478c wallet: Check last block and conflict height are valid in MarkConflicted (Andrew Chow)
5e51a9c ci: Nuke Android APK task, Use credits for tsan (MarcoFalke)
910c362 test: ensure old fee_estimate.dat not read on restart and flushed (ismaelsadeeq)
37764d3 tx fees, policy: read stale fee estimates with a regtest-only option (ismaelsadeeq)
16bb916 tx fees, policy: do not read estimates of old fee_estimates.dat (ismaelsadeeq)
c4dd598 tx fees, policy: periodically flush fee estimates to fee_estimates.dat (ismaelsadeeq)
c36770c test: wallet, verify migration doesn't crash for an invalid script (furszy)
0d2a33e wallet: disallow migration of invalid or not-watched scripts (furszy)
2c51a07 Do not use std::vector = {} to release memory (Pieter Wuille)

Pull request description:

  Further backports for the `25.x` branch. Currently:
  * #27622
  * #27834
  * #28125
  * #28452
  * #28542
  * #28543
  * #28551
  * #28571
  * bitcoin-core/gui#751

ACKs for top commit:
  hebasto:
    re-ACK 45a5fcb, only #28551 has been backported with since my recent [review](#28487 (review)).
  dergoegge:
    reACK 45a5fcb
  willcl-ark:
    reACK 45a5fcb

Tree-SHA512: 0f5807aa364b7c2a2039fef11d5cd5e168372c3bf5b0e941350fcd92e7db4a1662801b97bb4f68e29788c77d24bbf97385a483c4501ca72d93fa25327d5694fa
fanquake added a commit that referenced this pull request Oct 6, 2023
9077f21 depends: fix unusable memory_resource in macos qt build (fanquake)
dccacf0 build, macos: Fix `qt` package build with new Xcode 15 linker (Hennadii Stepanov)
4359649 ci: Switch to `amd64` container in "ARM" task (Hennadii Stepanov)
805f98b ci: Nuke Android APK task, Use credits for tsan (MarcoFalke)
cb5512d test: ensure old fee_estimate.dat not read on restart and flushed (ismaelsadeeq)
01f8ee4 tx fees, policy: read stale fee estimates with a regtest-only option (ismaelsadeeq)
1c98029 tx fees, policy: do not read estimates of old fee_estimates.dat (ismaelsadeeq)
77979e0 tx fees, policy: periodically flush fee estimates to fee_estimates.dat (ismaelsadeeq)
67b6d99 Do not use std::vector = {} to release memory (Pieter Wuille)
defdc15 ci: Use podman stop over podman kill (MarcoFalke)
7f1357d ci: Use podman for persistent workers (MarcoFalke)
0db69a3 ci: Prune dangling images on RESTART_CI_DOCKER_BEFORE_RUN (MarcoFalke)

Pull request description:

  Backports to the 24.x branch. Currently:
  * #27622
  * #27777
  * #27834
  * #27844
  * #27886
  * #28452
  * #28543
  * #28571

ACKs for top commit:
  stickies-v:
    ACK 9077f21

Tree-SHA512: abaafc9a048b67b494993134fd332457ea52695ec007b963c283f962ec40c3b6b3a7e98407481be55d3271a595088a0281cc84b79dad4f24d260381ea0153076
@bitcoin bitcoin locked and limited conversation to collaborators Sep 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants