-
Notifications
You must be signed in to change notification settings - Fork 37.8k
Cache vout IsMine() value on CWallet::AvailableCoins()
#9939
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
Cache vout IsMine() value on CWallet::AvailableCoins()
#9939
Conversation
src/wallet/wallet.cpp
Outdated
mine = mapOutputIsMine[pcoin->tx->vout[i].scriptPubKey] = IsMine(pcoin->tx->vout[i]); | ||
} | ||
|
||
if (mine != ISMINE_NO && !(IsSpent(wtxid, i)) && |
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 there's an ACK from the community, I would love to see this giant if
break down with several early continues (to facilitate the reading), as applied here.
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.
This would seem like a good idea, especially if the continues were documented with new comments. Not really on-topic for this PR, however.
Please check #9167, morcos is working on it |
@pedrobranco yes, that @NicolasDorier If I understood the @morcos PR correctly, it's related to the |
my bad I read too fast. |
Why not having the cache scoped to the WalletTransaction type ?
we would have
where WalletTransaction::IsMine would cache the result at the WalletTransaction level.
|
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 dca8d8926e655ba6639e748bcd32c560a5539b88
Code seems fine, but I don't know very much about the performance implications of this change, and would leave it for others to discuss. NicolasDorier's suggestion seems like a reasonable alternative, too.
src/wallet/wallet.cpp
Outdated
mine = im->second; | ||
} else { | ||
mine = mapOutputIsMine[pcoin->tx->vout[i].scriptPubKey] = IsMine(pcoin->tx->vout[i]); | ||
} |
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.
Can simplify this as:
+ auto inserted = mapOutputIsMine.emplace(pcoin->tx->vout[i].scriptPubKey, ISMINE_NO);
+ if (inserted.second) {
+ inserted.first->second = IsMine(pcoin->tx->vout[i]);
+ }
+ isminetype mine = inserted.first->second;
src/wallet/wallet.cpp
Outdated
mine = mapOutputIsMine[pcoin->tx->vout[i].scriptPubKey] = IsMine(pcoin->tx->vout[i]); | ||
} | ||
|
||
if (mine != ISMINE_NO && !(IsSpent(wtxid, i)) && |
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.
This would seem like a good idea, especially if the continues were documented with new comments. Not really on-topic for this PR, however.
@NicolasDorier that's a good suggestion. Actually a while ago I was trying to create a cache to store these values, but at the wallet level. But couldn't manage to successfully achieve a solution with both reliability and speed. But I will look into your approach. @ryanofsky Ok, I like it. I'll simplify that piece of code. |
dca8d89
to
46cbc0a
Compare
I've tested 46cbc0a Before median time of listunspent after a few runs on my wallet was 14.4 seconds, now it's 12.8s. So it seems faster, but not earth shatteringly so. For reference my wallet has about ~520k transactions in it, where of them ~168k are mine (outgoing transactions). -- To me it seems like @NicolasDorier's suggestion is the way to go, I suspect the reuse of the cache is going to help a lot. |
@RHavar thanks for testing! |
@RHavar thanks a lot for your feedback! The current implementation of I've tried to optimize each of the two heavier calls within I haven't had much time to work on this lately, but it's a problem I'd like to come up with a clean solution in the near future, since it's an issue affecting almost everyone with large wallets. |
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 46cbc0a. Code is slightly simplified since last review, and change looks good assuming performance improvements warrant it.
utACK 46cbc0a, it is simple enough. Can keep wallet level caching for later PR. |
Change is simple and has multiple acks, but needs to be rebased. |
Not rebased in 30 days. This is now up for grabs. |
Sorry guys, haven't had much time to work on this lately.. Although this improves the speed, the last tests I did showed there are still some edge cases where the unspent count differs which means that the cache is not accurate.. I'll try to pick this up in the future, and reopen if needed. |
Could add up for grabs label |
Special thanks to @joaopaulofonseca for contributing this to the Bitcoin repo bitcoin/bitcoin#9939
This PR tries to improve the performance of the
listunspent
,fundrawtransaction
,sendtoaddress
and other RPC calls by improvingCWallet::AvailableCoins()
iteration through the wallet transactions.E.g. If a given address is an outpoint of N transactions, I guess there's no point to check N times if the address
IsMine()
.This PR supersedes #7823 although it's a different approach. In that PR, caching accurately those values was a little tricky because of all the scenarios that could make the cache invalidated (conflicts, double-spends, reorganizations, abandoned transactions, etc, ...).
Still, with this approach I got performance gains up to 90% on
CWallet::AvailableCoins()
for large wallets.