-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Do not use std::vector = {} to release memory #28452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
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. |
dedb59d
to
92fedcc
Compare
92fedcc
to
eb07773
Compare
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 |
@theuni For 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. |
Concept ACK. I'm curious, does this change have an observable effect on peak memory usage? |
@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). |
FWIW,
|
@hebasto I'm aware. But it appears to work better in practice than I guess we could also use the combination,
|
I was thinking just about the same :) |
9a92fda
to
8332d52
Compare
8332d52
to
43627af
Compare
PR description needs updating? |
There was a problem hiding this 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:
bitcoin/src/net_processing.cpp
Line 2602 in adc0921
headers = {}; Line 575 in adc0921
void SetNull() { stack.clear(); stack.shrink_to_fit(); }
43627af
to
3fcd7fc
Compare
The
Yeah, not going to touch that in this PR. |
There was a problem hiding this 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.
There was a problem hiding this 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.
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. |
utACK 3fcd7fc |
Github-Pull: bitcoin#28452 Rebased-From: 3fcd7fc
Backported (partial) to 25.x in #28487. |
There was a problem hiding this 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.
Github-Pull: bitcoin#28452 Rebased-From: 3fcd7fc
Github-Pull: bitcoin#28452 Rebased-From: 3fcd7fc
Github-Pull: bitcoin#28452 Rebased-From: 3fcd7fc
Github-Pull: bitcoin#28452 Rebased-From: 3fcd7fc
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
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
It appears that invoking
v = {};
for anstd::vector<...> v
is equivalent tov.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 withstd::vector<...>{}.swap(v);
(using a helper functionClearShrink
in util/vector.h).To explain what is going on:
v = {...};
is equivalent in general tov.operator=({...});
. For many types, the{}
is converted to the type ofv
, and then assigned tov
- which forstd::vector
would ordinarily have the effect of clearing its memory (constructing a new empty vector, and then move-assigning it tov
). However, sincestd::vector<T>
has anoperator=(std::initializer_list<T>)
defined, it has precedence (since no implicit conversion is needed), and with an empty list, that is equivalent toclear()
.I did consider using
v = std::vector<T>{};
as replacement forv = {};
instances where memory releasing is desired, but it appears that it does not actually work universally either.V{}.swap(v);
does.