Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
There are a few related problems:
Solution
Add a new account type,
LazyAccount
, that conveniently allows deserialization of individual fields on-demand.When to use
This is currently an experimental account type and therefore should only be used when you're running into performance issues.
It's best to use
LazyAccount
when you only need to deserialize some of the fields, especially if the account is read-only.Replacing
Account
(includingBox
ed) withLazyAccount
can improve both stack memory and compute unit usage. However, this is not guaranteed. For example, if you need to deserialize the account fully, usingLazyAccount
will have additional overhead and therefore use slightly more compute units.Currently, using the
mut
constraint eventually results in the whole account getting deserialized, meaning it won't use fewer compute units compared toAccount
. This might get optimized in the future.Features
Account
.load
andload_mut
methods. These methods are non-inlined, meaning that they're less likely to cause stack violation errors.load_<field>
andload_mut_<field>
methods.Example
See the full example here.
Safety
The safety checks are done using the account's discriminator and the account's owner (similar to
Account
). However, you should be extra careful when deserializing individual fields if, for example, the account needs to be migrated. Make sure the previously serialized data always matches the account's type identically.Performance
Memory
All fields (including the inner account type) are heap allocated. It only uses 24 bytes (3x pointer size) of stack memory in total.
It's worth noting that where the account is being deserialized matters. For example, the main place where Anchor programs are likely to hit stack violation errors is a generated function called
try_accounts
(you might be familiar with it from the mangled build logs). This is where the instruction is deserialized and constraints are run. Although having everything at the same place is convenient for using constraints, this also makes it very easy to use the fixed amount of stack space (4096 bytes) SVM allocates just by increasing the number of accounts the instruction has. In SVM, each function has its own stack frame, meaning that it's possible to deserialize more accounts simply by deserializing them inside other functions (rather than intry_accounts
which is already quite heavy).The mentioned stack limitation can be solved using dynamic stack frames, see SIMD-0166.
Compute units
Compute is harder to formulate, as it varies based on the inner account's type. That being said, there are a few things you can do to optimize compute units usage when using
LazyAccount
:u8
,Pubkey
) to dynamic data (e.g.Vec
).Note: This is currently an experimental account, and is therefore behind a feature flag (
lazy-account
).LazyAccount
combined with #2939 results in a massive reduction in stack usage and thus resolves #3060.