-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Cache witness hash in CTransaction #13011
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
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.
utACK fae63d51ab252579adbaceb6aaba935bae75de88. Code looks ok, but what's the motivation for this change? I imagine there are a lot of performance implications.
Also, why is unique_ptr<uint256>
used for the witness hash when the plain hash is just uint256
?
src/primitives/transaction.cpp
Outdated
} | ||
return SerializeHash(*this, SER_GETHASH, 0); | ||
return std::unique_ptr<const uint256>{new uint256{SerializeHash(*this, SER_GETHASH, 0)}}; |
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.
Could use MakeUnique<const uint256>(SerializeHash(*this, SER_GETHASH, 0));
here
src/primitives/transaction.cpp
Outdated
CTransaction::CTransaction(CMutableTransaction &&tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} | ||
CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash{}, m_witness_hash{nullptr} {} | ||
CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} | ||
CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} |
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.
I believe you could remove some of the boilerplate and repetition here using member initialization in the header:
const std::unique_ptr<const uint256> m_witness_hash = ComputeWitnessHash();
It would be nice to do this for the other members, too.
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.
I wonder what would be the execution order in this case? ComputeWitnessHash
calls SerializeHash(*this, ...)
so other members must be initialized before.
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.
I wonder what would be the execution order in this case? ComputeWitnessHash calls SerializeHash(*this, ...) so other members must be initialized before.
Execution order always the matches order members are declared in the class, not the order initializations are written in the constructor (compilers will warn if the orders differ), so this wouldn't change behavior. You can see https://isocpp.org/wiki/faq/ctors#ctor-initializer-order for discussion about this.
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.
We couldn't do the same for the hash
member, I guess, so will leave this as is for now.
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.
Concept ACK.
I guess the extra memory usage is neglectable.
src/primitives/transaction.h
Outdated
// Compute a hash that includes both transaction and witness data | ||
uint256 GetWitnessHash() const; | ||
const uint256& GetHash() const { return hash; } | ||
const uint256& GetWitnessHash() const { return m_witness_hash ? *m_witness_hash : hash; }; |
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.
Could this expression be in ComputeWitnessHash
?
Concept ACK |
Honestly I think we need to revive #9700. Now that SegWit has some use, giving up on the huge performance gains of that (and this) for (IMO) minor design concerns is an absurd tradeoff. |
Then at the very least introduce alternate versions of CTransaction/CBlock that don't precompute anything, for use in reindexing and rescanning and serving blocks from disk. Otherwise you'll waste your time computing wtxids/sighashes in those cases, where they're never used. |
Indeed, though we may want to look into something wholesale smarter for rescan, its already impossibly slow even when considering the amount of I/O required. |
fae63d5
to
fa77996
Compare
Silently converting to a CMutableTransaction will drop all caches and should thus be done explicitly
fa77996
to
fac1223
Compare
Split into two commits for easier review |
utACK fac1223 Numbers would be nice, but the code looks good. |
Was a discussion about this in IRC meeting yesterday: https://botbot.me/freenode/bitcoin-core-dev/msg/99929243/ Beginning of discussion before it goes on to talk about related PRs like #13098
Continues https://botbot.me/freenode/bitcoin-core-dev/msg/99929408/ |
This speeds up:
This presumably slows down rescan, which uses a |
Note: You can run benchmarks such as:
|
If performance is a concern in contexts where the precomputed value isn't needed, couldn't this just lazily compute and cache the witness hash? There'd probably have to be some concurrency handling, I suppose. |
avoid "some concurrency handling" was the reason for making these fields const in the first place. The alternative is either indirections (through atomic pointers) or mutexes all over the place. The solution to not computing it in places where it isn't needed is not using a type that caches these values. |
utACK fac1223. |
utACK fac1223 |
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.
utACK fac1223. Only change since previous review is making m_witness_hash
member const uint256
instead of std::unique_ptr<const uint256>
, and adding the new cleanup commit: "Make CMutableTransaction constructor explicit …"
utACK fac1223 |
utACK fac1223 |
utACK fac1223 |
fac1223 Cache witness hash in CTransaction (MarcoFalke) faab55f Make CMutableTransaction constructor explicit (MarcoFalke) Pull request description: This speeds up: * compactblocks (v2) * ATMP * validation and miner (via `BlockWitnessMerkleRoot`) * sigcache (see also unrelated #13204) * rpc and rest (nice, but irrelevant) This presumably slows down rescan, which uses a `CTransaction` and its `GetHash`, but never uses the `GetWitnessHash`. The slow down is proportional to the number of witness transactions in the rescan window. I.e. early in the chain there should be no measurable slow down. Later in the chain, there should be a slow down, but acceptable given the speedups in the modules mentioned above. Tree-SHA512: 443e86acfcceb5af2163e68840c581d44159af3fd1fce266cab3504b29fcd74c50812b69a00d41582e7e1c5ea292f420ce5e892cdfab691da9c24ed1c44536c7
fac1223 Cache witness hash in CTransaction (MarcoFalke) faab55f Make CMutableTransaction constructor explicit (MarcoFalke) Pull request description: This speeds up: * compactblocks (v2) * ATMP * validation and miner (via `BlockWitnessMerkleRoot`) * sigcache (see also unrelated bitcoin#13204) * rpc and rest (nice, but irrelevant) This presumably slows down rescan, which uses a `CTransaction` and its `GetHash`, but never uses the `GetWitnessHash`. The slow down is proportional to the number of witness transactions in the rescan window. I.e. early in the chain there should be no measurable slow down. Later in the chain, there should be a slow down, but acceptable given the speedups in the modules mentioned above. Tree-SHA512: 443e86acfcceb5af2163e68840c581d44159af3fd1fce266cab3504b29fcd74c50812b69a00d41582e7e1c5ea292f420ce5e892cdfab691da9c24ed1c44536c7
fac1223 Cache witness hash in CTransaction (MarcoFalke) faab55f Make CMutableTransaction constructor explicit (MarcoFalke) Pull request description: This speeds up: * compactblocks (v2) * ATMP * validation and miner (via `BlockWitnessMerkleRoot`) * sigcache (see also unrelated bitcoin#13204) * rpc and rest (nice, but irrelevant) This presumably slows down rescan, which uses a `CTransaction` and its `GetHash`, but never uses the `GetWitnessHash`. The slow down is proportional to the number of witness transactions in the rescan window. I.e. early in the chain there should be no measurable slow down. Later in the chain, there should be a slow down, but acceptable given the speedups in the modules mentioned above. Tree-SHA512: 443e86acfcceb5af2163e68840c581d44159af3fd1fce266cab3504b29fcd74c50812b69a00d41582e7e1c5ea292f420ce5e892cdfab691da9c24ed1c44536c7
fac1223 Cache witness hash in CTransaction (MarcoFalke) faab55f Make CMutableTransaction constructor explicit (MarcoFalke) Pull request description: This speeds up: * compactblocks (v2) * ATMP * validation and miner (via `BlockWitnessMerkleRoot`) * sigcache (see also unrelated bitcoin#13204) * rpc and rest (nice, but irrelevant) This presumably slows down rescan, which uses a `CTransaction` and its `GetHash`, but never uses the `GetWitnessHash`. The slow down is proportional to the number of witness transactions in the rescan window. I.e. early in the chain there should be no measurable slow down. Later in the chain, there should be a slow down, but acceptable given the speedups in the modules mentioned above. Tree-SHA512: 443e86acfcceb5af2163e68840c581d44159af3fd1fce266cab3504b29fcd74c50812b69a00d41582e7e1c5ea292f420ce5e892cdfab691da9c24ed1c44536c7
fac1223 Cache witness hash in CTransaction (MarcoFalke) faab55f Make CMutableTransaction constructor explicit (MarcoFalke) Pull request description: This speeds up: * compactblocks (v2) * ATMP * validation and miner (via `BlockWitnessMerkleRoot`) * sigcache (see also unrelated bitcoin#13204) * rpc and rest (nice, but irrelevant) This presumably slows down rescan, which uses a `CTransaction` and its `GetHash`, but never uses the `GetWitnessHash`. The slow down is proportional to the number of witness transactions in the rescan window. I.e. early in the chain there should be no measurable slow down. Later in the chain, there should be a slow down, but acceptable given the speedups in the modules mentioned above. Tree-SHA512: 443e86acfcceb5af2163e68840c581d44159af3fd1fce266cab3504b29fcd74c50812b69a00d41582e7e1c5ea292f420ce5e892cdfab691da9c24ed1c44536c7
fac1223 Cache witness hash in CTransaction (MarcoFalke) faab55f Make CMutableTransaction constructor explicit (MarcoFalke) Pull request description: This speeds up: * compactblocks (v2) * ATMP * validation and miner (via `BlockWitnessMerkleRoot`) * sigcache (see also unrelated bitcoin#13204) * rpc and rest (nice, but irrelevant) This presumably slows down rescan, which uses a `CTransaction` and its `GetHash`, but never uses the `GetWitnessHash`. The slow down is proportional to the number of witness transactions in the rescan window. I.e. early in the chain there should be no measurable slow down. Later in the chain, there should be a slow down, but acceptable given the speedups in the modules mentioned above. Tree-SHA512: 443e86acfcceb5af2163e68840c581d44159af3fd1fce266cab3504b29fcd74c50812b69a00d41582e7e1c5ea292f420ce5e892cdfab691da9c24ed1c44536c7
This speeds up:
BlockWitnessMerkleRoot
)This presumably slows down rescan, which uses a
CTransaction
and itsGetHash
, but never uses theGetWitnessHash
. The slow down is proportional to the number of witness transactions in the rescan window. I.e. early in the chain there should be no measurable slow down. Later in the chain, there should be a slow down, but acceptable given the speedups in the modules mentioned above.