Last active
January 14, 2025 18:26
-
-
Save adrena-orex/d5de51e3c1f409954003bf3272e92651 to your computer and use it in GitHub Desktop.
Track instruction computing unit consumption and size in Rust integration tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Execute instruction and get back instruction size and used compute units | |
| let (ix_size, tx_used_cu) = utils::create_and_execute_adrena_ix( | |
| program_test_ctx, | |
| adrena::accounts::AddCollateralLong { | |
| // ... | |
| } | |
| .to_account_metas(None), | |
| adrena::instruction::AddCollateralLong { params }, | |
| Some(&payer.pubkey()), | |
| &[owner, payer], | |
| None, | |
| None, | |
| ) | |
| .await?; | |
| // Log usage | |
| utils::log_instruction("add_collateral_long", "public", ix_size, tx_used_cu); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Only package specifics to tracking | |
| [dev-dependencies] | |
| colored = "=2.2.0" # Bring some colors to the terminal | |
| ctor = "=0.2.9" # Used to execute a function when tests finishes | |
| once_cell = "=1.20.2" | |
| lazy_static = "=1.5.0" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Globally store information about instruction sizes and compute units | |
| // When tests finish, print the log of instructions with their sizes and compute units | |
| // | |
| #[derive(Debug, Default, Clone)] | |
| pub struct InstructionInfo { | |
| name: String, | |
| category: String, | |
| max_size_bytes: usize, | |
| min_size_bytes: usize, | |
| max_compute_units: u64, | |
| min_compute_units: u64, | |
| } | |
| impl InstructionInfo { | |
| pub fn new(name: &str, category: &str, size_bytes: usize, compute_units: u64) -> Self { | |
| Self { | |
| name: name.to_string(), | |
| category: category.to_string(), | |
| max_size_bytes: size_bytes, | |
| min_size_bytes: size_bytes, | |
| max_compute_units: compute_units, | |
| min_compute_units: compute_units, | |
| } | |
| } | |
| } | |
| // Store info globally | |
| pub static INSTRUCTION_LOG: Lazy<Arc<Mutex<Vec<InstructionInfo>>>> = | |
| Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); | |
| // Function that runs when the process stops | |
| #[dtor] | |
| fn print_instruction_log() { | |
| const MAX_NAME_LENGTH: usize = 40; // Set the max display width for names | |
| let log = INSTRUCTION_LOG.lock().unwrap(); | |
| // Clone and group by category | |
| let mut grouped_by_category: HashMap<String, Vec<InstructionInfo>> = HashMap::new(); | |
| for entry in log.iter() { | |
| grouped_by_category | |
| .entry(entry.category.clone()) | |
| .or_default() | |
| .push(entry.clone()); | |
| } | |
| // Iterate over each category | |
| for (category, mut instructions) in grouped_by_category { | |
| println!("\nCategory: {}", category.bold()); | |
| // Sort each category by max_compute_units in descending order | |
| instructions.sort_by(|a, b| b.max_compute_units.cmp(&a.max_compute_units)); | |
| // Print the header | |
| println!( | |
| "{:<42} {:<29} {:<28}", | |
| "Instruction".bold(), | |
| "Compute Units".bold(), | |
| "TX Byte Size".bold() | |
| ); | |
| println!( | |
| "{:<40} {:<14} {:<14} {:<13} {:<13}", | |
| "", | |
| "Max".bold(), | |
| "Min".bold(), | |
| "Max".bold(), | |
| "Min".bold() | |
| ); | |
| println!("{}", "-".repeat(84)); | |
| // Print each instruction | |
| for info in instructions.iter() { | |
| let truncated_name = if info.name.len() > MAX_NAME_LENGTH { | |
| format!("{}...", &info.name[..MAX_NAME_LENGTH - 3]) | |
| } else { | |
| info.name.clone() | |
| }; | |
| // Apply colors based on thresholds | |
| let max_cu_color = if info.max_compute_units > 1_000_000 { | |
| info.max_compute_units.to_string().red() | |
| } else if info.max_compute_units > 300_000 { | |
| info.max_compute_units.to_string().yellow() | |
| } else { | |
| info.max_compute_units.to_string().green() | |
| }; | |
| let min_cu_color = if info.min_compute_units > 1_000_000 { | |
| info.min_compute_units.to_string().red() | |
| } else if info.min_compute_units > 300_000 { | |
| info.min_compute_units.to_string().yellow() | |
| } else { | |
| info.min_compute_units.to_string().green() | |
| }; | |
| let max_size_color = if info.max_size_bytes > 1000 { | |
| info.max_size_bytes.to_string().red() | |
| } else if info.max_size_bytes > 600 { | |
| info.max_size_bytes.to_string().yellow() | |
| } else { | |
| info.max_size_bytes.to_string().green() | |
| }; | |
| let min_size_color = if info.min_size_bytes > 1000 { | |
| info.min_size_bytes.to_string().red() | |
| } else if info.min_size_bytes > 600 { | |
| info.min_size_bytes.to_string().yellow() | |
| } else { | |
| info.min_size_bytes.to_string().green() | |
| }; | |
| println!( | |
| "{:<40} {:<14} {:<14} {:<13} {:<13}", | |
| truncated_name, max_cu_color, min_cu_color, max_size_color, min_size_color | |
| ); | |
| } | |
| } | |
| } | |
| #[tokio::test] | |
| pub async fn test_integration_part1() { | |
| // Your tests here | |
| tests_suite::position::add_collateral_long().await; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Return the instruction size and the compute units consumed | |
| pub async fn create_and_execute_adrena_ix<T: InstructionData, U: Signers>( | |
| program_test_ctx: &RwLock<ProgramTestContext>, | |
| accounts_meta: Vec<AccountMeta>, | |
| args: T, | |
| payer: Option<&Pubkey>, | |
| signing_keypairs: &U, | |
| pre_ix: Option<solana_sdk::instruction::Instruction>, | |
| post_ix: Option<solana_sdk::instruction::Instruction>, | |
| ) -> std::result::Result<(usize, u64), BanksClientError> { | |
| // Returns main instruction size | |
| let ix = solana_sdk::instruction::Instruction { | |
| program_id: adrena::id(), | |
| accounts: accounts_meta, | |
| data: args.data(), | |
| }; | |
| let ix_size = serialize(&ix).unwrap().len(); | |
| let mut ctx = program_test_ctx.write().await; | |
| let last_blockhash = ctx.last_blockhash; | |
| let banks_client = &mut ctx.banks_client; | |
| let mut instructions: Vec<solana_sdk::instruction::Instruction> = Vec::new(); | |
| // Max allowed compute unit | |
| // https://github.com/solana-labs/solana/blob/5c2d7b6b8ae2afb443facfa5e7f608dbb41d3fdc/program-runtime/src/compute_budget_processor.rs#L19C1-L19C51 | |
| instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(1_400_000)); | |
| if let Some(pre_ix) = pre_ix { | |
| instructions.push(pre_ix); | |
| } | |
| instructions.push(ix.clone()); | |
| if let Some(post_ix) = post_ix { | |
| instructions.push(post_ix); | |
| } | |
| let tx: Transaction = solana_sdk::transaction::Transaction::new_signed_with_payer( | |
| instructions.as_slice(), | |
| payer, | |
| signing_keypairs, | |
| last_blockhash, | |
| ); | |
| let ret = banks_client | |
| .process_transaction_with_metadata(tx) | |
| .await | |
| .unwrap(); | |
| if ret.result.is_err() { | |
| return Err(ret.result.err().unwrap().into()); | |
| } | |
| Ok((ix_size, ret.metadata.unwrap().compute_units_consumed)) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment