2018-08-22 07:57:07 -07:00
|
|
|
#![feature(test)]
|
2018-12-08 21:44:20 -08:00
|
|
|
|
2018-08-22 07:57:07 -07:00
|
|
|
extern crate test;
|
2018-07-10 19:33:16 -07:00
|
|
|
|
2019-06-26 18:42:27 -07:00
|
|
|
use crossbeam_channel::unbounded;
|
2019-05-09 11:20:26 -07:00
|
|
|
use log::*;
|
2018-09-26 05:52:13 -07:00
|
|
|
use rand::{thread_rng, Rng};
|
2018-07-10 19:33:16 -07:00
|
|
|
use rayon::prelude::*;
|
2019-08-21 10:23:33 -07:00
|
|
|
use solana_core::banking_stage::{create_test_recorder, BankingStage};
|
|
|
|
use solana_core::cluster_info::ClusterInfo;
|
|
|
|
use solana_core::cluster_info::Node;
|
2019-09-18 12:16:22 -07:00
|
|
|
use solana_core::poh_recorder::WorkingBankEntry;
|
2020-01-13 13:13:52 -08:00
|
|
|
use solana_ledger::blockstore_processor::process_entries;
|
2019-10-18 09:28:51 -07:00
|
|
|
use solana_ledger::entry::{next_hash, Entry};
|
2020-03-21 10:54:40 -07:00
|
|
|
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
2020-01-13 13:13:52 -08:00
|
|
|
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
|
2020-03-17 23:30:23 -07:00
|
|
|
use solana_perf::packet::to_packets_chunked;
|
2019-11-06 10:52:30 -08:00
|
|
|
use solana_perf::test_tx::test_tx;
|
2019-02-18 22:26:22 -08:00
|
|
|
use solana_runtime::bank::Bank;
|
2019-11-08 20:56:57 -08:00
|
|
|
use solana_sdk::genesis_config::GenesisConfig;
|
2019-07-16 18:28:18 -07:00
|
|
|
use solana_sdk::hash::Hash;
|
2020-06-24 13:52:38 -07:00
|
|
|
use solana_sdk::message::Message;
|
2020-10-19 12:23:14 -07:00
|
|
|
use solana_sdk::pubkey;
|
2019-07-16 18:28:18 -07:00
|
|
|
use solana_sdk::signature::Keypair;
|
2019-05-09 11:20:26 -07:00
|
|
|
use solana_sdk::signature::Signature;
|
2020-02-20 13:28:55 -08:00
|
|
|
use solana_sdk::signature::Signer;
|
2019-07-16 18:28:18 -07:00
|
|
|
use solana_sdk::system_instruction;
|
2019-04-03 08:45:57 -07:00
|
|
|
use solana_sdk::system_transaction;
|
2019-07-16 18:28:18 -07:00
|
|
|
use solana_sdk::timing::{duration_as_us, timestamp};
|
|
|
|
use solana_sdk::transaction::Transaction;
|
2019-03-04 20:50:02 -08:00
|
|
|
use std::sync::atomic::Ordering;
|
2019-06-26 18:42:27 -07:00
|
|
|
use std::sync::mpsc::Receiver;
|
2020-04-24 12:03:46 -07:00
|
|
|
use std::sync::Arc;
|
2019-05-09 11:20:26 -07:00
|
|
|
use std::time::{Duration, Instant};
|
2018-08-22 07:57:07 -07:00
|
|
|
use test::Bencher;
|
2018-07-10 19:33:16 -07:00
|
|
|
|
2019-09-18 12:16:22 -07:00
|
|
|
fn check_txs(receiver: &Arc<Receiver<WorkingBankEntry>>, ref_tx_count: usize) {
|
2018-07-10 19:33:16 -07:00
|
|
|
let mut total = 0;
|
2019-06-17 19:04:21 -07:00
|
|
|
let now = Instant::now();
|
2018-08-12 10:04:21 -07:00
|
|
|
loop {
|
2019-09-18 12:16:22 -07:00
|
|
|
if let Ok((_bank, (entry, _tick_height))) = receiver.recv_timeout(Duration::new(1, 0)) {
|
|
|
|
total += entry.transactions.len();
|
2018-09-24 10:40:42 -07:00
|
|
|
}
|
|
|
|
if total >= ref_tx_count {
|
|
|
|
break;
|
2018-07-10 19:33:16 -07:00
|
|
|
}
|
2019-06-17 19:04:21 -07:00
|
|
|
if now.elapsed().as_secs() > 60 {
|
|
|
|
break;
|
|
|
|
}
|
2018-07-10 19:33:16 -07:00
|
|
|
}
|
|
|
|
assert_eq!(total, ref_tx_count);
|
|
|
|
}
|
|
|
|
|
2019-05-20 09:15:00 -07:00
|
|
|
#[bench]
|
|
|
|
fn bench_consume_buffered(bencher: &mut Bencher) {
|
2019-11-08 20:56:57 -08:00
|
|
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(100_000);
|
|
|
|
let bank = Arc::new(Bank::new(&genesis_config));
|
2019-05-20 09:15:00 -07:00
|
|
|
let ledger_path = get_tmp_ledger_path!();
|
2020-10-19 12:23:14 -07:00
|
|
|
let my_pubkey = pubkey::new_rand();
|
2019-05-20 09:15:00 -07:00
|
|
|
{
|
2020-01-13 13:13:52 -08:00
|
|
|
let blockstore = Arc::new(
|
|
|
|
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"),
|
2019-05-20 09:15:00 -07:00
|
|
|
);
|
|
|
|
let (exit, poh_recorder, poh_service, _signal_receiver) =
|
2020-01-13 13:13:52 -08:00
|
|
|
create_test_recorder(&bank, &blockstore, None);
|
2019-05-20 09:15:00 -07:00
|
|
|
|
|
|
|
let tx = test_tx();
|
|
|
|
let len = 4096;
|
|
|
|
let chunk_size = 1024;
|
|
|
|
let batches = to_packets_chunked(&vec![tx; len], chunk_size);
|
|
|
|
let mut packets = vec![];
|
|
|
|
for batch in batches {
|
|
|
|
let batch_len = batch.packets.len();
|
2019-05-20 17:48:42 -07:00
|
|
|
packets.push((batch, vec![0usize; batch_len]));
|
2019-05-20 09:15:00 -07:00
|
|
|
}
|
2020-08-07 11:21:35 -07:00
|
|
|
let (s, _r) = unbounded();
|
2019-05-20 09:15:00 -07:00
|
|
|
// This tests the performance of buffering packets.
|
|
|
|
// If the packet buffers are copied, performance will be poor.
|
|
|
|
bencher.iter(move || {
|
2019-06-28 01:55:24 -07:00
|
|
|
let _ignored = BankingStage::consume_buffered_packets(
|
|
|
|
&my_pubkey,
|
|
|
|
&poh_recorder,
|
|
|
|
&mut packets,
|
|
|
|
10_000,
|
2019-11-20 15:43:10 -08:00
|
|
|
None,
|
2020-08-07 11:21:35 -07:00
|
|
|
&s,
|
2019-06-28 01:55:24 -07:00
|
|
|
);
|
2019-05-20 09:15:00 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
poh_service.join().unwrap();
|
|
|
|
}
|
2020-01-13 13:13:52 -08:00
|
|
|
let _unused = Blockstore::destroy(&ledger_path);
|
2019-05-20 09:15:00 -07:00
|
|
|
}
|
|
|
|
|
2019-07-16 18:28:18 -07:00
|
|
|
fn make_accounts_txs(txes: usize, mint_keypair: &Keypair, hash: Hash) -> Vec<Transaction> {
|
2020-10-19 12:23:14 -07:00
|
|
|
let to_pubkey = pubkey::new_rand();
|
2019-07-16 18:28:18 -07:00
|
|
|
let dummy = system_transaction::transfer(mint_keypair, &to_pubkey, 1, hash);
|
|
|
|
(0..txes)
|
|
|
|
.into_par_iter()
|
|
|
|
.map(|_| {
|
|
|
|
let mut new = dummy.clone();
|
|
|
|
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
2020-10-19 12:23:14 -07:00
|
|
|
new.message.account_keys[0] = pubkey::new_rand();
|
|
|
|
new.message.account_keys[1] = pubkey::new_rand();
|
2019-07-16 18:28:18 -07:00
|
|
|
new.signatures = vec![Signature::new(&sig[0..64])];
|
|
|
|
new
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2020-08-14 11:43:14 -07:00
|
|
|
#[allow(clippy::same_item_push)]
|
2019-07-16 18:28:18 -07:00
|
|
|
fn make_programs_txs(txes: usize, hash: Hash) -> Vec<Transaction> {
|
|
|
|
let progs = 4;
|
|
|
|
(0..txes)
|
|
|
|
.map(|_| {
|
|
|
|
let mut instructions = vec![];
|
|
|
|
let from_key = Keypair::new();
|
|
|
|
for _ in 1..progs {
|
2020-10-19 12:23:14 -07:00
|
|
|
let to_key = pubkey::new_rand();
|
2019-07-16 18:28:18 -07:00
|
|
|
instructions.push(system_instruction::transfer(&from_key.pubkey(), &to_key, 1));
|
|
|
|
}
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(&instructions, Some(&from_key.pubkey()));
|
|
|
|
Transaction::new(&[&from_key], message, hash)
|
2019-07-16 18:28:18 -07:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
enum TransactionType {
|
|
|
|
Accounts,
|
|
|
|
Programs,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
|
2019-05-09 11:20:26 -07:00
|
|
|
solana_logger::setup();
|
2018-12-21 13:55:45 -08:00
|
|
|
let num_threads = BankingStage::num_threads() as usize;
|
2019-07-16 18:28:18 -07:00
|
|
|
// a multiple of packet chunk duplicates to avoid races
|
2019-06-27 01:37:33 -07:00
|
|
|
const CHUNKS: usize = 8;
|
|
|
|
const PACKETS_PER_BATCH: usize = 192;
|
|
|
|
let txes = PACKETS_PER_BATCH * num_threads * CHUNKS;
|
2018-07-10 19:33:16 -07:00
|
|
|
let mint_total = 1_000_000_000_000;
|
2019-11-08 20:56:57 -08:00
|
|
|
let GenesisConfigInfo {
|
|
|
|
mut genesis_config,
|
2019-05-22 20:39:00 -07:00
|
|
|
mint_keypair,
|
|
|
|
..
|
2019-11-08 20:56:57 -08:00
|
|
|
} = create_genesis_config(mint_total);
|
2019-05-09 11:20:26 -07:00
|
|
|
|
|
|
|
// Set a high ticks_per_slot so we don't run out of ticks
|
|
|
|
// during the benchmark
|
2019-11-08 20:56:57 -08:00
|
|
|
genesis_config.ticks_per_slot = 10_000;
|
2018-07-10 19:33:16 -07:00
|
|
|
|
2019-06-26 18:42:27 -07:00
|
|
|
let (verified_sender, verified_receiver) = unbounded();
|
|
|
|
let (vote_sender, vote_receiver) = unbounded();
|
2019-11-08 20:56:57 -08:00
|
|
|
let bank = Arc::new(Bank::new(&genesis_config));
|
2019-07-16 18:28:18 -07:00
|
|
|
|
2019-06-27 01:37:33 -07:00
|
|
|
debug!("threads: {} txs: {}", num_threads, txes);
|
2019-07-16 18:28:18 -07:00
|
|
|
|
|
|
|
let transactions = match tx_type {
|
2019-11-08 20:56:57 -08:00
|
|
|
TransactionType::Accounts => make_accounts_txs(txes, &mint_keypair, genesis_config.hash()),
|
|
|
|
TransactionType::Programs => make_programs_txs(txes, genesis_config.hash()),
|
2019-07-16 18:28:18 -07:00
|
|
|
};
|
|
|
|
|
2018-09-26 05:52:13 -07:00
|
|
|
// fund all the accounts
|
|
|
|
transactions.iter().for_each(|tx| {
|
2019-04-03 08:45:57 -07:00
|
|
|
let fund = system_transaction::transfer(
|
2019-01-24 12:04:04 -08:00
|
|
|
&mint_keypair,
|
2019-03-29 09:05:06 -07:00
|
|
|
&tx.message.account_keys[0],
|
2018-11-05 08:36:22 -08:00
|
|
|
mint_total / txes as u64,
|
2019-11-08 20:56:57 -08:00
|
|
|
genesis_config.hash(),
|
2018-09-26 05:52:13 -07:00
|
|
|
);
|
2018-11-02 14:32:05 -07:00
|
|
|
let x = bank.process_transaction(&fund);
|
2019-01-28 14:52:35 -08:00
|
|
|
x.unwrap();
|
2018-07-10 19:33:16 -07:00
|
|
|
});
|
2018-09-26 05:52:13 -07:00
|
|
|
//sanity check, make sure all the transactions can execute sequentially
|
|
|
|
transactions.iter().for_each(|tx| {
|
|
|
|
let res = bank.process_transaction(&tx);
|
|
|
|
assert!(res.is_ok(), "sanity test transactions");
|
|
|
|
});
|
|
|
|
bank.clear_signatures();
|
|
|
|
//sanity check, make sure all the transactions can execute in parallel
|
|
|
|
let res = bank.process_transactions(&transactions);
|
|
|
|
for r in res {
|
|
|
|
assert!(r.is_ok(), "sanity parallel execution");
|
2018-07-10 19:33:16 -07:00
|
|
|
}
|
2018-09-26 05:52:13 -07:00
|
|
|
bank.clear_signatures();
|
2020-06-08 17:38:14 -07:00
|
|
|
let verified: Vec<_> = to_packets_chunked(&transactions, PACKETS_PER_BATCH);
|
2019-03-29 20:00:36 -07:00
|
|
|
let ledger_path = get_tmp_ledger_path!();
|
|
|
|
{
|
2020-01-13 13:13:52 -08:00
|
|
|
let blockstore = Arc::new(
|
|
|
|
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"),
|
2019-03-29 20:00:36 -07:00
|
|
|
);
|
|
|
|
let (exit, poh_recorder, poh_service, signal_receiver) =
|
2020-01-13 13:13:52 -08:00
|
|
|
create_test_recorder(&bank, &blockstore, None);
|
2019-03-29 20:00:36 -07:00
|
|
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(cluster_info);
|
2020-08-07 11:21:35 -07:00
|
|
|
let (s, _r) = unbounded();
|
2019-04-17 21:07:45 -07:00
|
|
|
let _banking_stage = BankingStage::new(
|
|
|
|
&cluster_info,
|
|
|
|
&poh_recorder,
|
|
|
|
verified_receiver,
|
|
|
|
vote_receiver,
|
2019-11-20 15:43:10 -08:00
|
|
|
None,
|
2020-08-07 11:21:35 -07:00
|
|
|
s,
|
2019-04-17 21:07:45 -07:00
|
|
|
);
|
2019-03-29 20:00:36 -07:00
|
|
|
poh_recorder.lock().unwrap().set_bank(&bank);
|
2018-10-17 16:28:26 -07:00
|
|
|
|
2019-06-17 19:04:21 -07:00
|
|
|
let chunk_len = verified.len() / CHUNKS;
|
2019-03-29 20:00:36 -07:00
|
|
|
let mut start = 0;
|
2019-05-09 11:20:26 -07:00
|
|
|
|
|
|
|
// This is so that the signal_receiver does not go out of scope after the closure.
|
|
|
|
// If it is dropped before poh_service, then poh_service will error when
|
|
|
|
// calling send() on the channel.
|
|
|
|
let signal_receiver = Arc::new(signal_receiver);
|
2020-06-08 17:38:14 -07:00
|
|
|
let signal_receiver2 = signal_receiver;
|
2019-03-29 20:00:36 -07:00
|
|
|
bencher.iter(move || {
|
2019-05-09 11:20:26 -07:00
|
|
|
let now = Instant::now();
|
2019-06-17 19:04:21 -07:00
|
|
|
let mut sent = 0;
|
|
|
|
|
2019-06-27 01:37:33 -07:00
|
|
|
for v in verified[start..start + chunk_len].chunks(chunk_len / num_threads) {
|
|
|
|
debug!(
|
|
|
|
"sending... {}..{} {} v.len: {}",
|
2019-06-17 19:04:21 -07:00
|
|
|
start,
|
|
|
|
start + chunk_len,
|
2019-06-27 01:37:33 -07:00
|
|
|
timestamp(),
|
|
|
|
v.len(),
|
2019-06-17 19:04:21 -07:00
|
|
|
);
|
|
|
|
for xv in v {
|
2019-11-01 14:23:03 -07:00
|
|
|
sent += xv.packets.len();
|
2019-06-17 19:04:21 -07:00
|
|
|
}
|
2019-03-29 20:00:36 -07:00
|
|
|
verified_sender.send(v.to_vec()).unwrap();
|
|
|
|
}
|
2019-06-17 19:04:21 -07:00
|
|
|
check_txs(&signal_receiver2, txes / CHUNKS);
|
|
|
|
|
|
|
|
// This signature clear may not actually clear the signatures
|
2019-07-16 18:28:18 -07:00
|
|
|
// in this chunk, but since we rotate between CHUNKS then
|
2019-06-17 19:04:21 -07:00
|
|
|
// we should clear them by the time we come around again to re-use that chunk.
|
|
|
|
bank.clear_signatures();
|
2019-05-09 11:20:26 -07:00
|
|
|
trace!(
|
2019-06-17 19:04:21 -07:00
|
|
|
"time: {} checked: {} sent: {}",
|
|
|
|
duration_as_us(&now.elapsed()),
|
|
|
|
txes / CHUNKS,
|
|
|
|
sent,
|
2019-05-09 11:20:26 -07:00
|
|
|
);
|
2019-06-17 19:04:21 -07:00
|
|
|
start += chunk_len;
|
2019-03-29 20:00:36 -07:00
|
|
|
start %= verified.len();
|
|
|
|
});
|
2019-04-17 21:07:45 -07:00
|
|
|
drop(vote_sender);
|
2019-03-29 20:00:36 -07:00
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
poh_service.join().unwrap();
|
|
|
|
}
|
2020-01-13 13:13:52 -08:00
|
|
|
let _unused = Blockstore::destroy(&ledger_path);
|
2018-07-10 19:33:16 -07:00
|
|
|
}
|
2018-09-28 16:16:35 -07:00
|
|
|
|
|
|
|
#[bench]
|
2019-07-16 18:28:18 -07:00
|
|
|
fn bench_banking_stage_multi_accounts(bencher: &mut Bencher) {
|
|
|
|
bench_banking(bencher, TransactionType::Accounts);
|
|
|
|
}
|
2019-03-29 20:00:36 -07:00
|
|
|
|
2019-07-16 18:28:18 -07:00
|
|
|
#[bench]
|
|
|
|
fn bench_banking_stage_multi_programs(bencher: &mut Bencher) {
|
|
|
|
bench_banking(bencher, TransactionType::Programs);
|
2018-09-28 16:16:35 -07:00
|
|
|
}
|
2019-08-28 08:38:32 -07:00
|
|
|
|
|
|
|
fn simulate_process_entries(
|
|
|
|
randomize_txs: bool,
|
|
|
|
mint_keypair: &Keypair,
|
|
|
|
mut tx_vector: Vec<Transaction>,
|
2019-11-08 20:56:57 -08:00
|
|
|
genesis_config: &GenesisConfig,
|
2020-06-08 17:38:14 -07:00
|
|
|
keypairs: &[Keypair],
|
2019-08-28 08:38:32 -07:00
|
|
|
initial_lamports: u64,
|
|
|
|
num_accounts: usize,
|
|
|
|
) {
|
2019-11-08 20:56:57 -08:00
|
|
|
let bank = Arc::new(Bank::new(genesis_config));
|
2019-08-28 08:38:32 -07:00
|
|
|
|
|
|
|
for i in 0..(num_accounts / 2) {
|
|
|
|
bank.transfer(initial_lamports, mint_keypair, &keypairs[i * 2].pubkey())
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
for i in (0..num_accounts).step_by(2) {
|
|
|
|
tx_vector.push(system_transaction::transfer(
|
|
|
|
&keypairs[i],
|
|
|
|
&keypairs[i + 1].pubkey(),
|
|
|
|
initial_lamports,
|
|
|
|
bank.last_blockhash(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transfer lamports to each other
|
|
|
|
let entry = Entry {
|
|
|
|
num_hashes: 1,
|
|
|
|
hash: next_hash(&bank.last_blockhash(), 1, &tx_vector),
|
|
|
|
transactions: tx_vector,
|
|
|
|
};
|
2020-07-29 23:17:40 -07:00
|
|
|
process_entries(&bank, &[entry], randomize_txs, None, None).unwrap();
|
2019-08-28 08:38:32 -07:00
|
|
|
}
|
|
|
|
|
2020-08-14 11:43:14 -07:00
|
|
|
#[allow(clippy::same_item_push)]
|
2019-08-28 08:38:32 -07:00
|
|
|
fn bench_process_entries(randomize_txs: bool, bencher: &mut Bencher) {
|
|
|
|
// entropy multiplier should be big enough to provide sufficient entropy
|
|
|
|
// but small enough to not take too much time while executing the test.
|
|
|
|
let entropy_multiplier: usize = 25;
|
|
|
|
let initial_lamports = 100;
|
|
|
|
|
|
|
|
// number of accounts need to be in multiple of 4 for correct
|
|
|
|
// execution of the test.
|
|
|
|
let num_accounts = entropy_multiplier * 4;
|
2019-11-08 20:56:57 -08:00
|
|
|
let GenesisConfigInfo {
|
|
|
|
genesis_config,
|
2019-08-28 08:38:32 -07:00
|
|
|
mint_keypair,
|
|
|
|
..
|
2019-11-08 20:56:57 -08:00
|
|
|
} = create_genesis_config((num_accounts + 1) as u64 * initial_lamports);
|
2019-08-28 08:38:32 -07:00
|
|
|
|
|
|
|
let mut keypairs: Vec<Keypair> = vec![];
|
|
|
|
let tx_vector: Vec<Transaction> = Vec::with_capacity(num_accounts / 2);
|
|
|
|
|
|
|
|
for _ in 0..num_accounts {
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
keypairs.push(keypair);
|
|
|
|
}
|
|
|
|
|
|
|
|
bencher.iter(|| {
|
|
|
|
simulate_process_entries(
|
|
|
|
randomize_txs,
|
|
|
|
&mint_keypair,
|
|
|
|
tx_vector.clone(),
|
2019-11-08 20:56:57 -08:00
|
|
|
&genesis_config,
|
2019-08-28 08:38:32 -07:00
|
|
|
&keypairs,
|
|
|
|
initial_lamports,
|
|
|
|
num_accounts,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_process_entries_without_order_shuffeling(bencher: &mut Bencher) {
|
|
|
|
bench_process_entries(false, bencher);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn bench_process_entries_with_order_shuffeling(bencher: &mut Bencher) {
|
|
|
|
bench_process_entries(true, bencher);
|
|
|
|
}
|