add subcommand to set randomized compute-unit-price to transactions. (#26891)
* add subcommand to set randomized compute-unit-price to transactions. * add compute-unit-limit to limit additional cost from prioritization. * increase funding if use_randomized_compute_unit_price is enabled.
This commit is contained in:
parent
fc6cee9c06
commit
6c58acf73e
|
@ -4721,6 +4721,7 @@ dependencies = [
|
||||||
"clap 2.33.3",
|
"clap 2.33.3",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"log",
|
"log",
|
||||||
|
"rand 0.7.3",
|
||||||
"rayon",
|
"rayon",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
|
|
@ -12,6 +12,7 @@ publish = false
|
||||||
clap = "2.33.1"
|
clap = "2.33.1"
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
|
rand = "0.7.0"
|
||||||
rayon = "1.5.3"
|
rayon = "1.5.3"
|
||||||
serde_json = "1.0.83"
|
serde_json = "1.0.83"
|
||||||
serde_yaml = "0.8.26"
|
serde_yaml = "0.8.26"
|
||||||
|
|
|
@ -6,18 +6,20 @@ use {
|
||||||
send_batch::*,
|
send_batch::*,
|
||||||
},
|
},
|
||||||
log::*,
|
log::*,
|
||||||
|
rand::distributions::{Distribution, Uniform},
|
||||||
rayon::prelude::*,
|
rayon::prelude::*,
|
||||||
solana_client::nonce_utils,
|
solana_client::nonce_utils,
|
||||||
solana_metrics::{self, datapoint_info},
|
solana_metrics::{self, datapoint_info},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
clock::{DEFAULT_MS_PER_SLOT, DEFAULT_S_PER_SLOT, MAX_PROCESSING_AGE},
|
clock::{DEFAULT_MS_PER_SLOT, DEFAULT_S_PER_SLOT, MAX_PROCESSING_AGE},
|
||||||
|
compute_budget::ComputeBudgetInstruction,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
instruction::{AccountMeta, Instruction},
|
instruction::{AccountMeta, Instruction},
|
||||||
message::Message,
|
message::Message,
|
||||||
native_token::Sol,
|
native_token::Sol,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
system_transaction,
|
system_instruction, system_transaction,
|
||||||
timing::{duration_as_ms, duration_as_s, duration_as_us, timestamp},
|
timing::{duration_as_ms, duration_as_s, duration_as_us, timestamp},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
},
|
},
|
||||||
|
@ -36,6 +38,28 @@ use {
|
||||||
// The point at which transactions become "too old", in seconds.
|
// The point at which transactions become "too old", in seconds.
|
||||||
const MAX_TX_QUEUE_AGE: u64 = (MAX_PROCESSING_AGE as f64 * DEFAULT_S_PER_SLOT) as u64;
|
const MAX_TX_QUEUE_AGE: u64 = (MAX_PROCESSING_AGE as f64 * DEFAULT_S_PER_SLOT) as u64;
|
||||||
|
|
||||||
|
// Add prioritization fee to transfer transactions, when `--use-randomized-compute-unit-price`
|
||||||
|
// is used, compute-unit-price is randomly generated in range of (0..MAX_COMPUTE_UNIT_PRICE).
|
||||||
|
// It also sets transaction's compute-unit to TRANSFER_TRANSACTION_COMPUTE_UNIT. Therefore the
|
||||||
|
// max additional cost is `TRANSFER_TRANSACTION_COMPUTE_UNIT * MAX_COMPUTE_UNIT_PRICE / 1_000_000`
|
||||||
|
const MAX_COMPUTE_UNIT_PRICE: u64 = 50;
|
||||||
|
const TRANSFER_TRANSACTION_COMPUTE_UNIT: u32 = 200;
|
||||||
|
/// calculate maximum possible prioritizatino fee, if `use-randomized-compute-unit-price` is
|
||||||
|
/// enabled, round to nearest lamports.
|
||||||
|
pub fn max_lamporots_for_prioritization(use_randomized_compute_unit_price: bool) -> u64 {
|
||||||
|
if use_randomized_compute_unit_price {
|
||||||
|
const MICRO_LAMPORTS_PER_LAMPORT: u64 = 1_000_000;
|
||||||
|
let micro_lamport_fee: u128 = (MAX_COMPUTE_UNIT_PRICE as u128)
|
||||||
|
.saturating_mul(TRANSFER_TRANSACTION_COMPUTE_UNIT as u128);
|
||||||
|
let fee = micro_lamport_fee
|
||||||
|
.saturating_add(MICRO_LAMPORTS_PER_LAMPORT.saturating_sub(1) as u128)
|
||||||
|
.saturating_div(MICRO_LAMPORTS_PER_LAMPORT as u128);
|
||||||
|
u64::try_from(fee).unwrap_or(u64::MAX)
|
||||||
|
} else {
|
||||||
|
0u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type TimestampedTransaction = (Transaction, Option<u64>);
|
pub type TimestampedTransaction = (Transaction, Option<u64>);
|
||||||
pub type SharedTransactions = Arc<RwLock<VecDeque<Vec<TimestampedTransaction>>>>;
|
pub type SharedTransactions = Arc<RwLock<VecDeque<Vec<TimestampedTransaction>>>>;
|
||||||
|
|
||||||
|
@ -68,6 +92,7 @@ struct TransactionChunkGenerator<'a, 'b, T: ?Sized> {
|
||||||
nonce_chunks: Option<KeypairChunks<'b>>,
|
nonce_chunks: Option<KeypairChunks<'b>>,
|
||||||
chunk_index: usize,
|
chunk_index: usize,
|
||||||
reclaim_lamports_back_to_source_account: bool,
|
reclaim_lamports_back_to_source_account: bool,
|
||||||
|
use_randomized_compute_unit_price: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, T> TransactionChunkGenerator<'a, 'b, T>
|
impl<'a, 'b, T> TransactionChunkGenerator<'a, 'b, T>
|
||||||
|
@ -79,6 +104,7 @@ where
|
||||||
gen_keypairs: &'a [Keypair],
|
gen_keypairs: &'a [Keypair],
|
||||||
nonce_keypairs: Option<&'b Vec<Keypair>>,
|
nonce_keypairs: Option<&'b Vec<Keypair>>,
|
||||||
chunk_size: usize,
|
chunk_size: usize,
|
||||||
|
use_randomized_compute_unit_price: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let account_chunks = KeypairChunks::new(gen_keypairs, chunk_size);
|
let account_chunks = KeypairChunks::new(gen_keypairs, chunk_size);
|
||||||
let nonce_chunks =
|
let nonce_chunks =
|
||||||
|
@ -90,6 +116,7 @@ where
|
||||||
nonce_chunks,
|
nonce_chunks,
|
||||||
chunk_index: 0,
|
chunk_index: 0,
|
||||||
reclaim_lamports_back_to_source_account: false,
|
reclaim_lamports_back_to_source_account: false,
|
||||||
|
use_randomized_compute_unit_price,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +150,7 @@ where
|
||||||
dest_chunk,
|
dest_chunk,
|
||||||
self.reclaim_lamports_back_to_source_account,
|
self.reclaim_lamports_back_to_source_account,
|
||||||
blockhash.unwrap(),
|
blockhash.unwrap(),
|
||||||
|
self.use_randomized_compute_unit_price,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -302,6 +330,7 @@ where
|
||||||
tx_count,
|
tx_count,
|
||||||
sustained,
|
sustained,
|
||||||
target_slots_per_epoch,
|
target_slots_per_epoch,
|
||||||
|
use_randomized_compute_unit_price,
|
||||||
..
|
..
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
|
@ -311,6 +340,7 @@ where
|
||||||
&gen_keypairs,
|
&gen_keypairs,
|
||||||
None, // TODO(klykov): to be added in the follow up PR
|
None, // TODO(klykov): to be added in the follow up PR
|
||||||
tx_count,
|
tx_count,
|
||||||
|
use_randomized_compute_unit_price,
|
||||||
);
|
);
|
||||||
|
|
||||||
let first_tx_count = loop {
|
let first_tx_count = loop {
|
||||||
|
@ -423,6 +453,7 @@ fn generate_system_txs(
|
||||||
dest: &VecDeque<&Keypair>,
|
dest: &VecDeque<&Keypair>,
|
||||||
reclaim: bool,
|
reclaim: bool,
|
||||||
blockhash: &Hash,
|
blockhash: &Hash,
|
||||||
|
use_randomized_compute_unit_price: bool,
|
||||||
) -> Vec<TimestampedTransaction> {
|
) -> Vec<TimestampedTransaction> {
|
||||||
let pairs: Vec<_> = if !reclaim {
|
let pairs: Vec<_> = if !reclaim {
|
||||||
source.iter().zip(dest.iter()).collect()
|
source.iter().zip(dest.iter()).collect()
|
||||||
|
@ -430,6 +461,31 @@ fn generate_system_txs(
|
||||||
dest.iter().zip(source.iter()).collect()
|
dest.iter().zip(source.iter()).collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if use_randomized_compute_unit_price {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let range = Uniform::from(0..MAX_COMPUTE_UNIT_PRICE);
|
||||||
|
let compute_unit_prices: Vec<_> = (0..pairs.len())
|
||||||
|
.map(|_| range.sample(&mut rng) as u64)
|
||||||
|
.collect();
|
||||||
|
let pairs_with_compute_unit_prices: Vec<_> =
|
||||||
|
pairs.iter().zip(compute_unit_prices.iter()).collect();
|
||||||
|
|
||||||
|
pairs_with_compute_unit_prices
|
||||||
|
.par_iter()
|
||||||
|
.map(|((from, to), compute_unit_price)| {
|
||||||
|
(
|
||||||
|
transfer_with_compute_unit_price(
|
||||||
|
from,
|
||||||
|
&to.pubkey(),
|
||||||
|
1,
|
||||||
|
*blockhash,
|
||||||
|
**compute_unit_price,
|
||||||
|
),
|
||||||
|
Some(timestamp()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
pairs
|
pairs
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|(from, to)| {
|
.map(|(from, to)| {
|
||||||
|
@ -440,6 +496,24 @@ fn generate_system_txs(
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transfer_with_compute_unit_price(
|
||||||
|
from_keypair: &Keypair,
|
||||||
|
to: &Pubkey,
|
||||||
|
lamports: u64,
|
||||||
|
recent_blockhash: Hash,
|
||||||
|
compute_unit_price: 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 message = Message::new(&instructions, Some(&from_pubkey));
|
||||||
|
Transaction::new(&[from_keypair], message, recent_blockhash)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_nonce_blockhash<T: 'static + BenchTpsClient + Send + Sync + ?Sized>(
|
fn get_nonce_blockhash<T: 'static + BenchTpsClient + Send + Sync + ?Sized>(
|
||||||
client: Arc<T>,
|
client: Arc<T>,
|
||||||
|
|
|
@ -54,6 +54,7 @@ pub struct Config {
|
||||||
pub external_client_type: ExternalClientType,
|
pub external_client_type: ExternalClientType,
|
||||||
pub use_quic: bool,
|
pub use_quic: bool,
|
||||||
pub tpu_connection_pool_size: usize,
|
pub tpu_connection_pool_size: usize,
|
||||||
|
pub use_randomized_compute_unit_price: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
|
@ -81,6 +82,7 @@ impl Default for Config {
|
||||||
external_client_type: ExternalClientType::default(),
|
external_client_type: ExternalClientType::default(),
|
||||||
use_quic: DEFAULT_TPU_USE_QUIC,
|
use_quic: DEFAULT_TPU_USE_QUIC,
|
||||||
tpu_connection_pool_size: DEFAULT_TPU_CONNECTION_POOL_SIZE,
|
tpu_connection_pool_size: DEFAULT_TPU_CONNECTION_POOL_SIZE,
|
||||||
|
use_randomized_compute_unit_price: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,6 +305,12 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||||
.help("Controls the connection pool size per remote address; only affects ThinClient (default) \
|
.help("Controls the connection pool size per remote address; only affects ThinClient (default) \
|
||||||
or TpuClient sends"),
|
or TpuClient sends"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("use_randomized_compute_unit_price")
|
||||||
|
.long("use-randomized-compute-unit-price")
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Sets random compute-unit-price in range [0..100] to transfer transactions"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a clap `ArgMatches` structure into a `Config`
|
/// Parses a clap `ArgMatches` structure into a `Config`
|
||||||
|
@ -433,5 +441,9 @@ pub fn extract_args(matches: &ArgMatches) -> Config {
|
||||||
.expect("can't parse target slots per epoch");
|
.expect("can't parse target slots per epoch");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matches.is_present("use_randomized_compute_unit_price") {
|
||||||
|
args.use_randomized_compute_unit_price = true;
|
||||||
|
}
|
||||||
|
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use {
|
||||||
clap::value_t,
|
clap::value_t,
|
||||||
log::*,
|
log::*,
|
||||||
solana_bench_tps::{
|
solana_bench_tps::{
|
||||||
bench::do_bench_tps,
|
bench::{do_bench_tps, max_lamporots_for_prioritization},
|
||||||
bench_tps_client::BenchTpsClient,
|
bench_tps_client::BenchTpsClient,
|
||||||
cli::{self, ExternalClientType},
|
cli::{self, ExternalClientType},
|
||||||
keypairs::get_keypairs,
|
keypairs::get_keypairs,
|
||||||
|
@ -153,6 +153,7 @@ fn main() {
|
||||||
external_client_type,
|
external_client_type,
|
||||||
use_quic,
|
use_quic,
|
||||||
tpu_connection_pool_size,
|
tpu_connection_pool_size,
|
||||||
|
use_randomized_compute_unit_price,
|
||||||
..
|
..
|
||||||
} = &cli_config;
|
} = &cli_config;
|
||||||
|
|
||||||
|
@ -161,8 +162,11 @@ fn main() {
|
||||||
info!("Generating {} keypairs", keypair_count);
|
info!("Generating {} keypairs", keypair_count);
|
||||||
let (keypairs, _) = generate_keypairs(id, keypair_count as u64);
|
let (keypairs, _) = generate_keypairs(id, keypair_count as u64);
|
||||||
let num_accounts = keypairs.len() as u64;
|
let num_accounts = keypairs.len() as u64;
|
||||||
let max_fee =
|
let max_fee = FeeRateGovernor::new(*target_lamports_per_signature, 0)
|
||||||
FeeRateGovernor::new(*target_lamports_per_signature, 0).max_lamports_per_signature;
|
.max_lamports_per_signature
|
||||||
|
.saturating_add(max_lamporots_for_prioritization(
|
||||||
|
*use_randomized_compute_unit_price,
|
||||||
|
));
|
||||||
let num_lamports_per_account = (num_accounts - 1 + NUM_SIGNATURES_FOR_TXS * max_fee)
|
let num_lamports_per_account = (num_accounts - 1 + NUM_SIGNATURES_FOR_TXS * max_fee)
|
||||||
/ num_accounts
|
/ num_accounts
|
||||||
+ num_lamports_per_account;
|
+ num_lamports_per_account;
|
||||||
|
|
Loading…
Reference in New Issue