Skip to content

Commit

Permalink
sdk: Add deserialize_into to entrypoint to avoid heap allocations (s…
Browse files Browse the repository at this point in the history
…olana-labs#2618)

* sdk: Add `deserialize_into` to entrypoint

#### Problem

The main entrypoint for Solana programs allocates a vector on the heap
and pushes AccountInfos to it. Allocation is expensive.

#### Summary of changes

Add a new version of `deserialize` called `deserialize_into`, which
expects a slice of `MaybeUninit<AccountInfo>`. The entrypoint can
allocate a maximum array of AccountInfos on the stack and then pass it
in. This new version of the entrypoint saves roughly 30 CUs per unique
account passed to the program.

In an earlier version, I had the new function return the array itself,
but this used slightly more CUs, and didn't work for an array with 64
elements. Let me know how it looks!

* Call instruction processor in non-inlined function

* Add test for max supported accounts in a transaction

* Refactor extracting account infos and instruction data

* Changes required from rebase

* Add clippy allow

* Add panic message if too many accounts provided

* Add `entrypoint_no_alloc!` and revert behavior in entrypoint!

* Use entrypoint_no_alloc! everywhere except noop

* Comment why noop program works the way it does

* Add limit in doc-string

* CHANGELOG: Add entry for entrypoint
  • Loading branch information
joncinque authored Aug 26, 2024
1 parent df892c4 commit 71eb1d8
Show file tree
Hide file tree
Showing 36 changed files with 289 additions and 96 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ Release channels have their own copy of this changelog:
* Banks-client:
* relax functions to use `&self` instead of `&mut self` (#2591)
* Changes
* SDK: removed the `respan` macro. This was marked as "internal use only" and was no longer used internally.
* SDK:
* removed the `respan` macro. This was marked as "internal use only" and was no longer used internally.
* add `entrypoint_no_alloc!`, a more performant program entrypoint that avoids allocations, saving 20-30 CUs per unique account
* `agave-validator`: Update PoH speed check to compare against current hash rate from a Bank (#2447)
* `solana-test-validator`: Add `--clone-feature-set` flag to mimic features from a target cluster (#2480)
* `solana-genesis`: the `--cluster-type` parameter now clones the feature set from the target cluster (#2587)
Expand Down
53 changes: 52 additions & 1 deletion program-test/tests/bpf.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use {
solana_program_test::ProgramTest,
solana_sdk::{
bpf_loader, instruction::Instruction, pubkey::Pubkey, signature::Signer,
bpf_loader, feature_set,
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
signature::Signer,
transaction::Transaction,
},
test_case::test_case,
};

#[tokio::test]
Expand Down Expand Up @@ -39,3 +43,50 @@ async fn test_add_bpf_program() {
.await
.unwrap();
}

#[test_case(64, true, true; "success with 64 accounts and without feature")]
#[test_case(65, true, false; "failure with 65 accounts and without feature")]
#[test_case(128, false, true; "success with 128 accounts and with feature")]
#[test_case(129, false, false; "failure with 129 accounts and with feature")]
#[tokio::test]
async fn test_max_accounts(num_accounts: u8, deactivate_feature: bool, expect_success: bool) {
let program_id = Pubkey::new_unique();

let mut program_test = ProgramTest::default();

program_test.prefer_bpf(true);
program_test.add_program("noop_program", program_id, None);
if deactivate_feature {
program_test.deactivate_feature(feature_set::increase_tx_account_lock_limit::id());
}

let context = program_test.start_with_context().await;

// Subtract 2 to account for the program and fee payer
let num_extra_accounts = num_accounts.checked_sub(2).unwrap();
let account_metas = (0..num_extra_accounts)
.map(|_| AccountMeta::new_readonly(Pubkey::new_unique(), false))
.collect::<Vec<_>>();
let instruction = Instruction::new_with_bytes(program_id, &[], account_metas);
let transaction = Transaction::new_signed_with_payer(
&[instruction],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);

// Invoke the program.
if expect_success {
context
.banks_client
.process_transaction(transaction)
.await
.unwrap();
} else {
context
.banks_client
.process_transaction(transaction)
.await
.unwrap_err();
}
}
2 changes: 1 addition & 1 deletion programs/sbf/rust/call_args/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct OutputData {
many_args_2: i64,
}

solana_program::entrypoint!(entry);
solana_program::entrypoint_no_alloc!(entry);

pub fn entry(_program_id: &Pubkey, _accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
// This code is supposed to occupy stack space. The purpose of this test is to make sure
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/caller_access/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use {
std::convert::TryInto,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/custom_heap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ unsafe impl std::alloc::GlobalAlloc for BumpAllocator {
#[global_allocator]
static A: BumpAllocator = BumpAllocator;

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/dup_accounts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use solana_program::{
pubkey::Pubkey,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/error_handling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl PrintProgramError for MyError {
}
}

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/external_spend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
extern crate solana_program;
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/finalize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use solana_program::{
program::invoke, pubkey::Pubkey,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/get_minimum_delegation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey, stake,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/instruction_introspection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use solana_program::{
sysvar::instructions,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/invoke/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn do_nested_invokes(num_nested_invokes: u64, accounts: &[AccountInfo]) -> Progr
Ok(())
}

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction<'a>(
program_id: &Pubkey,
accounts: &[AccountInfo<'a>],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/invoke_and_error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use solana_program::{
pubkey::Pubkey,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/invoke_and_ok/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use solana_program::{
pubkey::Pubkey,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/invoke_and_return/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use solana_program::{
pubkey::Pubkey,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/invoked/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use {
solana_sbf_rust_invoked_dep::*,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
#[allow(clippy::cognitive_complexity)]
fn process_instruction(
program_id: &Pubkey,
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/log_data/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use solana_program::{
program::set_return_data, pubkey::Pubkey,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
#[allow(clippy::cognitive_complexity)]
fn process_instruction(
_program_id: &Pubkey,
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/mem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use {
solana_sbf_rust_mem_dep::{run_mem_tests, MemOps},
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
Expand Down
2 changes: 2 additions & 0 deletions programs/sbf/rust/noop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
extern crate solana_program;
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};

// This program intentionally uses `entrypoint!` instead of `entrypoint_no_alloc!`
// to handle any number of accounts.
solana_program::entrypoint!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/panic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn custom_panic(info: &core::panic::PanicInfo<'_>) {
extern crate solana_program;
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/rand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
extern crate solana_program;
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/realloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use {
std::{convert::TryInto, mem},
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/realloc_invoke/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use {
std::convert::TryInto,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/remaining_compute_units/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use solana_program::{
account_info::AccountInfo, compute_units::sol_remaining_compute_units,
entrypoint::ProgramResult, msg, pubkey::Pubkey,
};
solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/ro_account_modify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const INSTRUCTION_INVOKE_MODIFY: u8 = 1;
const INSTRUCTION_MODIFY_INVOKE: u8 = 2;
const INSTRUCTION_VERIFY_MODIFIED: u8 = 3;

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/ro_modify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ fn check_preconditions(
Ok(())
}

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/sanity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn return_sstruct() -> SStruct {
SStruct { x: 1, y: 2, z: 3 }
}

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/sibling_inner_instructions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use solana_program::{
pubkey::Pubkey,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/sibling_instructions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use solana_program::{
pubkey::Pubkey,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/simulation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use {

declare_id!("Sim1jD5C35odT8mzctm8BWnjic8xW5xgeb5MbcbErTo");

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);

pub fn process_instruction(
_program_id: &Pubkey,
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/spoof1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use solana_program::{
system_program,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/spoof1_system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/sysvar/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use solana_program::{
},
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/upgradeable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey, sysvar::clock,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
2 changes: 1 addition & 1 deletion programs/sbf/rust/upgraded/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey, sysvar::clock,
};

solana_program::entrypoint!(process_instruction);
solana_program::entrypoint_no_alloc!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down
Loading

0 comments on commit 71eb1d8

Please sign in to comment.