1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider};
use sc_cli::{Error, Result};
use sc_client_api::Backend as ClientBackend;
use sp_api::{ApiExt, BlockId, Core, ProvideRuntimeApi};
use sp_blockchain::{
ApplyExtrinsicFailed::Validity,
Error::{ApplyExtrinsicFailed, RuntimeApiError},
};
use sp_runtime::{
traits::{Block as BlockT, Zero},
transaction_validity::{InvalidTransaction, TransactionValidityError},
Digest, DigestItem, OpaqueExtrinsic,
};
use clap::Args;
use log::info;
use serde::Serialize;
use std::{marker::PhantomData, sync::Arc, time::Instant};
use super::ExtrinsicBuilder;
use crate::shared::{StatSelect, Stats};
#[derive(Debug, Default, Serialize, Clone, PartialEq, Args)]
pub struct BenchmarkParams {
#[clap(long, default_value = "10")]
pub warmup: u32,
#[clap(long, default_value = "100")]
pub repeat: u32,
#[clap(long)]
pub max_ext_per_block: Option<u32>,
}
pub(crate) type BenchRecord = Vec<u64>;
pub(crate) struct Benchmark<Block, BA, C> {
client: Arc<C>,
params: BenchmarkParams,
inherent_data: sp_inherents::InherentData,
digest_items: Vec<DigestItem>,
_p: PhantomData<(Block, BA)>,
}
impl<Block, BA, C> Benchmark<Block, BA, C>
where
Block: BlockT<Extrinsic = OpaqueExtrinsic>,
BA: ClientBackend<Block>,
C: BlockBuilderProvider<BA, Block, C> + ProvideRuntimeApi<Block>,
C::Api: ApiExt<Block, StateBackend = BA::State> + BlockBuilderApi<Block>,
{
pub fn new(
client: Arc<C>,
params: BenchmarkParams,
inherent_data: sp_inherents::InherentData,
digest_items: Vec<DigestItem>,
) -> Self {
Self { client, params, inherent_data, digest_items, _p: PhantomData }
}
pub fn bench_block(&self) -> Result<Stats> {
let (block, _) = self.build_block(None)?;
let record = self.measure_block(&block)?;
Stats::new(&record)
}
pub fn bench_extrinsic(&self, ext_builder: &dyn ExtrinsicBuilder) -> Result<Stats> {
let (block, _) = self.build_block(None)?;
let base = self.measure_block(&block)?;
let base_time = Stats::new(&base)?.select(StatSelect::Average);
let (block, num_ext) = self.build_block(Some(ext_builder))?;
let num_ext = num_ext.ok_or_else(|| Error::Input("Block was empty".into()))?;
let mut records = self.measure_block(&block)?;
for r in &mut records {
*r = r.saturating_sub(base_time);
*r = ((*r as f64) / (num_ext as f64)).ceil() as u64;
}
Stats::new(&records)
}
fn build_block(
&self,
ext_builder: Option<&dyn ExtrinsicBuilder>,
) -> Result<(Block, Option<u64>)> {
let mut builder = self.client.new_block(Digest { logs: self.digest_items.clone() })?;
let inherents = builder.create_inherents(self.inherent_data.clone())?;
for inherent in inherents {
builder.push(inherent)?;
}
let ext_builder = if let Some(ext_builder) = ext_builder {
ext_builder
} else {
return Ok((builder.build()?.block, None))
};
info!("Building block, this takes some time...");
let mut num_ext = 0;
for nonce in 0..self.max_ext_per_block() {
let ext = ext_builder.build(nonce)?;
match builder.push(ext.clone()) {
Ok(()) => {},
Err(ApplyExtrinsicFailed(Validity(TransactionValidityError::Invalid(
InvalidTransaction::ExhaustsResources,
)))) => break, Err(e) => return Err(Error::Client(e)),
}
num_ext += 1;
}
if num_ext == 0 {
return Err("A Block must hold at least one extrinsic".into())
}
info!("Extrinsics per block: {}", num_ext);
let block = builder.build()?.block;
Ok((block, Some(num_ext)))
}
fn measure_block(&self, block: &Block) -> Result<BenchRecord> {
let mut record = BenchRecord::new();
let genesis = BlockId::Number(Zero::zero());
info!("Running {} warmups...", self.params.warmup);
for _ in 0..self.params.warmup {
self.client
.runtime_api()
.execute_block(&genesis, block.clone())
.map_err(|e| Error::Client(RuntimeApiError(e)))?;
}
info!("Executing block {} times", self.params.repeat);
for _ in 0..self.params.repeat {
let block = block.clone();
let runtime_api = self.client.runtime_api();
let start = Instant::now();
runtime_api
.execute_block(&genesis, block)
.map_err(|e| Error::Client(RuntimeApiError(e)))?;
let elapsed = start.elapsed().as_nanos();
record.push(elapsed as u64);
}
Ok(record)
}
fn max_ext_per_block(&self) -> u32 {
self.params.max_ext_per_block.unwrap_or(u32::MAX)
}
}