bench-tps: Add instruction padding program support (#27813)
* bench-tps: Add instruction padding program support * Add ability to customize program id * Improve names and comments
This commit is contained in:
parent
64f64dedea
commit
f3fcbdba29
|
@ -2,6 +2,7 @@ use {
|
|||
crate::{
|
||||
bench_tps_client::*,
|
||||
cli::Config,
|
||||
inline_instruction_padding_program::{create_padded_instruction, InstructionPaddingConfig},
|
||||
perf_utils::{sample_txs, SampleStats},
|
||||
send_batch::*,
|
||||
},
|
||||
|
@ -20,7 +21,7 @@ use {
|
|||
native_token::Sol,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
system_instruction, system_transaction,
|
||||
system_instruction,
|
||||
timing::{duration_as_ms, duration_as_s, duration_as_us, timestamp},
|
||||
transaction::Transaction,
|
||||
},
|
||||
|
@ -94,6 +95,7 @@ struct TransactionChunkGenerator<'a, 'b, T: ?Sized> {
|
|||
chunk_index: usize,
|
||||
reclaim_lamports_back_to_source_account: bool,
|
||||
use_randomized_compute_unit_price: bool,
|
||||
instruction_padding_config: Option<InstructionPaddingConfig>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, T> TransactionChunkGenerator<'a, 'b, T>
|
||||
|
@ -106,6 +108,7 @@ where
|
|||
nonce_keypairs: Option<&'b Vec<Keypair>>,
|
||||
chunk_size: usize,
|
||||
use_randomized_compute_unit_price: bool,
|
||||
instruction_padding_config: Option<InstructionPaddingConfig>,
|
||||
) -> Self {
|
||||
let account_chunks = KeypairChunks::new(gen_keypairs, chunk_size);
|
||||
let nonce_chunks =
|
||||
|
@ -118,6 +121,7 @@ where
|
|||
chunk_index: 0,
|
||||
reclaim_lamports_back_to_source_account: false,
|
||||
use_randomized_compute_unit_price,
|
||||
instruction_padding_config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,6 +147,7 @@ where
|
|||
source_nonce_chunk,
|
||||
dest_nonce_chunk,
|
||||
self.reclaim_lamports_back_to_source_account,
|
||||
&self.instruction_padding_config,
|
||||
)
|
||||
} else {
|
||||
assert!(blockhash.is_some());
|
||||
|
@ -151,6 +156,7 @@ where
|
|||
dest_chunk,
|
||||
self.reclaim_lamports_back_to_source_account,
|
||||
blockhash.unwrap(),
|
||||
&self.instruction_padding_config,
|
||||
self.use_randomized_compute_unit_price,
|
||||
)
|
||||
};
|
||||
|
@ -345,6 +351,7 @@ where
|
|||
target_slots_per_epoch,
|
||||
use_randomized_compute_unit_price,
|
||||
use_durable_nonce,
|
||||
instruction_padding_config,
|
||||
..
|
||||
} = config;
|
||||
|
||||
|
@ -355,6 +362,7 @@ where
|
|||
nonce_keypairs.as_ref(),
|
||||
tx_count,
|
||||
use_randomized_compute_unit_price,
|
||||
instruction_padding_config,
|
||||
);
|
||||
|
||||
let first_tx_count = loop {
|
||||
|
@ -479,6 +487,7 @@ fn generate_system_txs(
|
|||
dest: &VecDeque<&Keypair>,
|
||||
reclaim: bool,
|
||||
blockhash: &Hash,
|
||||
instruction_padding_config: &Option<InstructionPaddingConfig>,
|
||||
use_randomized_compute_unit_price: bool,
|
||||
) -> Vec<TimestampedTransaction> {
|
||||
let pairs: Vec<_> = if !reclaim {
|
||||
|
@ -500,12 +509,13 @@ fn generate_system_txs(
|
|||
.par_iter()
|
||||
.map(|((from, to), compute_unit_price)| {
|
||||
(
|
||||
transfer_with_compute_unit_price(
|
||||
transfer_with_compute_unit_price_and_padding(
|
||||
from,
|
||||
&to.pubkey(),
|
||||
1,
|
||||
*blockhash,
|
||||
**compute_unit_price,
|
||||
instruction_padding_config,
|
||||
Some(**compute_unit_price),
|
||||
),
|
||||
Some(timestamp()),
|
||||
)
|
||||
|
@ -516,7 +526,14 @@ fn generate_system_txs(
|
|||
.par_iter()
|
||||
.map(|(from, to)| {
|
||||
(
|
||||
system_transaction::transfer(from, &to.pubkey(), 1, *blockhash),
|
||||
transfer_with_compute_unit_price_and_padding(
|
||||
from,
|
||||
&to.pubkey(),
|
||||
1,
|
||||
*blockhash,
|
||||
instruction_padding_config,
|
||||
None,
|
||||
),
|
||||
Some(timestamp()),
|
||||
)
|
||||
})
|
||||
|
@ -524,19 +541,34 @@ fn generate_system_txs(
|
|||
}
|
||||
}
|
||||
|
||||
fn transfer_with_compute_unit_price(
|
||||
fn transfer_with_compute_unit_price_and_padding(
|
||||
from_keypair: &Keypair,
|
||||
to: &Pubkey,
|
||||
lamports: u64,
|
||||
recent_blockhash: Hash,
|
||||
compute_unit_price: u64,
|
||||
instruction_padding_config: &Option<InstructionPaddingConfig>,
|
||||
compute_unit_price: Option<u64>,
|
||||
) -> Transaction {
|
||||
let from_pubkey = from_keypair.pubkey();
|
||||
let instructions = vec![
|
||||
system_instruction::transfer(&from_pubkey, to, lamports),
|
||||
ComputeBudgetInstruction::set_compute_unit_limit(TRANSFER_TRANSACTION_COMPUTE_UNIT),
|
||||
ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price),
|
||||
];
|
||||
let transfer_instruction = system_instruction::transfer(&from_pubkey, to, lamports);
|
||||
let instruction = if let Some(instruction_padding_config) = instruction_padding_config {
|
||||
create_padded_instruction(
|
||||
instruction_padding_config.program_id,
|
||||
transfer_instruction,
|
||||
vec![],
|
||||
instruction_padding_config.data_size,
|
||||
)
|
||||
.expect("Could not create padded instruction")
|
||||
} else {
|
||||
transfer_instruction
|
||||
};
|
||||
let mut instructions = vec![instruction];
|
||||
if let Some(compute_unit_price) = compute_unit_price {
|
||||
instructions.extend_from_slice(&[
|
||||
ComputeBudgetInstruction::set_compute_unit_limit(TRANSFER_TRANSACTION_COMPUTE_UNIT),
|
||||
ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price),
|
||||
])
|
||||
}
|
||||
let message = Message::new(&instructions, Some(&from_pubkey));
|
||||
Transaction::new(&[from_keypair], message, recent_blockhash)
|
||||
}
|
||||
|
@ -601,6 +633,37 @@ fn get_nonce_blockhashes<T: 'static + BenchTpsClient + Send + Sync + ?Sized>(
|
|||
blockhashes
|
||||
}
|
||||
|
||||
fn nonced_transfer_with_padding(
|
||||
from_keypair: &Keypair,
|
||||
to: &Pubkey,
|
||||
lamports: u64,
|
||||
nonce_account: &Pubkey,
|
||||
nonce_authority: &Keypair,
|
||||
nonce_hash: Hash,
|
||||
instruction_padding_config: &Option<InstructionPaddingConfig>,
|
||||
) -> Transaction {
|
||||
let from_pubkey = from_keypair.pubkey();
|
||||
let transfer_instruction = system_instruction::transfer(&from_pubkey, to, lamports);
|
||||
let instruction = if let Some(instruction_padding_config) = instruction_padding_config {
|
||||
create_padded_instruction(
|
||||
instruction_padding_config.program_id,
|
||||
transfer_instruction,
|
||||
vec![],
|
||||
instruction_padding_config.data_size,
|
||||
)
|
||||
.expect("Could not create padded instruction")
|
||||
} else {
|
||||
transfer_instruction
|
||||
};
|
||||
let message = Message::new_with_nonce(
|
||||
vec![instruction],
|
||||
Some(&from_pubkey),
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
);
|
||||
Transaction::new(&[from_keypair, nonce_authority], message, nonce_hash)
|
||||
}
|
||||
|
||||
fn generate_nonced_system_txs<T: 'static + BenchTpsClient + Send + Sync + ?Sized>(
|
||||
client: Arc<T>,
|
||||
source: &[&Keypair],
|
||||
|
@ -608,6 +671,7 @@ fn generate_nonced_system_txs<T: 'static + BenchTpsClient + Send + Sync + ?Sized
|
|||
source_nonce: &[&Keypair],
|
||||
dest_nonce: &VecDeque<&Keypair>,
|
||||
reclaim: bool,
|
||||
instruction_padding_config: &Option<InstructionPaddingConfig>,
|
||||
) -> Vec<TimestampedTransaction> {
|
||||
let length = source.len();
|
||||
let mut transactions: Vec<TimestampedTransaction> = Vec::with_capacity(length);
|
||||
|
@ -620,13 +684,14 @@ fn generate_nonced_system_txs<T: 'static + BenchTpsClient + Send + Sync + ?Sized
|
|||
let blockhashes: Vec<Hash> = get_nonce_blockhashes(&client, &pubkeys);
|
||||
for i in 0..length {
|
||||
transactions.push((
|
||||
system_transaction::nonced_transfer(
|
||||
nonced_transfer_with_padding(
|
||||
source[i],
|
||||
&dest[i].pubkey(),
|
||||
1,
|
||||
&source_nonce[i].pubkey(),
|
||||
source[i],
|
||||
blockhashes[i],
|
||||
instruction_padding_config,
|
||||
),
|
||||
None,
|
||||
));
|
||||
|
@ -637,13 +702,14 @@ fn generate_nonced_system_txs<T: 'static + BenchTpsClient + Send + Sync + ?Sized
|
|||
|
||||
for i in 0..length {
|
||||
transactions.push((
|
||||
system_transaction::nonced_transfer(
|
||||
nonced_transfer_with_padding(
|
||||
dest[i],
|
||||
&source[i].pubkey(),
|
||||
1,
|
||||
&dest_nonce[i].pubkey(),
|
||||
dest[i],
|
||||
blockhashes[i],
|
||||
instruction_padding_config,
|
||||
),
|
||||
None,
|
||||
));
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use {
|
||||
crate::inline_instruction_padding_program::{self, InstructionPaddingConfig},
|
||||
clap::{crate_description, crate_name, App, Arg, ArgMatches},
|
||||
solana_clap_utils::input_validators::{is_url, is_url_or_moniker},
|
||||
solana_cli_config::{ConfigInput, CONFIG_FILE},
|
||||
|
@ -56,6 +57,7 @@ pub struct Config {
|
|||
pub tpu_connection_pool_size: usize,
|
||||
pub use_randomized_compute_unit_price: bool,
|
||||
pub use_durable_nonce: bool,
|
||||
pub instruction_padding_config: Option<InstructionPaddingConfig>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -85,6 +87,7 @@ impl Default for Config {
|
|||
tpu_connection_pool_size: DEFAULT_TPU_CONNECTION_POOL_SIZE,
|
||||
use_randomized_compute_unit_price: false,
|
||||
use_durable_nonce: false,
|
||||
instruction_padding_config: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -318,6 +321,20 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
|||
.long("use-durable-nonce")
|
||||
.help("Use durable transaction nonce instead of recent blockhash"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("instruction_padding_program_id")
|
||||
.long("instruction-padding-program-id")
|
||||
.requires("instruction_padding_data_size")
|
||||
.takes_value(true)
|
||||
.value_name("PUBKEY")
|
||||
.help("If instruction data is padded, optionally specify the padding program id to target"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("instruction_padding_data_size")
|
||||
.long("instruction-padding-data-size")
|
||||
.takes_value(true)
|
||||
.help("If set, wraps all instructions in the instruction padding program, with the given amount of padding bytes in instruction data."),
|
||||
)
|
||||
}
|
||||
|
||||
/// Parses a clap `ArgMatches` structure into a `Config`
|
||||
|
@ -456,5 +473,19 @@ pub fn extract_args(matches: &ArgMatches) -> Config {
|
|||
args.use_durable_nonce = true;
|
||||
}
|
||||
|
||||
if let Some(data_size) = matches.value_of("instruction_padding_data_size") {
|
||||
let program_id = matches
|
||||
.value_of("instruction_padding_program_id")
|
||||
.map(|target_str| target_str.parse().unwrap())
|
||||
.unwrap_or(inline_instruction_padding_program::ID);
|
||||
args.instruction_padding_config = Some(InstructionPaddingConfig {
|
||||
program_id,
|
||||
data_size: data_size
|
||||
.to_string()
|
||||
.parse()
|
||||
.expect("Can't parse padded instruction data size"),
|
||||
});
|
||||
}
|
||||
|
||||
args
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
use {
|
||||
solana_sdk::{
|
||||
declare_id,
|
||||
instruction::{AccountMeta, Instruction},
|
||||
program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
syscalls::{MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_DATA_LEN},
|
||||
},
|
||||
std::{convert::TryInto, mem::size_of},
|
||||
};
|
||||
|
||||
pub struct InstructionPaddingConfig {
|
||||
pub program_id: Pubkey,
|
||||
pub data_size: u32,
|
||||
}
|
||||
|
||||
declare_id!("iXpADd6AW1k5FaaXum5qHbSqyd7TtoN6AD7suVa83MF");
|
||||
|
||||
pub fn create_padded_instruction(
|
||||
program_id: Pubkey,
|
||||
instruction: Instruction,
|
||||
padding_accounts: Vec<AccountMeta>,
|
||||
padding_data: u32,
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
// The format for instruction data goes:
|
||||
// * 1 byte for the instruction type
|
||||
// * 4 bytes for the number of accounts required
|
||||
// * 4 bytes for the size of the data required
|
||||
// * the actual instruction data
|
||||
// * additional bytes are all padding
|
||||
let data_size = size_of::<u8>()
|
||||
+ size_of::<u32>()
|
||||
+ size_of::<u32>()
|
||||
+ instruction.data.len()
|
||||
+ padding_data as usize;
|
||||
// crude, but can find a potential issue right away
|
||||
if instruction.data.len() > MAX_CPI_INSTRUCTION_DATA_LEN as usize {
|
||||
return Err(ProgramError::InvalidInstructionData);
|
||||
}
|
||||
let mut data = Vec::with_capacity(data_size);
|
||||
data.push(1);
|
||||
let num_accounts: u32 = instruction
|
||||
.accounts
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| ProgramError::InvalidInstructionData)?;
|
||||
data.extend(num_accounts.to_le_bytes().into_iter());
|
||||
|
||||
let data_size: u32 = instruction
|
||||
.data
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| ProgramError::InvalidInstructionData)?;
|
||||
data.extend(data_size.to_le_bytes().into_iter());
|
||||
data.extend(instruction.data.into_iter());
|
||||
for i in 0..padding_data {
|
||||
data.push((i % u8::MAX as u32) as u8);
|
||||
}
|
||||
|
||||
// The format for account data goes:
|
||||
// * accounts required for the CPI
|
||||
// * program account to call into
|
||||
// * additional accounts may be included as padding or to test loading / locks
|
||||
let num_accounts = instruction.accounts.len() + 1 + padding_accounts.len();
|
||||
if num_accounts > MAX_CPI_ACCOUNT_INFOS {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
}
|
||||
let mut accounts = Vec::with_capacity(num_accounts);
|
||||
accounts.extend(instruction.accounts.into_iter());
|
||||
accounts.push(AccountMeta::new_readonly(instruction.program_id, false));
|
||||
accounts.extend(padding_accounts.into_iter());
|
||||
|
||||
Ok(Instruction {
|
||||
program_id,
|
||||
accounts,
|
||||
data,
|
||||
})
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
pub mod bench;
|
||||
pub mod bench_tps_client;
|
||||
pub mod cli;
|
||||
pub mod inline_instruction_padding_program;
|
||||
pub mod keypairs;
|
||||
mod perf_utils;
|
||||
pub mod send_batch;
|
||||
|
|
|
@ -155,6 +155,7 @@ fn main() {
|
|||
tpu_connection_pool_size,
|
||||
use_randomized_compute_unit_price,
|
||||
use_durable_nonce,
|
||||
instruction_padding_config,
|
||||
..
|
||||
} = &cli_config;
|
||||
|
||||
|
@ -223,6 +224,15 @@ fn main() {
|
|||
*num_nodes,
|
||||
*target_node,
|
||||
);
|
||||
if let Some(instruction_padding_config) = instruction_padding_config {
|
||||
info!(
|
||||
"Checking for existence of instruction padding program: {}",
|
||||
instruction_padding_config.program_id
|
||||
);
|
||||
client
|
||||
.get_account(&instruction_padding_config.program_id)
|
||||
.expect("Instruction padding program must be deployed to this cluster. Deploy the program using `solana program deploy ./bench-tps/tests/fixtures/spl_instruction_padding.so` and pass the resulting program id with `--instruction-padding-program-id`");
|
||||
}
|
||||
let keypairs = get_keypairs(
|
||||
client.clone(),
|
||||
id,
|
||||
|
|
|
@ -5,6 +5,7 @@ use {
|
|||
solana_bench_tps::{
|
||||
bench::{do_bench_tps, generate_and_fund_keypairs},
|
||||
cli::Config,
|
||||
inline_instruction_padding_program::{self, InstructionPaddingConfig},
|
||||
send_batch::generate_durable_nonce_accounts,
|
||||
},
|
||||
solana_core::validator::ValidatorConfig,
|
||||
|
@ -16,11 +17,14 @@ use {
|
|||
solana_rpc::rpc::JsonRpcConfig,
|
||||
solana_rpc_client::rpc_client::RpcClient,
|
||||
solana_sdk::{
|
||||
account::{Account, AccountSharedData},
|
||||
commitment_config::CommitmentConfig,
|
||||
fee_calculator::FeeRateGovernor,
|
||||
rent::Rent,
|
||||
signature::{Keypair, Signer},
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_test_validator::TestValidator,
|
||||
solana_test_validator::TestValidatorGenesis,
|
||||
solana_thin_client::thin_client::ThinClient,
|
||||
solana_tpu_client::{
|
||||
connection_cache::ConnectionCache,
|
||||
|
@ -29,8 +33,22 @@ use {
|
|||
std::{sync::Arc, time::Duration},
|
||||
};
|
||||
|
||||
fn program_account(program_data: &[u8]) -> AccountSharedData {
|
||||
AccountSharedData::from(Account {
|
||||
lamports: Rent::default().minimum_balance(program_data.len()).min(1),
|
||||
data: program_data.to_vec(),
|
||||
owner: solana_sdk::bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn test_bench_tps_local_cluster(config: Config) {
|
||||
let native_instruction_processors = vec![];
|
||||
let additional_accounts = vec![(
|
||||
inline_instruction_padding_program::id(),
|
||||
program_account(include_bytes!("fixtures/spl_instruction_padding.so")),
|
||||
)];
|
||||
|
||||
solana_logger::setup();
|
||||
|
||||
|
@ -54,6 +72,7 @@ fn test_bench_tps_local_cluster(config: Config) {
|
|||
NUM_NODES,
|
||||
),
|
||||
native_instruction_processors,
|
||||
additional_accounts,
|
||||
..ClusterConfig::default()
|
||||
},
|
||||
SocketAddrSpace::Unspecified,
|
||||
|
@ -92,8 +111,20 @@ fn test_bench_tps_test_validator(config: Config) {
|
|||
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
let test_validator = TestValidatorGenesis::default()
|
||||
.fee_rate_governor(FeeRateGovernor::new(0, 0))
|
||||
.rent(Rent {
|
||||
lamports_per_byte_year: 1,
|
||||
exemption_threshold: 1.0,
|
||||
..Rent::default()
|
||||
})
|
||||
.faucet_addr(Some(faucet_addr))
|
||||
.add_program(
|
||||
"spl_instruction_padding",
|
||||
inline_instruction_padding_program::id(),
|
||||
)
|
||||
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
|
||||
.expect("validator start failed");
|
||||
|
||||
let rpc_client = Arc::new(RpcClient::new_with_commitment(
|
||||
test_validator.rpc_url(),
|
||||
|
@ -164,3 +195,31 @@ fn test_bench_tps_tpu_client_nonce() {
|
|||
..Config::default()
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_bench_tps_local_cluster_with_padding() {
|
||||
test_bench_tps_local_cluster(Config {
|
||||
tx_count: 100,
|
||||
duration: Duration::from_secs(10),
|
||||
instruction_padding_config: Some(InstructionPaddingConfig {
|
||||
program_id: inline_instruction_padding_program::id(),
|
||||
data_size: 0,
|
||||
}),
|
||||
..Config::default()
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_bench_tps_tpu_client_with_padding() {
|
||||
test_bench_tps_test_validator(Config {
|
||||
tx_count: 100,
|
||||
duration: Duration::from_secs(10),
|
||||
instruction_padding_config: Some(InstructionPaddingConfig {
|
||||
program_id: inline_instruction_padding_program::id(),
|
||||
data_size: 0,
|
||||
}),
|
||||
..Config::default()
|
||||
});
|
||||
}
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue