The RPC calls getreceivedbyaddress/getreceivedbylabel both use the internal helper function GetReceived which was introduced in PR #17579 to deduplicate tallying code. For every wallet-related transaction output, the following unnecessary operations are currently performed in the tally loop, leading to a quite bad performance (as reported in #23645):
- converting from CScript -> TxDestination (
ExtractDestination(...)), converting from TxDestination -> CScript (CWallet::IsMine(const CTxDestination& dest)); this can be avoided by directly using output scripts in the search set instead of addresses (first commit) - checking if the iterated output script belongs to the wallet by calling
IsMine; this can be avoided by only adding addresses to the search set which fulfilIsMinein the first place (second commit)
Benchmark results
The functional test wallet_pr23662.py (not part of this PR) creates transactions with 15000 different addresses:
- 5000 outputs (500 txs with 10 outputs each) with label set, IsMine set (received)
- 5000 outputs (500 txs with 10 outputs each) with label set, IsMine not set (sent)
- 5000 outputs (500 txs with 10 outputs each) without label set, IsMine not set (sent)
Then, the time is measured for calling getreceivedbyaddress and getreceivedbylabel, the latter with two variants. Results on my machine:
| branch | getreceivedbyaddress (single) |
getreceivedbylabel (single) |
getreceivedbylabel (10000) |
|---|---|---|---|
| master | 406.13ms | 425.33ms | 446.58ms |
| PR (first commit) | 367.18ms | 365.81ms | 426.33ms |
| PR (second commit) | 3.96ms | 4.83ms | 339.69ms |
Fixes #23645.