Skip to content

Commit 6f15337

Browse files
authored
[benchmark] Add concept of workload (#14198)
## Description This PR introduces the concept of Workload, and wraps all workload related code/data into it. It cleans up the command interface to make it more extensible. ## Test Plan CI --- If your changes are not user-facing and not a breaking change, you can skip the following section. Otherwise, please indicate what changed, and then add to the Release Notes section as highlighted during the release process. ### Type of Change (Check all that apply) - [ ] protocol change - [ ] user-visible impact - [ ] breaking change for a client SDKs - [ ] breaking change for FNs (FN binary must upgrade) - [ ] breaking change for validators or node operators (must upgrade binaries) - [ ] breaking change for on-chain data layout - [ ] necessitate either a data wipe or data migration ### Release notes
1 parent 3edd30e commit 6f15337

File tree

7 files changed

+185
-184
lines changed

7 files changed

+185
-184
lines changed

crates/sui-single-node-benchmark/src/benchmark_context.rs

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
use crate::command::Component;
55
use crate::mock_consensus::ConsensusMode;
66
use crate::single_node::SingleValidator;
7-
use crate::tx_generator::TxGenerator;
7+
use crate::tx_generator::{RootObjectCreateTxGenerator, TxGenerator};
88
use futures::stream::FuturesUnordered;
99
use futures::StreamExt;
1010
use std::collections::HashMap;
1111
use std::path::PathBuf;
1212
use std::sync::Arc;
1313
use sui_types::base_types::{ObjectID, ObjectRef, SuiAddress, SUI_ADDRESS_LENGTH};
1414
use sui_types::crypto::{get_account_key_pair, AccountKeyPair};
15-
use sui_types::effects::TransactionEffects;
15+
use sui_types::effects::{TransactionEffects, TransactionEffectsAPI};
1616
use sui_types::messages_grpc::HandleTransactionResponse;
1717
use sui_types::object::Object;
1818
use sui_types::transaction::{CertifiedTransaction, SignedTransaction, Transaction};
@@ -133,6 +133,50 @@ impl BenchmarkContext {
133133
.await
134134
}
135135

136+
/// In order to benchmark transactions that can read dynamic fields, we must first create
137+
/// a root object with dynamic fields for each account address.
138+
pub(crate) async fn preparing_dynamic_fields(
139+
&mut self,
140+
move_package: ObjectID,
141+
num_dynamic_fields: u64,
142+
) -> HashMap<SuiAddress, ObjectRef> {
143+
if num_dynamic_fields == 0 {
144+
return HashMap::new();
145+
}
146+
147+
info!("Preparing root object with dynamic fields");
148+
let root_object_create_transactions = self
149+
.generate_transactions(Arc::new(RootObjectCreateTxGenerator::new(
150+
move_package,
151+
num_dynamic_fields,
152+
)))
153+
.await;
154+
let results = self
155+
.execute_transactions_immediately(root_object_create_transactions)
156+
.await;
157+
let mut root_objects = HashMap::new();
158+
let mut new_gas_objects = HashMap::new();
159+
for effects in results {
160+
let (owner, root_object) = effects
161+
.created()
162+
.into_iter()
163+
.filter_map(|(oref, owner)| {
164+
owner
165+
.get_address_owner_address()
166+
.ok()
167+
.map(|owner| (owner, oref))
168+
})
169+
.next()
170+
.unwrap();
171+
let gas_object = effects.gas_object().0;
172+
root_objects.insert(owner, root_object);
173+
new_gas_objects.insert(gas_object.0, gas_object);
174+
}
175+
self.refresh_gas_objects(new_gas_objects);
176+
info!("Finished preparing root object with dynamic fields");
177+
root_objects
178+
}
179+
136180
pub(crate) async fn generate_transactions(
137181
&self,
138182
tx_generator: Arc<dyn TxGenerator>,
@@ -199,7 +243,7 @@ impl BenchmarkContext {
199243
results.into_iter().map(|r| r.unwrap()).collect()
200244
}
201245

202-
pub(crate) async fn execute_transactions_immediately(
246+
async fn execute_transactions_immediately(
203247
&self,
204248
transactions: Vec<Transaction>,
205249
) -> Vec<TransactionEffects> {
@@ -214,10 +258,7 @@ impl BenchmarkContext {
214258
results.into_iter().map(|r| r.unwrap()).collect()
215259
}
216260

217-
pub(crate) fn refresh_gas_objects(
218-
&mut self,
219-
mut new_gas_objects: HashMap<ObjectID, ObjectRef>,
220-
) {
261+
fn refresh_gas_objects(&mut self, mut new_gas_objects: HashMap<ObjectID, ObjectRef>) {
221262
info!("Refreshing gas objects");
222263
for gas_objects in self.gas_object_refs.iter_mut() {
223264
let refreshed_gas_objects: Vec<_> = gas_objects
Lines changed: 42 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,58 @@
11
// Copyright (c) Mysten Labs, Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use clap::{Parser, ValueEnum};
4+
use clap::{Parser, Subcommand, ValueEnum};
55
use strum_macros::EnumIter;
66

77
#[derive(Parser)]
8-
#[command(
8+
#[clap(
99
name = "sui-single-node-benchmark",
1010
about = "Benchmark a single validator node",
1111
rename_all = "kebab-case",
1212
author,
1313
version
1414
)]
15-
pub enum Command {
16-
#[command(name = "no-move")]
17-
NoMove {
18-
#[arg(
19-
long,
20-
default_value_t = 500000,
21-
help = "Number of transactions to submit"
22-
)]
23-
tx_count: u64,
24-
#[arg(
25-
long,
26-
default_value = "baseline",
27-
ignore_case = true,
28-
help = "Which component to benchmark"
29-
)]
30-
component: Component,
31-
},
32-
#[command(name = "move")]
15+
pub struct Command {
16+
#[arg(
17+
long,
18+
default_value_t = 500000,
19+
help = "Number of transactions to submit"
20+
)]
21+
pub tx_count: u64,
22+
#[arg(
23+
long,
24+
default_value = "baseline",
25+
ignore_case = true,
26+
help = "Which component to benchmark"
27+
)]
28+
pub component: Component,
29+
#[clap(subcommand)]
30+
pub workload: WorkloadKind,
31+
}
32+
33+
#[derive(Copy, Clone, EnumIter, ValueEnum)]
34+
pub enum Component {
35+
/// Baseline includes the execution and storage layer only.
36+
Baseline,
37+
/// On top of Baseline, this schedules transactions through the transaction manager.
38+
WithTxManager,
39+
/// This goes through the `handle_certificate` entry point on authority_server, which includes
40+
/// certificate verification, transaction manager, as well as a noop consensus layer. The noop
41+
/// consensus layer does absolutely nothing when receiving a transaction in consensus.
42+
ValidatorWithoutConsensus,
43+
/// Similar to ValidatorWithNoopConsensus, but the consensus layer contains a fake consensus
44+
/// protocol that basically sequences transactions in order. It then verify the transaction
45+
/// and store the sequenced transactions into the store. It covers the consensus-independent
46+
/// portion of the code in consensus handler.
47+
ValidatorWithFakeConsensus,
48+
/// Benchmark only validator signing component: `handle_transaction`.
49+
TxnSigning,
50+
}
51+
52+
#[derive(Subcommand)]
53+
pub enum WorkloadKind {
54+
NoMove,
3355
Move {
34-
#[arg(
35-
long,
36-
default_value_t = 500000,
37-
help = "Number of transactions to submit"
38-
)]
39-
tx_count: u64,
40-
#[arg(
41-
long,
42-
default_value = "baseline",
43-
ignore_case = true,
44-
help = "Which component to benchmark"
45-
)]
46-
component: Component,
4756
#[arg(
4857
long,
4958
default_value_t = 2,
@@ -68,22 +77,3 @@ pub enum Command {
6877
computation: u8,
6978
},
7079
}
71-
72-
#[derive(Copy, Clone, EnumIter, ValueEnum)]
73-
pub enum Component {
74-
/// Baseline includes the execution and storage layer only.
75-
Baseline,
76-
/// On top of Baseline, this schedules transactions through the transaction manager.
77-
WithTxManager,
78-
/// This goes through the `handle_certificate` entry point on authority_server, which includes
79-
/// certificate verification, transaction manager, as well as a noop consensus layer. The noop
80-
/// consensus layer does absolutely nothing when receiving a transaction in consensus.
81-
ValidatorWithoutConsensus,
82-
/// Similar to ValidatorWithNoopConsensus, but the consensus layer contains a fake consensus
83-
/// protocol that basically sequences transactions in order. It then verify the transaction
84-
/// and store the sequenced transactions into the store. It covers the consensus-independent
85-
/// portion of the code in consensus handler.
86-
ValidatorWithFakeConsensus,
87-
/// Benchmark only validator signing compoment: `handle_transaction`.
88-
TxnSigning,
89-
}

