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:
Tao Zhu 2022-08-11 20:59:17 -05:00 committed by GitHub
parent fc6cee9c06
commit 6c58acf73e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 13 deletions

1
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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,15 +461,58 @@ fn generate_system_txs(
dest.iter().zip(source.iter()).collect() dest.iter().zip(source.iter()).collect()
}; };
pairs if use_randomized_compute_unit_price {
.par_iter() let mut rng = rand::thread_rng();
.map(|(from, to)| { let range = Uniform::from(0..MAX_COMPUTE_UNIT_PRICE);
( let compute_unit_prices: Vec<_> = (0..pairs.len())
system_transaction::transfer(from, &to.pubkey(), 1, *blockhash), .map(|_| range.sample(&mut rng) as u64)
Some(timestamp()), .collect();
) let pairs_with_compute_unit_prices: Vec<_> =
}) pairs.iter().zip(compute_unit_prices.iter()).collect();
.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
.par_iter()
.map(|(from, to)| {
(
system_transaction::transfer(from, &to.pubkey(), 1, *blockhash),
Some(timestamp()),
)
})
.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>(

View File

@ -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
} }

View File

@ -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;