Add conflict groups to bench-tps (#29513)

* Add conflict groups to bench-tps

* Add tests for KeyChunks initialization

* Add validation for num-conflict-groups

* Refactor of destination key generation

* Clarify in comments it is slice, not vector

* Use repeat_with in tests
This commit is contained in:
apfitzge 2023-01-19 11:58:43 -08:00 committed by GitHub
parent d463bcc5f8
commit 9fa3cb659c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 4 deletions

View File

@ -74,13 +74,31 @@ struct KeypairChunks<'a> {
} }
impl<'a> KeypairChunks<'a> { impl<'a> KeypairChunks<'a> {
/// Split input vector of keypairs into two sets of chunks of given size /// Split input slice of keypairs into two sets of chunks of given size
fn new(keypairs: &'a [Keypair], chunk_size: usize) -> Self { fn new(keypairs: &'a [Keypair], chunk_size: usize) -> Self {
// Use `chunk_size` as the number of conflict groups per chunk so that each destination key is unique
Self::new_with_conflict_groups(keypairs, chunk_size, chunk_size)
}
/// Split input slice of keypairs into two sets of chunks of given size. Each chunk
/// has a set of source keys and a set of destination keys. There will be
/// `num_conflict_groups_per_chunk` unique destination keys per chunk, so that the
/// destination keys may conflict with each other.
fn new_with_conflict_groups(
keypairs: &'a [Keypair],
chunk_size: usize,
num_conflict_groups_per_chunk: usize,
) -> Self {
let mut source_keypair_chunks: Vec<Vec<&Keypair>> = Vec::new(); let mut source_keypair_chunks: Vec<Vec<&Keypair>> = Vec::new();
let mut dest_keypair_chunks: Vec<VecDeque<&Keypair>> = Vec::new(); let mut dest_keypair_chunks: Vec<VecDeque<&Keypair>> = Vec::new();
for chunk in keypairs.chunks_exact(2 * chunk_size) { for chunk in keypairs.chunks_exact(2 * chunk_size) {
source_keypair_chunks.push(chunk[..chunk_size].iter().collect()); source_keypair_chunks.push(chunk[..chunk_size].iter().collect());
dest_keypair_chunks.push(chunk[chunk_size..].iter().collect()); dest_keypair_chunks.push(
std::iter::repeat(&chunk[chunk_size..chunk_size + num_conflict_groups_per_chunk])
.flatten()
.take(chunk_size)
.collect(),
);
} }
KeypairChunks { KeypairChunks {
source: source_keypair_chunks, source: source_keypair_chunks,
@ -110,8 +128,13 @@ where
chunk_size: usize, chunk_size: usize,
use_randomized_compute_unit_price: bool, use_randomized_compute_unit_price: bool,
instruction_padding_config: Option<InstructionPaddingConfig>, instruction_padding_config: Option<InstructionPaddingConfig>,
num_conflict_groups: Option<usize>,
) -> Self { ) -> Self {
let account_chunks = KeypairChunks::new(gen_keypairs, chunk_size); let account_chunks = if let Some(num_conflict_groups) = num_conflict_groups {
KeypairChunks::new_with_conflict_groups(gen_keypairs, chunk_size, num_conflict_groups)
} else {
KeypairChunks::new(gen_keypairs, chunk_size)
};
let nonce_chunks = let nonce_chunks =
nonce_keypairs.map(|nonce_keypairs| KeypairChunks::new(nonce_keypairs, chunk_size)); nonce_keypairs.map(|nonce_keypairs| KeypairChunks::new(nonce_keypairs, chunk_size));
@ -353,6 +376,7 @@ where
use_randomized_compute_unit_price, use_randomized_compute_unit_price,
use_durable_nonce, use_durable_nonce,
instruction_padding_config, instruction_padding_config,
num_conflict_groups,
.. ..
} = config; } = config;
@ -364,6 +388,7 @@ where
tx_count, tx_count,
use_randomized_compute_unit_price, use_randomized_compute_unit_price,
instruction_padding_config, instruction_padding_config,
num_conflict_groups,
); );
let first_tx_count = loop { let first_tx_count = loop {
@ -1160,4 +1185,60 @@ mod tests {
} }
withdraw_durable_nonce_accounts(client, &authority_keypairs, &nonce_keypairs) withdraw_durable_nonce_accounts(client, &authority_keypairs, &nonce_keypairs)
} }
#[test]
fn test_bench_tps_key_chunks_new() {
let num_keypairs = 16;
let chunk_size = 4;
let keypairs = std::iter::repeat_with(Keypair::new)
.take(num_keypairs)
.collect::<Vec<_>>();
let chunks = KeypairChunks::new(&keypairs, chunk_size);
assert_eq!(
chunks.source[0],
&[&keypairs[0], &keypairs[1], &keypairs[2], &keypairs[3]]
);
assert_eq!(
chunks.dest[0],
&[&keypairs[4], &keypairs[5], &keypairs[6], &keypairs[7]]
);
assert_eq!(
chunks.source[1],
&[&keypairs[8], &keypairs[9], &keypairs[10], &keypairs[11]]
);
assert_eq!(
chunks.dest[1],
&[&keypairs[12], &keypairs[13], &keypairs[14], &keypairs[15]]
);
}
#[test]
fn test_bench_tps_key_chunks_new_with_conflict_groups() {
let num_keypairs = 16;
let chunk_size = 4;
let num_conflict_groups = 2;
let keypairs = std::iter::repeat_with(Keypair::new)
.take(num_keypairs)
.collect::<Vec<_>>();
let chunks =
KeypairChunks::new_with_conflict_groups(&keypairs, chunk_size, num_conflict_groups);
assert_eq!(
chunks.source[0],
&[&keypairs[0], &keypairs[1], &keypairs[2], &keypairs[3]]
);
assert_eq!(
chunks.dest[0],
&[&keypairs[4], &keypairs[5], &keypairs[4], &keypairs[5]]
);
assert_eq!(
chunks.source[1],
&[&keypairs[8], &keypairs[9], &keypairs[10], &keypairs[11]]
);
assert_eq!(
chunks.dest[1],
&[&keypairs[12], &keypairs[13], &keypairs[12], &keypairs[13]]
);
}
} }

