Skip to content

Commit dfd070c

Browse files
committed
Benchmarks for address-lookup-table program instructions
1 parent f07a570 commit dfd070c

File tree

3 files changed

+285
-0
lines changed

3 files changed

+285
-0
lines changed

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

programs/address-lookup-table-tests/Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,21 @@ edition = { workspace = true }
1414
[dev-dependencies]
1515
assert_matches = { workspace = true }
1616
bincode = { workspace = true }
17+
criterion = { workspace = true }
18+
solana-account = { workspace = true }
1719
solana-address-lookup-table-program = { workspace = true }
1820
solana-feature-set = { workspace = true }
21+
solana-instruction = { workspace = true }
22+
solana-program-runtime = { workspace = true }
1923
solana-program-test = { workspace = true }
24+
solana-pubkey = { workspace = true }
2025
solana-sdk = { workspace = true }
26+
solana-sdk-ids = { workspace = true }
27+
solana-system-program = { workspace = true }
28+
29+
[[bench]]
30+
name = "address_lookup_table"
31+
harness = false
2132

2233
[package.metadata.docs.rs]
2334
targets = ["x86_64-unknown-linux-gnu"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
use {
2+
criterion::{black_box, criterion_group, criterion_main, Criterion},
3+
solana_account::{create_account_shared_data_for_test, AccountSharedData},
4+
solana_instruction::AccountMeta,
5+
solana_program_runtime::{
6+
invoke_context::mock_process_instruction, loaded_programs::ProgramCacheEntry,
7+
},
8+
solana_pubkey::Pubkey,
9+
solana_sdk::{
10+
address_lookup_table::instruction::{derive_lookup_table_address, ProgramInstruction},
11+
clock::{Clock, Slot},
12+
hash::Hash,
13+
rent::Rent,
14+
sysvar::{clock, slot_hashes::SlotHashes},
15+
},
16+
solana_sdk_ids::{
17+
address_lookup_table, system_program,
18+
sysvar::{rent, slot_hashes},
19+
},
20+
std::sync::Arc,
21+
};
22+
23+
const ACCOUNT_BALANCE: u64 = u64::MAX / 4;
24+
25+
#[derive(Default)]
26+
struct TestSetup {
27+
authority_address: Pubkey,
28+
payer_address: Pubkey,
29+
lookup_table_address: Pubkey,
30+
bump_seed: u8,
31+
recent_slot: Slot,
32+
transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
33+
instruction_accounts: Vec<AccountMeta>,
34+
instruction_data: Vec<u8>,
35+
}
36+
37+
impl TestSetup {
38+
fn new() -> Self {
39+
let authority_address = Pubkey::new_unique();
40+
let payer_address = Pubkey::new_unique();
41+
let recent_slot = 1;
42+
let mut slot_hashes = SlotHashes::default();
43+
slot_hashes.add(recent_slot, Hash::new_unique());
44+
45+
let (lookup_table_address, bump_seed) =
46+
derive_lookup_table_address(&authority_address, recent_slot);
47+
48+
let transaction_accounts: Vec<(Pubkey, AccountSharedData)> = vec![
49+
// lookup table account is initially owned by system program, it later
50+
// allocate() by native_invoke System program during create_lookup_table
51+
(
52+
lookup_table_address,
53+
AccountSharedData::new(0, 0, &system_program::id()),
54+
),
55+
(
56+
authority_address,
57+
AccountSharedData::new(ACCOUNT_BALANCE, 0, &Pubkey::new_unique()),
58+
),
59+
(
60+
payer_address,
61+
AccountSharedData::new(ACCOUNT_BALANCE, 0, &system_program::id()),
62+
),
63+
(
64+
system_program::id(),
65+
AccountSharedData::new(0, 0, &system_program::id()),
66+
),
67+
(
68+
slot_hashes::id(),
69+
create_account_shared_data_for_test(&slot_hashes),
70+
),
71+
(
72+
rent::id(),
73+
create_account_shared_data_for_test(&Rent::default()),
74+
),
75+
(
76+
clock::id(),
77+
create_account_shared_data_for_test(&Clock::default()),
78+
),
79+
];
80+
81+
Self {
82+
authority_address,
83+
payer_address,
84+
lookup_table_address,
85+
bump_seed,
86+
recent_slot,
87+
transaction_accounts,
88+
..TestSetup::default()
89+
}
90+
}
91+
92+
fn prep_create_lookup_table(&mut self) {
93+
self.instruction_accounts = vec![
94+
AccountMeta::new(self.lookup_table_address, false),
95+
AccountMeta::new_readonly(self.authority_address, false),
96+
AccountMeta::new(self.payer_address, true),
97+
AccountMeta::new_readonly(system_program::id(), false),
98+
];
99+
100+
self.instruction_data = bincode::serialize(&ProgramInstruction::CreateLookupTable {
101+
recent_slot: self.recent_slot,
102+
bump_seed: self.bump_seed,
103+
})
104+
.unwrap();
105+
}
106+
107+
fn exec_create_lookup_table(&mut self) {
108+
self.prep_create_lookup_table();
109+
let accounts = self.run();
110+
111+
let lookup_table_account = accounts[0].clone();
112+
self.transaction_accounts[0] = (self.lookup_table_address, lookup_table_account);
113+
}
114+
115+
fn prep_extend_lookup_table(&mut self) {
116+
self.instruction_accounts = vec![
117+
AccountMeta::new(self.lookup_table_address, false),
118+
AccountMeta::new_readonly(self.authority_address, true),
119+
AccountMeta::new(self.payer_address, true),
120+
AccountMeta::new_readonly(system_program::id(), false),
121+
];
122+
123+
// extend reasonable number of addresses at a time, so bench loop won't
124+
// add more than LOOKUP_TABLE_MAX_ADDRESSES addresses to lookup_table
125+
let new_addresses: Vec<_> = (0..16).map(|_| Pubkey::new_unique()).collect();
126+
self.instruction_data =
127+
bincode::serialize(&ProgramInstruction::ExtendLookupTable { new_addresses }).unwrap();
128+
}
129+
130+
fn exec_extend_lookup_table(&mut self) {
131+
self.prep_extend_lookup_table();
132+
let accounts = self.run();
133+
134+
let lookup_table_account = accounts[0].clone();
135+
self.transaction_accounts[0] = (self.lookup_table_address, lookup_table_account);
136+
}
137+
138+
fn prep_freeze_lookup_table(&mut self) {
139+
self.instruction_accounts = vec![
140+
AccountMeta::new(self.lookup_table_address, false),
141+
AccountMeta::new(self.authority_address, true),
142+
];
143+
144+
self.instruction_data = bincode::serialize(&ProgramInstruction::FreezeLookupTable).unwrap();
145+
}
146+
147+
fn prep_deactivate_lookup_table(&mut self) {
148+
self.instruction_accounts = vec![
149+
AccountMeta::new(self.lookup_table_address, false),
150+
AccountMeta::new(self.authority_address, true),
151+
];
152+
153+
self.instruction_data =
154+
bincode::serialize(&ProgramInstruction::DeactivateLookupTable).unwrap();
155+
}
156+
157+
fn exec_deactivate_lookup_table(&mut self) {
158+
self.prep_deactivate_lookup_table();
159+
let accounts = self.run();
160+
161+
let lookup_table_account = accounts[0].clone();
162+
self.transaction_accounts[0] = (self.lookup_table_address, lookup_table_account);
163+
// advance clock after deactivating
164+
self.transaction_accounts[6] = (
165+
clock::id(),
166+
create_account_shared_data_for_test(&Clock {
167+
slot: self.recent_slot.wrapping_add(1),
168+
..Clock::default()
169+
}),
170+
);
171+
}
172+
173+
fn prep_close_lookup_table(&mut self) {
174+
self.instruction_accounts = vec![
175+
AccountMeta::new(self.lookup_table_address, false),
176+
AccountMeta::new(self.authority_address, true),
177+
AccountMeta::new(self.payer_address, false),
178+
];
179+
180+
self.instruction_data = bincode::serialize(&ProgramInstruction::CloseLookupTable).unwrap();
181+
}
182+
183+
fn run(&self) -> Vec<AccountSharedData> {
184+
mock_process_instruction(
185+
&address_lookup_table::id(),
186+
Vec::new(),
187+
&self.instruction_data,
188+
self.transaction_accounts.clone(),
189+
self.instruction_accounts.clone(),
190+
Ok(()), //expected_result,
191+
solana_address_lookup_table_program::processor::Entrypoint::vm,
192+
|invoke_context| {
193+
// add System to program_cache_for_tx_batch, so it can be native_invoke()
194+
invoke_context.program_cache_for_tx_batch.replenish(
195+
system_program::id(),
196+
Arc::new(ProgramCacheEntry::new_builtin(
197+
0,
198+
0,
199+
solana_system_program::system_processor::Entrypoint::vm,
200+
)),
201+
);
202+
},
203+
|_invoke_context| {},
204+
)
205+
}
206+
}
207+
208+
fn bench_create_lookup_table(c: &mut Criterion) {
209+
let mut test_setup = TestSetup::new();
210+
test_setup.prep_create_lookup_table();
211+
212+
c.bench_function("create_lookup_table", |bencher| {
213+
bencher.iter(|| black_box(test_setup.run()))
214+
});
215+
}
216+
217+
fn bench_extend_lookup_table(c: &mut Criterion) {
218+
let mut test_setup = TestSetup::new();
219+
test_setup.exec_create_lookup_table();
220+
test_setup.prep_extend_lookup_table();
221+
222+
c.bench_function("extend_lookup_table", |bencher| {
223+
bencher.iter(|| black_box(test_setup.run()))
224+
});
225+
}
226+
227+
fn bench_freeze_lookup_table(c: &mut Criterion) {
228+
let mut test_setup = TestSetup::new();
229+
test_setup.exec_create_lookup_table();
230+
test_setup.exec_extend_lookup_table();
231+
test_setup.prep_freeze_lookup_table();
232+
233+
c.bench_function("freeze_lookup_table", |bencher| {
234+
bencher.iter(|| black_box(test_setup.run()))
235+
});
236+
}
237+
238+
fn bench_deactivate_lookup_table(c: &mut Criterion) {
239+
let mut test_setup = TestSetup::new();
240+
test_setup.exec_create_lookup_table();
241+
test_setup.prep_deactivate_lookup_table();
242+
243+
c.bench_function("deactivate_lookup_table", |bencher| {
244+
bencher.iter(|| black_box(test_setup.run()))
245+
});
246+
}
247+
248+
fn bench_close_lookup_table(c: &mut Criterion) {
249+
let mut test_setup = TestSetup::new();
250+
test_setup.exec_create_lookup_table();
251+
test_setup.exec_deactivate_lookup_table();
252+
test_setup.prep_close_lookup_table();
253+
254+
c.bench_function("close_lookup_table", |bencher| {
255+
bencher.iter(|| black_box(test_setup.run()))
256+
});
257+
}
258+
259+
criterion_group!(
260+
benches,
261+
bench_create_lookup_table,
262+
bench_extend_lookup_table,
263+
bench_freeze_lookup_table,
264+
bench_deactivate_lookup_table,
265+
bench_close_lookup_table,
266+
);
267+
criterion_main!(benches);

0 commit comments

Comments
 (0)