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> {
/// 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 {
// 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 dest_keypair_chunks: Vec<VecDeque<&Keypair>> = Vec::new();
for chunk in keypairs.chunks_exact(2 * chunk_size) {
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 {
source: source_keypair_chunks,
@ -110,8 +128,13 @@ where
chunk_size: usize,
use_randomized_compute_unit_price: bool,
instruction_padding_config: Option<InstructionPaddingConfig>,
num_conflict_groups: Option<usize>,
) -> 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 =
nonce_keypairs.map(|nonce_keypairs| KeypairChunks::new(nonce_keypairs, chunk_size));
@ -353,6 +376,7 @@ where
use_randomized_compute_unit_price,
use_durable_nonce,
instruction_padding_config,
num_conflict_groups,
..
} = config;
@ -364,6 +388,7 @@ where
tx_count,
use_randomized_compute_unit_price,
instruction_padding_config,
num_conflict_groups,
);
let first_tx_count = loop {
@ -1160,4 +1185,60 @@ mod tests {
}
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 {
crate::spl_convert::FromOtherSolana,
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_sdk::{
fee_calculator::FeeRateGovernor,
@ -65,6 +65,7 @@ pub struct Config {
pub use_randomized_compute_unit_price: bool,
pub use_durable_nonce: bool,
pub instruction_padding_config: Option<InstructionPaddingConfig>,
pub num_conflict_groups: Option<usize>,
}
impl Default for Config {
@ -95,6 +96,7 @@ impl Default for Config {
use_randomized_compute_unit_price: false,
use_durable_nonce: false,
instruction_padding_config: None,
num_conflict_groups: None,
}
}
}
@ -342,6 +344,13 @@ pub fn build_args<'a>(version: &'_ str) -> App<'a, '_> {
.takes_value(true)
.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`
@ -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
}