Skip to content

runtime/Optimize accounts loading (one pass) #1157

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

HaoranYi
Copy link

@HaoranYi HaoranYi commented May 2, 2024

Problem

During transaction execution, we load accounts twice - first during filtering program accounts, and second for loading the actual accounts before execution. And by default, loading program accounts will insert the loaded program accounts into read cache, which pollutes read cache unnecessarily. This is inefficient and can be optimized.

This is an alternative idea for #1056 and #1180

Summary of Changes

  • Add a new API for account-db load_with(), which takes a callback to indicate whether to insert the loaded account into read_cache.
  • Update program account loads and transaction accounts loads to use the new API to do finer control on whether to insert the account into read_cache after loading.
  • Implement one pass for loading the account.
  • Remove the filtering pass completely.
  • Reorder loading account before program cache replenish.

Performance Comparision

image

  • account load time (top-left); prog_cache time (top-right), replay time (bottom)
  • blue (this PR), red (master)

Account loading time increased, program cache time decreased. The decrease of program cache time is more than the increase of accounts loading time. Total replay time decreased.

Fixes #

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch 2 times, most recently from 21b2fd7 to 985c590 Compare May 3, 2024 01:01
@HaoranYi HaoranYi changed the title runtime/owner check2 runtime/Optimize program and transaction accounts load 2 May 3, 2024
@HaoranYi HaoranYi changed the title runtime/Optimize program and transaction accounts load 2 runtime/Optimize program and transaction accounts load (one pass) May 3, 2024
@codecov-commenter
Copy link

codecov-commenter commented May 3, 2024

Codecov Report

Attention: Patch coverage is 99.57627% with 2 lines in your changes are missing coverage. Please review.

Project coverage is 82.7%. Comparing base (92b093d) to head (e4b9e9c).

Additional details and impacted files
@@            Coverage Diff            @@
##           master    #1157     +/-   ##
=========================================
- Coverage    82.7%    82.7%   -0.1%     
=========================================
  Files         876      876             
  Lines      371130   371181     +51     