View File

@ -1,7 +1,7 @@
use { use {
crate::spl_convert::FromOtherSolana, crate::spl_convert::FromOtherSolana,
clap::{crate_description, crate_name, App, Arg, ArgMatches}, clap::{crate_description, crate_name, App, Arg, ArgMatches},
solana_clap_utils::input_validators::{is_url, is_url_or_moniker}, solana_clap_utils::input_validators::{is_url, is_url_or_moniker, is_within_range},
solana_cli_config::{ConfigInput, CONFIG_FILE}, solana_cli_config::{ConfigInput, CONFIG_FILE},
solana_sdk::{ solana_sdk::{
fee_calculator::FeeRateGovernor, fee_calculator::FeeRateGovernor,
@ -65,6 +65,7 @@ pub struct Config {
pub use_randomized_compute_unit_price: bool, pub use_randomized_compute_unit_price: bool,
pub use_durable_nonce: bool, pub use_durable_nonce: bool,
pub instruction_padding_config: Option<InstructionPaddingConfig>, pub instruction_padding_config: Option<InstructionPaddingConfig>,
pub num_conflict_groups: Option<usize>,
} }
impl Default for Config { impl Default for Config {
@ -95,6 +96,7 @@ impl Default for Config {
use_randomized_compute_unit_price: false, use_randomized_compute_unit_price: false,
use_durable_nonce: false, use_durable_nonce: false,
instruction_padding_config: None, instruction_padding_config: None,
num_conflict_groups: None,
} }
} }
} }
@ -342,6 +344,13 @@ pub fn build_args<'a>(version: &'_ str) -> App<'a, '_> {
.takes_value(true) .takes_value(true)
.help("If set, wraps all instructions in the instruction padding program, with the given amount of padding bytes in instruction data."), .help("If set, wraps all instructions in the instruction padding program, with the given amount of padding bytes in instruction data."),
) )
.arg(
Arg::with_name("num_conflict_groups")
.long("num-conflict-groups")
.takes_value(true)
.validator(|arg| is_within_range(arg, 1, usize::MAX - 1))
.help("The number of unique destination accounts per transactions 'chunk'. Lower values will result in more transaction conflicts.")
)
} }
/// Parses a clap `ArgMatches` structure into a `Config` /// Parses a clap `ArgMatches` structure into a `Config`
@ -494,5 +503,13 @@ pub fn extract_args(matches: &ArgMatches) -> Config {
}); });
} }
if let Some(num_conflict_groups) = matches.value_of("num_conflict_groups") {
args.num_conflict_groups = Some(
num_conflict_groups
.parse()
.expect("Can't parse conflict groups"),
);
}
args args
} }