The other day @gmaxwell pointed out on IRC that we’re calculating the block hash many more times than we need to. This attempts to improve that and should result in better performance but I haven’t run any benchmark.
Contains: #9717
2402 // unless we're still syncing with the network.
2403 // Such an unrequested block may still be processed, subject to the
2404 // conditions in AcceptBlock().
2405 bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
2406- const uint256 hash(pblock->GetHash());
2407+ const uint256 hash(blockHash);
2819@@ -2820,10 +2820,10 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
2820 return true;
2821 }
2822
2823-bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW)
2019@@ -2020,6 +2020,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2020 // Keep a CBlock for "optimistic" compactblock reconstructions (see
2021 // below)
2022 std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
2023+ const uint256& blockHash = pblock->GetHash();
2819@@ -2820,10 +2820,10 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
2820 return true;
2821 }
2822
2823-bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams)
2824+static bool CheckProof(const Consensus::Params& consensusParams, const CBlockHeader& block, const uint256& blockHash, CValidationState& state)
I think a better approach would be to cache it. A bit like it is already done in CTransaction versus CMulableTransaction
What is better about it? Anyway, this doesn’t preclude from caching it too.
Needs rebase
Rebased
EDIT: @NicolasDorier I think even if we do that it’s better that some functions take the hash directly, maybe not all the the functions here. But I’m happy to review such a PR.
Hm. if the block were made immutable, caching the result would be trivial. The only place where a block is not immutable in the code is in the creation of a block (where we don’t care about the hash). These changes require carrying around a hash where with an immutable block it would just be a method to return it.
So while these changes don’t preclude caching it, I think the caching it patch would be smaller and simpler. Thoughts?
3235@@ -3235,7 +3236,8 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
3236 CValidationState state;
3237 // Ensure that CheckBlock() passes before calling AcceptBlock, as
3238 // belt-and-suspenders.
3239- bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
3240+ const uint256 blockHash = pblock->GetHash();
In commit “Pow: Optimization: Pass const uint256& blockHash to CheckBlock”
Some places you are using const uint256
and other places you are using const uint256&
for the blockHash
local. Both ways should be functionality identical, but it might be nice to stick with a consistent style.
utACK 2069f9282cebb1c08b7e12c8155de3403dda39b4. Nice cleanup.
If a small caching implementation is possible, that would probably give a preferable end result. But this PR very simple and an obvious improvement over the status quo.
1692@@ -1693,13 +1693,14 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
1693 {
1694 AssertLockHeld(cs_main);
1695 assert(pindex);
1696+ const uint256& blockHash = block.GetHash();
hashBlock
would be correct here.
3150@@ -3151,6 +3151,7 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
3151 static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock)
3152 {
3153 const CBlock& block = *pblock;
3154+ const uint256& blockHash = pblock->GetHash();
const uint256 hashBlock =
here instead.
I don’t see why it would require #9717. At least here, it renames the function, although it looks like it’s doing some confusing overloading now :/
It is not strictly necessary, I can do this without any renaming and while keeping the stupid boolean argument in CheckBlockHeader if that is preferred. Or squash the first 2 commits, or rename it again to something different from “CheckProofOfWork” (but not “CheckProof” since that has been discarded already. The only thing I don’t want to do is remove CheckBlockHeader here or in #9717 . Can we please move forward and leave that decision (remove CheckBlockHeader or not [or whatever is called after rename]) for later? I thought we agreed on #9717 that it was an improvement even if some people want to go further and remove CheckBlockHeader and others aren’t convinced.
- Pow: Pass const uint256& block_hash to s/CheckBlockHeader/CheckProofOfWork/
Optimization because AcceptBlockHeader had the hash calculated already
- Pass const uint256& block_hash to CheckBlock
Optimization because now ConnectBlock calls CBlock::GetHash() less times
- Pass const uint256& block_hash to ConnectBlock
Optimization because TestBlockValidity had calculated block_hash already
- Pass const uint256& block_hash to AcceptBlock
Optimization because now AcceptBlock reuses CBlock::GetHash() calculation from ProcessNewBlock
- Pass const uint256& block_hash to ProcessNewBlock
- Call CBlock::GetHash() less in net_processing::ProcessMessage()
- Pass const uint256& block_hash to TestBlockValidity
Optimization because getblocktemplate had the hash calculated already
Aim: Block 1..300000 -reindex-chainstate
. Added instrumentation to CBlockHeader::GetHash()
:
There’s a slight bit of imprecision because -stopatheight=300000
didn’t work, so I had to use my reflexes. But I’m fairly sure the ~26% saving in block hash operations doesn’t result from only those 1130 blocks. Oh wait, it’s the other way around even :)
Let’s say I claim “In a rather naive benchmark (reindex-chainstate up to block 284k, with -assumevalid=0 and a very large dbcache), this connected blocks ~1.05x faster.” similarly to what @TheBlueMatt in #10192, would someone check this claim (did anyone checked for #10192)? If true, would that be enough for making this interesting to those who isn’t? Is anyone working on doing that kind of benchmark automatically?
I assume he did it by looking at the logs. I haven’t tried that for wither this PR or #10192, but if 1.05x is not enough…this probably gives less than that. It just seems strange that this is disregarded as “the performance improvement is not worth it” without anyone actually meassured it besides laanwj’s tests which only counts the number of hashes (not saying that’s not helpful, but that doesn’t seem to be enough).
So while these changes don’t preclude caching it, I think the caching it patch would be smaller and simpler. Thoughts?
Implicit caching, although the explicit code impact is smaller, is always somewhat error-prone. I think I prefer explicitly passing values in.
But if this isn’t worth doing, we should simply not do it. Unnecessary changes are only a risk. Closing.