=========================================
+ Hits       307273   307314     +41     
- Misses      63857    63867     +10     

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch from 62a6488 to e15a1a9 Compare May 3, 2024 19:47
@HaoranYi HaoranYi changed the title runtime/Optimize program and transaction accounts load (one pass) runtime/Optimize accounts loading (one pass) May 3, 2024
// Optimization to skip loading of accounts which are only used as
// programs in top-level instructions and not passed as instruction accounts.
let program_account = account_shared_data_from_program(&program);
(program.account_size, program_account, 0)
} else {
Copy link
Author

@HaoranYi HaoranYi May 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This optimization, which set program account data to empty, is not applicable
any more because we no longer prime program cache before loading the accounts.

There is also a change on the reported execution errors when a transaction is
called for uninitialized or buffer program accounts. The original code will
throw "InvalidAccountData" error, while this PR will throw
"InvalidProgramForExecution" error. The reason is that original code will mark
the program account executable and failed on a later check when execute the
program. This PR will fail on executable check in this function early on.

Fortunately, this change won't affect consensus because program errors are not
part of the consensus.

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch 2 times, most recently from 7658cc1 to e0d2ab3 Compare May 10, 2024 14:04
@HaoranYi HaoranYi marked this pull request as ready for review May 13, 2024 14:38
@Lichtso
Copy link

Lichtso commented May 13, 2024

I think the massive increase in account load time can be explained by the fact that we are still loading missing program accounts twice. And now the second read is not in the read cache combined with the fact that program accounts are big cause this to be a lot. However, this would only make sense if it is roughly proportional to the program cache misses. Can you add program cache misses to the account load time plot so we can see if it correlates?

@HaoranYi
Copy link
Author

image
program cache misses seem to be similar.

@Lichtso
Copy link

Lichtso commented May 13, 2024

Hm, problem is that the account load time has a very high base line while the program cache misses have a very low base line. So it can't explain it completely and there must be some other effect going on.

@HaoranYi
Copy link
Author

In master, we are doing a filtering pass on the accounts before actually loading the account. The filtering pass could be "prefetch" the accounts into memory by os, which benefit the loading. The filtering timing is counted into prog_cache stats in master, but no longer exists for this PR. That's why we see a big decrease in prog_cache time.

In this PR, we are not filtering and loading the accounts directly. We don't have the benefit of prefetching. Probably, that's why aaccount load_us is much higher than the base line.

@Lichtso
Copy link

Lichtso commented May 13, 2024

So, in other words it is the same time spent, just measured / counted in a different category. Makes sense.

@brooksprumo brooksprumo self-requested a review May 13, 2024 17:25
if message.is_writable(i) {
.load_account_with(key, |account| {
// only cache account that is not writable and not having program owners.
!is_account_writable && !program_owners.contains(account.owner())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you test how much performance changes if you remove the !is_account_writable &&? I suspect it would be barely measurable, as program management instructions are quite rare. Also, the writeability should not determine if it goes into the read cache or not.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
In the above graph, red, no writable check, shows better load time.
total replay time didn't change much.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you run one more test with .load_account_with(key, |account| false), effectively disabling the read caching altogether?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure. will do.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So total replay just gets a little more variance with the read caching enabled.
But you did not disable it in both places where load_account_with() is used and missing program loading still does the read cached load right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both places are disabled.
The missing program loading already have the callback to return false.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So where else is the read cache populated then? The blue read cache hits graph is not zero.

Copy link
Author

@HaoranYi HaoranYi May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many other places that load accounts and populate read cache besides transaction processor.

  1. programdata account
  2. program owner account
  3. sysvar account
  4. vote account
  5. feature account
  6. buildin account
  7. nounce account
  8. fee account
  9. withdraw/deposit

The following commit lists several places that we populate read cache.
cc2bacc

Some of them should be optimized to skip populating read cache obviously. For example, vote account, and sysvar accounts and fee paying accounts. And some may need more context, i.e. nounce accounts?

I think this change is out of scope of this PR. Probably, we need another project to review all the places that we load accounts and decide whether it is valuable to store the accounts into the cache.

It is unfortunate that we choose the default to put the account into read cache when loading them. Probably, we should have defaulted to not putting account into read cache.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1405 will avoid inserting the accounts into read ache for items 8/9.

@HaoranYi
Copy link
Author

image

image

image

blue: with read_cache completely disabled.
red: with read_cache enabled.

load time with cache (red) is a better. But the replay time without cache (blue) seems to be a little better.

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch 2 times, most recently from 294805d to e4b9e9c Compare May 24, 2024 16:15
@HaoranYi HaoranYi removed the request for review from jeffwashington June 7, 2024 15:11
@alessandrod
Copy link

@HaoranYi ping? are you going to attempt #1408 (comment)?

if message.is_writable(i) {
.load_account_with(key, |_account| {
// cache the account in read-only cache
true
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alessandrod

#1408 (comment) is addressed here.

@HaoranYi
Copy link
Author

@HaoranYi ping? are you going to attempt #1408 (comment)?

Yes, this is addressed.

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch 2 times, most recently from cdc896b to 81bf453 Compare June 17, 2024 15:38
@@ -1983,4 +1995,696 @@ mod tests {
assert_eq!(account.rent_epoch(), 0);
assert_eq!(account.lamports(), 0);
}

#[test]
Copy link
Author

@HaoranYi HaoranYi Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following tests are adapted from corresponding tests in transaction_processor.rs, and moved here, since we get rid of fn filter_program_accounts() from transaction_processor.rs.

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch 2 times, most recently from dae3892 to 67c17ea Compare June 17, 2024 17:10
@HaoranYi
Copy link
Author

HaoranYi commented Jun 17, 2024

Rebase the PR on top of master to pick up recent svm changes.

@alessandrod
Copy link

I'm profiling this now, ideally I'd like to get it in before we branch 2.0

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch from 8391d59 to d977050 Compare June 20, 2024 13:49
@HaoranYi
Copy link
Author

rebase to master to pick up audit fixes.

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch from d977050 to 5764c5f Compare June 21, 2024 14:20
@HaoranYi
Copy link
Author

rebase again.

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch 3 times, most recently from 053166b to 8098948 Compare June 24, 2024 21:54
@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch from 8098948 to 50d4708 Compare July 9, 2024 14:27
@HaoranYi
Copy link
Author

HaoranYi commented Jul 9, 2024

Rebase on master.

@HaoranYi HaoranYi force-pushed the runtime/owner_check2 branch from 50d4708 to 0ac1a61 Compare July 10, 2024 18:43
@brooksprumo brooksprumo removed their request for review October 3, 2024 16:34
@apfitzge
Copy link

apfitzge commented Aug 4, 2025

@HaoranYi are there still plans for this PR?

I noticed the load_account_with callback was never actually something other than just true or false, and changed it here: #7294.

@Lichtso
Copy link

Lichtso commented Aug 4, 2025

#6036 is what we are currently aiming for

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants