Zero Copy
Learn how to use Anchor's zero-copy deserialization feature to handle large account data in Solana programs.
Overview
Zero-copy deserialization allows programs to read and write account data directly from memory without copying or deserializing it. This is essential for handling large accounts efficiently on Solana.
Why Use Zero-Copy?
Traditional account deserialization (Account<T>) copies data from the account
into a heap-allocated struct. This has limitations:
- Size Constraints: Stack (4KB) and heap (32KB) limits restrict account sizes
- Compute Cost: Deserialization consumes significant compute units
- Memory Overhead: Data is duplicated in memory
Zero-copy (AccountLoader<T>) instead:
- Direct Access: Casts raw account bytes to the struct type (no copying)
- Larger Accounts: Supports accounts up to 10MB (10,485,760 bytes)
- Lower Compute: ~90% reduction in CU usage for large accounts
- In-Place Updates: Modifies account data directly
Performance Comparison
| Account Size | Account<T> | AccountLoader<T> | Improvement |
|---|---|---|---|
| 1 KB | ~8,000 CU | ~1,500 CU | 81% faster |
| 10 KB | ~50,000 CU | ~5,000 CU | 90% faster |
| 100 KB | Too large | ~12,000 CU | Possible |
| 1 MB | Impossible | ~25,000 CU | Possible |
When to Use Zero-Copy
Use zero-copy for:
- Accounts larger than 1KB
- Arrays with many elements (orderbooks, event queues)
- High-frequency operations
- Compute-sensitive programs Use regular Account<T> for:
- Small accounts (< 1KB)
- Dynamic data structures (Vec, String, HashMap)
- Frequently changing schemas
- Simple state that doesn't need optimization
Usage
Zero copy is a deserialization feature that allows programs to read account data directly from memory without copying it. This is particularly useful when working with large accounts.
To use zero-copy add the bytemuck crate to your dependencies. Add the
min_const_generics feature to allow working with arrays of any size in your
zero-copy types.
Define a Zero Copy Account
To define an account type that uses zero-copy, annotate the struct with
#[account(zero_copy)].
The #[account(zero_copy)] attribute automatically implements several traits
required for zero-copy deserialization:
Use AccountLoader for Zero Copy Accounts
To deserialize a zero-copy account, use
AccountLoader<'info, T>,
where T is the zero-copy account type defined with the #[account(zero_copy)]
attribute.
For example:
Initialize a Zero Copy Account
The init constraint can be used with the AccountLoader type to create a
zero-copy account.
The init constraint is limited to allocating a maximum of 10240 bytes due to
CPI limitations. Under the hood, the init constraint makes a CPI call to the
SystemProgram to create the account.
When initializing a zero-copy account for the first time, use
load_init
to get a mutable reference to the account data. The load_init method also sets
the account discriminator.
For accounts that require more than 10240 bytes, use the
zero
constraint instead of init. The zero constraint verifies the account has not
been initialized by checking that its discriminator has not been set.
With the zero constraint, you'll need to first create the account in a
separate instruction by directly calling the System Program. This allows you to
create accounts up to Solana's maximum account size of 10MB (10_485_760 bytes),
bypassing the CPI limitation.
Just as before, use load_init to get a mutable reference to the account data
and set the account discriminator. Since 8 bytes are reserved for the account
discriminator, the maximum data size is 10_485_752 bytes (10MB - 8 bytes).
Update a Zero Copy Account
Use
load_mut
when you need mutable access to update an existing zero-copy account:
Read a Zero Copy Account
Use
load
to only read the account data.
Common Patterns
Nested Zero-Copy Types
For types used within zero-copy accounts, use #[zero_copy] (without account):
Accessor Methods for Byte Arrays
Zero-copy uses #[repr(packed)], making field references unsafe. Use the
#[accessor] attribute for safe getter/setter methods:
Zero-Copy with PDAs
Zero-copy accounts work seamlessly with program-derived addresses:
Separate Types for RPC Parameters
Zero-copy types cannot derive AnchorSerialize/AnchorDeserialize. Use
separate types for instruction parameters:
Common Pitfalls
Forgetting the Account Discriminator
Always add 8 bytes for the account discriminator when calculating space:
Using Dynamic Types
Zero-copy requires all fields to be Copy types:
Using load_init vs load_mut
Use load_init() for first-time initialization (sets discriminator):
Not Validating Array Indices
Always validate array indices to prevent panics:
Real-World Use Cases
Event Queue Pattern
Store large sequences of events efficiently:
Used by: Trading protocols, audit logs, messaging systems
Order Book Pattern
Efficient storage for trading pairs:
Used by: DEXs (Serum, Mango), NFT marketplaces
Examples
The examples below demonstrate two approaches for initializing zero-copy accounts in Anchor:
- Using the
initconstraint to initialize the account in a single instruction - Using the
zeroconstraint to initialize an account with data greater than 10240 bytes
Zero Copy
Initialize Large Account
When initializing an account that requires more than 10,240 bytes of space, you must split the initialization into two steps:
- Create the account in a separate instruction invoking the System Program
- Initialize the account data in your program instruction
Note that the maximum Solana account size is 10MB (10_485_760 bytes), 8 bytes are reserved for the account discriminator.