This PR:
- gets rid of the
policy/fees
->txmempool
->policy/fees
circular dependency - is an alternative to #13949, which nukes only one circular dependency
Related discussion:
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
See the guideline for information on the review process.
Type | Reviewers |
---|---|
ACK | ryanofsky, theStack, glozow |
Concept ACK | practicalswift, JeremyRubin, Empact |
Stale ACK | promag |
Reviewers, this pull request conflicts with the following ones:
int32_t
type for transaction size/weight consistently by hebasto)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.
ACK fbf6b7524eac8a7668e49b7c340d06447a9d248d.
I’d squash 1st and 2nd commits. 3rd commit is unrelated but is a nice cleanup.
Generally agree with this change, I’ve thought about doing it myself a few times myself. It also seems to be a good alternative to the observer pattern mentioned as we change far fewer things to get the same circular dependency break. So a slight code review ACK that this seems correct.
Slightly short of concept ack for a few reasons:
… to show that we haven’t made functions calling the CTxMemPoolEntry’s non-header functions slower because they are no longer within the same compilation unit
How is it possible?
It’s annoying, but because these functions were previously in the same translation unit then it’s possible they can get completely inlined, but now they’re in separate units they will definitely trigger function calls.
Because these updater functions get called in hot loops you have possibly replaced something optimized previously with something slow.
This matters a bit because we may expect these loops/regions to get even hotter if we increase mempool limits.
One way of addressing this would be to make CTxMemPoolEntry header only…
It’s annoying, but because these functions were previously in the same translation unit then it’s possible they can get completely inlined, but now they’re in separate units they will definitely trigger function calls.
IIUC, making function inline depends on compiler and its optimization settings. The bitcoind
compiled with GCC 7.4.0 was checked with nm
tool, and CTxMemPoolEntry
member function symbols are present, therefore those functions are not inlined (except those which are defined in class declaration, as expected).
It seems runtime performance issues and circular dependency resolving are orthogonal. And “yes”, we could
… make CTxMemPoolEntry header only…
Correct; this is compiler dependent behavior. But we happen to compile with optimizations enabled for release builds – not sure if we do different flags for non release builds (I think it’s the same?).
Presence of the symbols doesn’t prove that they aren’t also inlined. An inline function is a hint to inline, not a mandate, and the function can be either called or or inlined depending on context, you would need to check the actual call sites.
I agree it’s not a large issue; but specifically code organization does affect emitted code (this is what LTO is intended, in a sense, to get around, but I don’t think we do that other than experimentally)
Presence of the symbols doesn’t prove that they aren’t also inlined. An inline function is a hint to inline, not a mandate, and the function can be either called or or inlined depending on context, you would need to check the actual call sites.
I mean if a function is inlined in all call sites, there is no dedicated assembly code and no symbol. Could a compiler inline a function in some call sites and make a standard function call of the same function in other call sites?
Unless the symbol isn’t exported there will be a separate copy of it included in the object for external callers. That extra copy will persist in the final binary so long as there are any non-inlined callers.
For a quick check, just look at the size of all functions in dump of the binary. If they’re all the same size then it’s very likely that all you did was move things around.
inline
is more than a hint, and actually has a meaning: it’s a function that may be defined (and exported from) multiple translation units without conflicting with itself (it’s only well defined if all definitions are identical, though). This is what allows having code in .h files. In practice it means the linker will merge all the definitions into one symbol.
I think if you were to just put all the mempool entry functions into the header I wouldn’t have any concerns. It doesn’t seem to me like there’s a really strong reason for them to be separate, and any time you change the initializers you’ve likely also changed the class definition so it doesn’t seem like it would trigger recompilation explosions.
It also (maybe I’m wrong here) looks like you have some unneeded dependencies in the cpp (e.g., memusage), so that can be pruned out to keep the header small.
@JeremyRubin Thank you for your review.
I think if you were to just put all the mempool entry functions into the header I wouldn’t have any concerns.
Done.
It also (maybe I’m wrong here) looks like you have some unneeded dependencies in the cpp (e.g., memusage), so that can be pruned out to keep the header small.
All #include
s are checked again. They all are needed, e.g., core_memusage.h
is needed for RecursiveDynamicUsage
.
Updated 19f6bf826d890cc78bd4a19f10dfbf59dd58c10d -> 287b930a18921716bdf6961350959804042b46b0 (pr17786.07 -> pr17786.08, diff):
76+ LockPoints lockPoints; //!< Track the height and time at which tx was final
77+
78+ // Information about descendants of this transaction that are in the
79+ // mempool; if we remove this transaction we must remove all of these
80+ // descendants as well.
81+ uint64_t nCountWithDescendants{1}; //!< number of descendant transactions
Looks correct / refactor only.
CR-Ack.
Why the close?
Reopened and rebased 9782a8ee773a59667424353de047fd6bb9c3cb5d -> 77438e18478ca0a06708f3e988fdb31567bb76e0 (pr17786.10 -> pr17786.11) on top of the merged #23054 and the recent CI changes.
112+ const CTransaction& GetTx() const { return *this->tx; }
113+ CTransactionRef GetSharedTx() const { return this->tx; }
114+ const CAmount& GetFee() const { return nFee; }
115+ size_t GetTxSize() const
116+ {
117+ return GetVirtualTransactionSize(nTxWeight, sigOpCost);
There were concerns from @JeremyRubin:
I think if you were to just put all the mempool entry functions into the header I wouldn’t have any concerns.
Concept ACK to removing this circular dependency
But perhaps a better approach would be to remove minerPolicyEstimator
from txmempool, make it a client of CValidationInterface
instead (will need to return more information to clients in the interface), and remove policy/fees
as a dependency of txmempool
. In fact, it seems like background fee estimator has been planned for a while (https://github.com/bitcoin/bitcoin/pull/10199#discussion_r115530273) and implemented but unmerged at some point (#11775).
Code review ACK f140f9468bfa505a34112d625128f3fcff18ddba. This seems like an improvement over status quo to remove a circular dependency and improve code organization.
I agree with Gloria in #17786 (comment) other improvements could also be made which remove the circular dependency, but they seem complimentary and could build on this.
I think all the other discussion about code inlining and runtime performance and build performance makes sense, but I don’t think those issues are a lot more pertinent to this PR than a lot of other PRs. So it just seems unlucky for this PR that they were brought up here. It would be good if we could think of a standard approach to avoiding performance regressions that could apply more fairly.
One other suggestion to make the PR more reviewable might be to split it into two commits: a move-only commit that just moved the CTxMemPoolEntry
class definition without changing it, followed by a commit inlining its various methods. (This PR has been open a while and I probably would have reviewed it earlier if the bigger moveonly change didn’t include other changes and was easier to verify.)
Thank you for your review!
One other suggestion to make the PR more reviewable might be to split it into two commits: a move-only commit that just moved the
CTxMemPoolEntry
class definition without changing it, followed by a commit inlining its various methods.
Done.
This change nukes the policy/fees->mempool circular dependency.
Easy to review using `diff --color-moved=dimmed-zebra`.
Code-review ACK c8dc0e3eaa9e0f956c5177bcb69632beb0d51770
Nice to see another circular dependency gone 💯
260@@ -261,6 +261,7 @@ BITCOIN_CORE_H = \
261 torcontrol.h \
262 txdb.h \
263 txmempool.h \
264+ txmempool_entry.h \
kernel/txmempool_entry.h
?
62@@ -62,6 +63,7 @@ implicit-integer-sign-change:script/bitcoinconsensus.cpp
63 implicit-integer-sign-change:script/interpreter.cpp
64 implicit-integer-sign-change:serialize.h
65 implicit-integer-sign-change:txmempool.cpp
66+implicit-integer-sign-change:txmempool_entry.h