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:
parent
d463bcc5f8
commit
9fa3cb659c
|
@ -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]]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue