Instead of calling pushKV(hash, info), which incurs in duplicate key checks, call __pushKV which (currently) doesn't.
Improves RPC getrawmempool and REST /rest/mempool/contents.json.
Fixes #14765.
Thanks @MarcoFalke, the report says >100k. I don't think this matters for small mempool.
mempool with 100k transactions:
Before:
time bitcoin-cli -regtest getrawmempool true > /dev/null
bitcoin-cli -regtest getrawmempool true > /dev/null 3.75s user 0.90s system 8% cpu 52.170 total
After:
time bitcoin-cli -regtest getrawmempool true > /dev/null
bitcoin-cli -regtest getrawmempool true > /dev/null 3.43s user 0.73s system 45% cpu 9.174 total
I guess you could duplicate one of the benchmarks
Can you point me in the right direction?
or write a functional test.
What should I test?
Could call __pushKV instead.
I don't think that's "public"?
It's declared publicly, may be __ because it does not check the type of target, which is set properly here.
__pushKV was added in https://github.com/jgarzik/univalue/commit/ceb1194137421afba01e8c1042bf75f3a2cdddc3.
Looks like it could be either private or have another name. I don't have strong preference as the goal is to avoid the duplicate key check, but I'm more inclined to not use the "internal/private/reserved" method.
OTOH it would be reasonable to add duplicate key checks to pushKVs and then we would have a performance regression.
Maybe we should rename __pushKV to pushKVUnchecked?
465 | @@ -466,7 +466,10 @@ UniValue mempoolToJSON(bool fVerbose) 466 | const uint256& hash = e.GetTx().GetHash(); 467 | UniValue info(UniValue::VOBJ); 468 | entryToJSON(info, e); 469 | - o.pushKV(hash.ToString(), info); 470 | + UniValue entry(UniValue::VOBJ); 471 | + entry.pushKV(hash.ToString(), info); 472 | + // `pushKVs` is used to avoid duplicate key check in `pushKV`
// pushKVs is used to avoid duplicate key check in pushKV
// It is safe to use because ::mempool.mapTx is a set without duplicates
@MarcoFalke not sure about the benchmark. This is a small change to fix the original issue. But I think univalue should have an official API to avoid duplicate key checks and so the benchmark should be there, or am I wrong?
Pushed with your suggestion.
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
No conflicts as of last run.
Co-Authored-By: MarcoFalke <falke.marco@gmail.com>
Rebased, switched to UniValue::__pushKV and adjusted the comment.
I've seen this PR a bit late (just after it got merged), but fwiw here is some real world improvement data:
Time it takes to GET /rest/mempool/contents.json (querys over a minute apart):
in v0.18.0 :
57.9s for 59474 unconfirmed Transactions (36.11 MB)
48.7s for 60199 unconfirmed Transactions (37.02 MB)
59.2s for 55544 unconfirmed Transactions (36.14 MB)
45.5s for 55464 unconfirmed Transactions (35.45 MB)
avg. of 52.8s
v0.18.0 with the proposed change:
35.8s for 54308 unconfirmed Transactions (34.94 MB)
40.4s for 55065 unconfirmed Transactions (35.17 MB)
37.0s for 55914 unconfirmed Transactions (35.47 MB)
33.4s for 56739 unconfirmed Transactions (35.73 MB)
avg. of 36.6s
Which for my sample is a reduction of 16.2s or 30%!! :tada:
This is an easy backport but I'm not sure if it's a candidate.
<img src="https://screenshotscdn.firefoxusercontent.com/images/669c4a3d-b06d-4592-9b78-d9028f248327.png"></img>
Milestone
0.19.0