crates/sui-single-node-benchmark/src/execution.rs

Lines changed: 12 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -3,107 +3,25 @@
33

44
use crate::benchmark_context::BenchmarkContext;
55
use crate::command::Component;
6-
use crate::tx_generator::{MoveTxGenerator, NonMoveTxGenerator, RootObjectCreateTxGenerator};
7-
use std::collections::HashMap;
8-
use std::sync::Arc;
9-
use sui_types::base_types::{ObjectID, ObjectRef, SuiAddress};
6+
use crate::workload::Workload;
107
use sui_types::effects::TransactionEffectsAPI;
118
use sui_types::transaction::Transaction;
129
use tracing::info;
1310

14-
/// Benchmark simple transfer transactions.
15-
/// Each transaction transfers the gas object from an address to itself.
16-
/// The execution does not invoke Move VM, and is considered the cheapest kind of transaction.
17-
///
18-
/// \tx_count: the number of transactions to execute.
19-
/// \component: The component to benchmark.
20-
pub async fn benchmark_simple_transfer(tx_count: u64, component: Component) {
21-
let ctx = BenchmarkContext::new(tx_count, 1, component).await;
22-
let transactions = ctx
23-
.generate_transactions(Arc::new(NonMoveTxGenerator::new()))
24-
.await;
25-
benchmark_transactions(&ctx, transactions, component).await;
26-
}
27-
28-
/// Benchmark Move transactions.
29-
/// Each transaction is a programmable transaction that performs a series of operations specified
30-
/// by the parameters.
31-
///
32-
/// \tx_count: the number of transactions to execute.
33-
/// \component: The component to benchmark.
34-
/// \num_input_objects: the number of address owned input coin objects for each transaction.
35-
/// These objects will be read during input checking, and merged during execution.
36-
/// \num_dynamic_fields: the number of dynamic fields read during execution of each transaction.
37-
/// \computation: Computation intensity for each transaction.
38-
pub async fn benchmark_move_transactions(
39-
tx_count: u64,
40-
component: Component,
41-
num_input_objects: u8,
42-
num_dynamic_fields: u64,
43-
computation: u8,
44-
) {
45-
assert!(
46-
num_input_objects >= 1,
47-
"Each transaction requires at least 1 input object"
48-
);
49-
let mut ctx = BenchmarkContext::new(tx_count, num_input_objects as u64, component).await;
50-
let move_package = ctx.publish_package().await;
51-
let root_objects = preparing_dynamic_fields(&mut ctx, move_package.0, num_dynamic_fields).await;
52-
let transactions = ctx
53-
.generate_transactions(Arc::new(MoveTxGenerator::new(
54-
move_package.0,
55-
num_input_objects,
56-
computation,
57-
root_objects,
58-
)))
59-
.await;
11+
/// Benchmark a given workload on a specified component.
12+
/// The different kinds of workloads and components can be found in command.rs.
13+
pub async fn run_benchmark(workload: Workload, component: Component) {
14+
let mut ctx = BenchmarkContext::new(
15+
workload.num_accounts(),
16+
workload.gas_object_num_per_account(),
17+
component,
18+
)
19+
.await;
20+
let tx_generator = workload.create_tx_generator(&mut ctx).await;
21+
let transactions = ctx.generate_transactions(tx_generator).await;
6022
benchmark_transactions(&ctx, transactions, component).await;
6123
}
6224

63-
/// In order to benchmark transactions that can read dynamic fields, we must first create
64-
/// a root object with dynamic fields for each account address.
65-
async fn preparing_dynamic_fields(
66-
ctx: &mut BenchmarkContext,
67-
move_package: ObjectID,
68-
num_dynamic_fields: u64,
69-
) -> HashMap<SuiAddress, ObjectRef> {
70-
if num_dynamic_fields == 0 {
71-
return HashMap::new();
72-
}
73-
74-
info!("Preparing root object with dynamic fields");
75-
let root_object_create_transactions = ctx
76-
.generate_transactions(Arc::new(RootObjectCreateTxGenerator::new(
77-
move_package,
78-
num_dynamic_fields,
79-
)))
80-
.await;
81-
let results = ctx
82-
.execute_transactions_immediately(root_object_create_transactions)
83-
.await;
84-
let mut root_objects = HashMap::new();
85-
let mut new_gas_objects = HashMap::new();
86-
for effects in results {
87-
let (owner, root_object) = effects
88-
.created()
89-
.into_iter()
90-
.filter_map(|(oref, owner)| {
91-
owner
92-
.get_address_owner_address()
93-
.ok()
94-
.map(|owner| (owner, oref))
95-
})
96-
.next()
97-
.unwrap();
98-
let gas_object = effects.gas_object().0;
99-
root_objects.insert(owner, root_object);
100-
new_gas_objects.insert(gas_object.0, gas_object);
101-
}
102-
ctx.refresh_gas_objects(new_gas_objects);
103-
info!("Finished preparing root object with dynamic fields");
104-
root_objects
105-
}
106-
10725
async fn benchmark_transactions(
10826
ctx: &BenchmarkContext,
10927
transactions: Vec<Transaction>,

crates/sui-single-node-benchmark/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pub mod execution;
77
pub(crate) mod mock_consensus;
88
pub(crate) mod single_node;
99
pub(crate) mod tx_generator;
10+
pub mod workload;

crates/sui-single-node-benchmark/src/main.rs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33

44
use clap::Parser;
55
use sui_single_node_benchmark::command::Command;
6-
use sui_single_node_benchmark::execution::{
7-
benchmark_move_transactions, benchmark_simple_transfer,
8-
};
6+
use sui_single_node_benchmark::execution::run_benchmark;
7+
use sui_single_node_benchmark::workload::Workload;
98

109
#[tokio::main]
1110
async fn main() {
@@ -15,26 +14,5 @@ async fn main() {
1514
.init();
1615

1716
let args = Command::parse();
18-
match args {
19-
Command::NoMove {
20-
tx_count,
21-
component,
22-
} => benchmark_simple_transfer(tx_count, component).await,
23-
Command::Move {
24-
tx_count,
25-
component,
26-
num_input_objects,
27-
num_dynamic_fields,
28-
computation,
29-
} => {
30-
benchmark_move_transactions(
31-
tx_count,
32-
component,
33-
num_input_objects,
34-
num_dynamic_fields,
35-
computation,
36-
)
37-
.await
38-
}
39-
}
17+
run_benchmark(Workload::new(args.tx_count, args.workload), args.component).await;
4018
}

0 commit comments

Comments
 (0)