feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,769 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Tools for analyzing the benchmark results.
use crate::BenchmarkResult;
use std::collections::BTreeMap;
pub struct Analysis {
pub base: u128,
pub slopes: Vec<u128>,
pub names: Vec<String>,
pub value_dists: Option<Vec<(Vec<u32>, u128, u128)>>,
pub errors: Option<Vec<u128>>,
pub minimum: u128,
selector: BenchmarkSelector,
}
#[derive(Clone, Copy)]
pub enum BenchmarkSelector {
ExtrinsicTime,
StorageRootTime,
Reads,
Writes,
ProofSize,
}
/// Multiplies the value by 1000 and converts it into an u128.
fn mul_1000_into_u128(value: f64) -> u128 {
// This is slightly more precise than the alternative of `(value * 1000.0) as u128`.
(value as u128)
.saturating_mul(1000)
.saturating_add((value.fract() * 1000.0) as u128)
}
impl BenchmarkSelector {
fn scale_and_cast_weight(self, value: f64, round_up: bool) -> u128 {
if let BenchmarkSelector::ExtrinsicTime = self {
// We add a very slight bias here to counteract the numerical imprecision of the linear
// regression where due to rounding issues it can emit a number like `2999999.999999998`
// which we most certainly always want to round up instead of truncating.
mul_1000_into_u128(value + 0.000_000_005)
} else {
if round_up {
(value + 0.5) as u128
} else {
value as u128
}
}
}
fn scale_weight(self, value: u128) -> u128 {
if let BenchmarkSelector::ExtrinsicTime = self {
value.saturating_mul(1000)
} else {
value
}
}
fn nanos_from_weight(self, value: u128) -> u128 {
if let BenchmarkSelector::ExtrinsicTime = self {
value / 1000
} else {
value
}
}
fn get_value(self, result: &BenchmarkResult) -> u128 {
match self {
BenchmarkSelector::ExtrinsicTime => result.extrinsic_time,
BenchmarkSelector::StorageRootTime => result.storage_root_time,
BenchmarkSelector::Reads => result.reads.into(),
BenchmarkSelector::Writes => result.writes.into(),
BenchmarkSelector::ProofSize => result.proof_size.into(),
}
}
fn get_minimum(self, results: &[BenchmarkResult]) -> u128 {
results
.iter()
.map(|result| self.get_value(result))
.min()
.expect("results cannot be empty")
}
}
#[derive(Debug)]
pub enum AnalysisChoice {
/// Use minimum squares regression for analyzing the benchmarking results.
MinSquares,
/// Use median slopes for analyzing the benchmarking results.
MedianSlopes,
/// Use the maximum values among all other analysis functions for the benchmarking results.
Max,
}
impl Default for AnalysisChoice {
fn default() -> Self {
AnalysisChoice::MinSquares
}
}
impl TryFrom<Option<String>> for AnalysisChoice {
type Error = &'static str;
fn try_from(s: Option<String>) -> Result<Self, Self::Error> {
match s {
None => Ok(AnalysisChoice::default()),
Some(i) => match &i[..] {
"min-squares" | "min_squares" => Ok(AnalysisChoice::MinSquares),
"median-slopes" | "median_slopes" => Ok(AnalysisChoice::MedianSlopes),
"max" => Ok(AnalysisChoice::Max),
_ => Err("invalid analysis string"),
},
}
}
}
fn raw_linear_regression(
xs: &[f64],
ys: &[f64],
x_vars: usize,
with_intercept: bool,
) -> Option<(f64, Vec<f64>, Vec<f64>)> {
let mut data: Vec<f64> = Vec::new();
// Here we build a raw matrix of linear equations for the `linregress` crate to solve for us
// and build a linear regression model around it.
//
// Each row of the matrix contains as the first column the actual value which we want
// the model to predict for us (the `y`), and the rest of the columns contain the input
// parameters on which the model will base its predictions on (the `xs`).
//
// In machine learning terms this is essentially the training data for the model.
//
// As a special case the very first input parameter represents the constant factor
// of the linear equation: the so called "intercept value". Since it's supposed to
// be constant we can just put a dummy input parameter of either a `1` (in case we want it)
// or a `0` (in case we do not).
for (&y, xs) in ys.iter().zip(xs.chunks_exact(x_vars)) {
data.push(y);
if with_intercept {
data.push(1.0);
} else {
data.push(0.0);
}
data.extend(xs);
}
let model = linregress::fit_low_level_regression_model(&data, ys.len(), x_vars + 2).ok()?;
Some((model.parameters()[0], model.parameters()[1..].to_vec(), model.se().to_vec()))
}
fn linear_regression(
xs: Vec<f64>,
mut ys: Vec<f64>,
x_vars: usize,
) -> Option<(f64, Vec<f64>, Vec<f64>)> {
let (intercept, params, errors) = raw_linear_regression(&xs, &ys, x_vars, true)?;
if intercept >= -0.0001 {
// The intercept is positive, or is effectively zero.
return Some((intercept, params, errors[1..].to_vec()));
}
// The intercept is negative.
// The weights must be always positive, so we can't have that.
let mut min = ys[0];
for &value in &ys {
if value < min {
min = value;
}
}
for value in &mut ys {
*value -= min;
}
let (intercept, params, errors) = raw_linear_regression(&xs, &ys, x_vars, false)?;
assert!(intercept.abs() <= 0.0001);
Some((min, params, errors[1..].to_vec()))
}
impl Analysis {
// Useful for when there are no components, and we just need an median value of the benchmark
// results. Note: We choose the median value because it is more robust to outliers.
fn median_value(r: &Vec<BenchmarkResult>, selector: BenchmarkSelector) -> Option<Self> {
if r.is_empty() {
return None;
}
let mut values: Vec<u128> = r
.iter()
.map(|result| match selector {
BenchmarkSelector::ExtrinsicTime => result.extrinsic_time,
BenchmarkSelector::StorageRootTime => result.storage_root_time,
BenchmarkSelector::Reads => result.reads.into(),
BenchmarkSelector::Writes => result.writes.into(),
BenchmarkSelector::ProofSize => result.proof_size.into(),
})
.collect();
values.sort();
let mid = values.len() / 2;
Some(Self {
base: selector.scale_weight(values[mid]),
slopes: Vec::new(),
names: Vec::new(),
value_dists: None,
errors: None,
minimum: selector.get_minimum(&r),
selector,
})
}
pub fn median_slopes(r: &Vec<BenchmarkResult>, selector: BenchmarkSelector) -> Option<Self> {
if r[0].components.is_empty() {
return Self::median_value(r, selector);
}
let results = r[0]
.components
.iter()
.enumerate()
.map(|(i, &(param, _))| {
let mut counted = BTreeMap::<Vec<u32>, usize>::new();
for result in r.iter() {
let mut p = result.components.iter().map(|x| x.1).collect::<Vec<_>>();
p[i] = 0;
*counted.entry(p).or_default() += 1;
}
let others: Vec<u32> =
counted.iter().max_by_key(|i| i.1).expect("r is not empty; qed").0.clone();
let values = r
.iter()
.filter(|v| {
v.components
.iter()
.map(|x| x.1)
.zip(others.iter())
.enumerate()
.all(|(j, (v1, v2))| j == i || v1 == *v2)
})
.map(|result| {
// Extract the data we are interested in analyzing
let data = match selector {
BenchmarkSelector::ExtrinsicTime => result.extrinsic_time,
BenchmarkSelector::StorageRootTime => result.storage_root_time,
BenchmarkSelector::Reads => result.reads.into(),
BenchmarkSelector::Writes => result.writes.into(),
BenchmarkSelector::ProofSize => result.proof_size.into(),
};
(result.components[i].1, data)
})
.collect::<Vec<_>>();
(format!("{:?}", param), i, others, values)
})
.collect::<Vec<_>>();
let models = results
.iter()
.map(|(_, _, _, ref values)| {
let mut slopes = vec![];
for (i, &(x1, y1)) in values.iter().enumerate() {
for &(x2, y2) in values.iter().skip(i + 1) {
if x1 != x2 {
slopes.push((y1 as f64 - y2 as f64) / (x1 as f64 - x2 as f64));
}
}
}
slopes.sort_by(|a, b| a.partial_cmp(b).expect("values well defined; qed"));
let slope = slopes[slopes.len() / 2];
let mut offsets = vec![];
for &(x, y) in values.iter() {
offsets.push(y as f64 - slope * x as f64);
}
offsets.sort_by(|a, b| a.partial_cmp(b).expect("values well defined; qed"));
let offset = offsets[offsets.len() / 2];
(offset, slope)
})
.collect::<Vec<_>>();
let models = models
.iter()
.zip(results.iter())
.map(|((offset, slope), (_, i, others, _))| {
let over = others
.iter()
.enumerate()
.filter(|(j, _)| j != i)
.map(|(j, v)| models[j].1 * *v as f64)
.fold(0f64, |acc, i| acc + i);
(*offset - over, *slope)
})
.collect::<Vec<_>>();
let base = selector.scale_and_cast_weight(models[0].0.max(0f64), false);
let slopes = models
.iter()
.map(|x| selector.scale_and_cast_weight(x.1.max(0f64), false))
.collect::<Vec<_>>();
Some(Self {
base,
slopes,
names: results.into_iter().map(|x| x.0).collect::<Vec<_>>(),
value_dists: None,
errors: None,
minimum: selector.get_minimum(&r),
selector,
})
}
pub fn min_squares_iqr(r: &Vec<BenchmarkResult>, selector: BenchmarkSelector) -> Option<Self> {
if r[0].components.is_empty() || r.len() <= 2 {
return Self::median_value(r, selector);
}
let mut results = BTreeMap::<Vec<u32>, Vec<u128>>::new();
for result in r.iter() {
let p = result.components.iter().map(|x| x.1).collect::<Vec<_>>();
results.entry(p).or_default().push(match selector {
BenchmarkSelector::ExtrinsicTime => result.extrinsic_time,
BenchmarkSelector::StorageRootTime => result.storage_root_time,
BenchmarkSelector::Reads => result.reads.into(),
BenchmarkSelector::Writes => result.writes.into(),
BenchmarkSelector::ProofSize => result.proof_size.into(),
})
}
for (_, rs) in results.iter_mut() {
rs.sort();
let ql = rs.len() / 4;
*rs = rs[ql..rs.len() - ql].to_vec();
}
let names = r[0].components.iter().map(|x| format!("{:?}", x.0)).collect::<Vec<_>>();
let value_dists = results
.iter()
.map(|(p, vs)| {
// Avoid divide by zero
if vs.is_empty() {
return (p.clone(), 0, 0);
}
let total = vs.iter().fold(0u128, |acc, v| acc + *v);
let mean = total / vs.len() as u128;
let sum_sq_diff = vs.iter().fold(0u128, |acc, v| {
let d = mean.max(*v) - mean.min(*v);
acc + d * d
});
let stddev = (sum_sq_diff as f64 / vs.len() as f64).sqrt() as u128;
(p.clone(), mean, stddev)
})
.collect::<Vec<_>>();
let mut ys: Vec<f64> = Vec::new();
let mut xs: Vec<f64> = Vec::new();
for result in results {
let x: Vec<f64> = result.0.iter().map(|value| *value as f64).collect();
for y in result.1 {
xs.extend(x.iter().copied());
ys.push(y as f64);
}
}
let (intercept, slopes, errors) = linear_regression(xs, ys, r[0].components.len())?;
Some(Self {
base: selector.scale_and_cast_weight(intercept, true),
slopes: slopes
.into_iter()
.map(|value| selector.scale_and_cast_weight(value, true))
.collect(),
names,
value_dists: Some(value_dists),
errors: Some(
errors
.into_iter()
.map(|value| selector.scale_and_cast_weight(value, false))
.collect(),
),
minimum: selector.get_minimum(&r),
selector,
})
}
pub fn max(r: &Vec<BenchmarkResult>, selector: BenchmarkSelector) -> Option<Self> {
let median_slopes = Self::median_slopes(r, selector);
let min_squares = Self::min_squares_iqr(r, selector);
if median_slopes.is_none() || min_squares.is_none() {
return None;
}
let median_slopes = median_slopes.unwrap();
let min_squares = min_squares.unwrap();
let base = median_slopes.base.max(min_squares.base);
let slopes = median_slopes
.slopes
.into_iter()
.zip(min_squares.slopes.into_iter())
.map(|(a, b): (u128, u128)| a.max(b))
.collect::<Vec<u128>>();
// components should always be in the same order
median_slopes
.names
.iter()
.zip(min_squares.names.iter())
.for_each(|(a, b)| assert!(a == b, "benchmark results not in the same order"));
let names = median_slopes.names;
let value_dists = min_squares.value_dists;
let errors = min_squares.errors;
let minimum = selector.get_minimum(&r);
Some(Self { base, slopes, names, value_dists, errors, selector, minimum })
}
}
fn ms(mut nanos: u128) -> String {
let mut x = 100_000u128;
while x > 1 {
if nanos > x * 1_000 {
nanos = nanos / x * x;
break;
}
x /= 10;
}
format!("{}", nanos as f64 / 1_000f64)
}
impl std::fmt::Display for Analysis {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some(ref value_dists) = self.value_dists {
writeln!(f, "\nData points distribution:")?;
writeln!(
f,
"{} mean µs sigma µs %",
self.names.iter().map(|p| format!("{:>5}", p)).collect::<Vec<_>>().join(" ")
)?;
for (param_values, mean, sigma) in value_dists.iter() {
if *mean == 0 {
writeln!(
f,
"{} {:>8} {:>8} {:>3}.{}%",
param_values
.iter()
.map(|v| format!("{:>5}", v))
.collect::<Vec<_>>()
.join(" "),
ms(*mean),
ms(*sigma),
"?",
"?"
)?;
} else {
writeln!(
f,
"{} {:>8} {:>8} {:>3}.{}%",
param_values
.iter()
.map(|v| format!("{:>5}", v))
.collect::<Vec<_>>()
.join(" "),
ms(*mean),
ms(*sigma),
(sigma * 100 / mean),
(sigma * 1000 / mean % 10)
)?;
}
}
}
if let Some(ref errors) = self.errors {
writeln!(f, "\nQuality and confidence:")?;
writeln!(f, "param error")?;
for (p, se) in self.names.iter().zip(errors.iter()) {
writeln!(f, "{} {:>8}", p, ms(self.selector.nanos_from_weight(*se)))?;
}
}
writeln!(f, "\nModel:")?;
writeln!(f, "Time ~= {:>8}", ms(self.selector.nanos_from_weight(self.base)))?;
for (&t, n) in self.slopes.iter().zip(self.names.iter()) {
writeln!(f, " + {} {:>8}", n, ms(self.selector.nanos_from_weight(t)))?;
}
writeln!(f, " µs")
}
}
impl std::fmt::Debug for Analysis {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.base)?;
for (&m, n) in self.slopes.iter().zip(self.names.iter()) {
write!(f, " + ({} * {})", m, n)?;
}
write!(f, "")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::BenchmarkParameter;
fn benchmark_result(
components: Vec<(BenchmarkParameter, u32)>,
extrinsic_time: u128,
storage_root_time: u128,
reads: u32,
writes: u32,
) -> BenchmarkResult {
BenchmarkResult {
components,
extrinsic_time,
storage_root_time,
reads,
repeat_reads: 0,
writes,
repeat_writes: 0,
proof_size: 0,
keys: vec![],
}
}
#[test]
fn test_linear_regression() {
let ys = vec![
3797981.0,
37857779.0,
70569402.0,
104004114.0,
137233924.0,
169826237.0,
203521133.0,
237552333.0,
271082065.0,
305554637.0,
335218347.0,
371759065.0,
405086197.0,
438353555.0,
472891417.0,
505339532.0,
527784778.0,
562590596.0,
635291991.0,
673027090.0,
708119408.0,
];
let xs = vec![
0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0,
16.0, 17.0, 18.0, 19.0, 20.0,
];
let (intercept, params, errors) = raw_linear_regression(&xs, &ys, 1, true).unwrap();
assert_eq!(intercept as i64, -2712997);
assert_eq!(params.len(), 1);
assert_eq!(params[0] as i64, 34444926);
assert_eq!(errors.len(), 2);
assert_eq!(errors[0] as i64, 4805766);
assert_eq!(errors[1] as i64, 411084);
let (intercept, params, errors) = linear_regression(xs, ys, 1).unwrap();
assert_eq!(intercept as i64, 3797981);
assert_eq!(params.len(), 1);
assert_eq!(params[0] as i64, 33968513);
assert_eq!(errors.len(), 1);
assert_eq!(errors[0] as i64, 217331);
}
#[test]
fn analysis_median_slopes_should_work() {
let data = vec![
benchmark_result(
vec![(BenchmarkParameter::n, 1), (BenchmarkParameter::m, 5)],
11_500_000,
0,
3,
10,
),
benchmark_result(
vec![(BenchmarkParameter::n, 2), (BenchmarkParameter::m, 5)],
12_500_000,
0,
4,
10,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 5)],
13_500_000,
0,
5,
10,
),
benchmark_result(
vec![(BenchmarkParameter::n, 4), (BenchmarkParameter::m, 5)],
14_500_000,
0,
6,
10,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 1)],
13_100_000,
0,
5,
2,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 3)],
13_300_000,
0,
5,
6,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 7)],
13_700_000,
0,
5,
14,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 10)],
14_000_000,
0,
5,
20,
),
];
let extrinsic_time =
Analysis::median_slopes(&data, BenchmarkSelector::ExtrinsicTime).unwrap();
assert_eq!(extrinsic_time.base, 10_000_000_000);
assert_eq!(extrinsic_time.slopes, vec![1_000_000_000, 100_000_000]);
let reads = Analysis::median_slopes(&data, BenchmarkSelector::Reads).unwrap();
assert_eq!(reads.base, 2);
assert_eq!(reads.slopes, vec![1, 0]);
let writes = Analysis::median_slopes(&data, BenchmarkSelector::Writes).unwrap();
assert_eq!(writes.base, 0);
assert_eq!(writes.slopes, vec![0, 2]);
}
#[test]
fn analysis_median_min_squares_should_work() {
let data = vec![
benchmark_result(
vec![(BenchmarkParameter::n, 1), (BenchmarkParameter::m, 5)],
11_500_000,
0,
3,
10,
),
benchmark_result(
vec![(BenchmarkParameter::n, 2), (BenchmarkParameter::m, 5)],
12_500_000,
0,
4,
10,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 5)],
13_500_000,
0,
5,
10,
),
benchmark_result(
vec![(BenchmarkParameter::n, 4), (BenchmarkParameter::m, 5)],
14_500_000,
0,
6,
10,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 1)],
13_100_000,
0,
5,
2,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 3)],
13_300_000,
0,
5,
6,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 7)],
13_700_000,
0,
5,
14,
),
benchmark_result(
vec![(BenchmarkParameter::n, 3), (BenchmarkParameter::m, 10)],
14_000_000,
0,
5,
20,
),
];
let extrinsic_time =
Analysis::min_squares_iqr(&data, BenchmarkSelector::ExtrinsicTime).unwrap();
assert_eq!(extrinsic_time.base, 10_000_000_000);
assert_eq!(extrinsic_time.slopes, vec![1000000000, 100000000]);
let reads = Analysis::min_squares_iqr(&data, BenchmarkSelector::Reads).unwrap();
assert_eq!(reads.base, 2);
assert_eq!(reads.slopes, vec![1, 0]);
let writes = Analysis::min_squares_iqr(&data, BenchmarkSelector::Writes).unwrap();
assert_eq!(writes.base, 0);
assert_eq!(writes.slopes, vec![0, 2]);
}
#[test]
fn analysis_min_squares_iqr_uses_multiple_samples_for_same_parameters() {
let data = vec![
benchmark_result(vec![(BenchmarkParameter::n, 0)], 2_000_000, 0, 0, 0),
benchmark_result(vec![(BenchmarkParameter::n, 0)], 4_000_000, 0, 0, 0),
benchmark_result(vec![(BenchmarkParameter::n, 1)], 4_000_000, 0, 0, 0),
benchmark_result(vec![(BenchmarkParameter::n, 1)], 8_000_000, 0, 0, 0),
];
let extrinsic_time =
Analysis::min_squares_iqr(&data, BenchmarkSelector::ExtrinsicTime).unwrap();
assert_eq!(extrinsic_time.base, 3_000_000_000);
assert_eq!(extrinsic_time.slopes, vec![3_000_000_000]);
}
#[test]
fn intercept_of_a_little_under_zero_is_rounded_up_to_zero() {
// Analytically this should result in an intercept of 0, but
// due to numerical imprecision this will generate an intercept
// equal to roughly -0.0000000000000004440892098500626
let data = vec![
benchmark_result(vec![(BenchmarkParameter::n, 1)], 2, 0, 0, 0),
benchmark_result(vec![(BenchmarkParameter::n, 2)], 4, 0, 0, 0),
benchmark_result(vec![(BenchmarkParameter::n, 3)], 6, 0, 0, 0),
];
let extrinsic_time =
Analysis::min_squares_iqr(&data, BenchmarkSelector::ExtrinsicTime).unwrap();
assert_eq!(extrinsic_time.base, 0);
assert_eq!(extrinsic_time.slopes, vec![2000]);
}
}
@@ -0,0 +1,166 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! A set of benchmarks which can establish a global baseline for all other
//! benchmarking. These benchmarks do not require a pallet to be deployed.
#![cfg(feature = "runtime-benchmarks")]
use crate::benchmarks;
use alloc::{vec, vec::Vec};
use pezframe_system::Pallet as System;
use pezsp_runtime::{
traits::{AppVerify, Hash},
RuntimeAppPublic,
};
mod crypto {
use pezsp_application_crypto::{app_crypto, sr25519, KeyTypeId};
pub const TEST_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test");
app_crypto!(sr25519, TEST_KEY_TYPE_ID);
}
pub type SignerId = crypto::Public;
pub struct Pallet<T: Config>(System<T>);
pub trait Config: pezframe_system::Config {}
benchmarks! {
addition {
let i in 0 .. 1_000_000;
let mut start = 0;
}: {
(0..i).for_each(|_| start += 1);
} verify {
assert_eq!(start, i);
}
subtraction {
let i in 0 .. 1_000_000;
let mut start = u32::MAX;
}: {
(0..i).for_each(|_| start -= 1);
} verify {
assert_eq!(start, u32::MAX - i);
}
multiplication {
let i in 0 .. 1_000_000;
let mut out = 0;
}: {
(1..=i).for_each(|j| out = 2 * j);
} verify {
assert_eq!(out, 2 * i);
}
division {
let i in 0 .. 1_000_000;
let mut out = 0;
}: {
(0..=i).for_each(|j| out = j / 2);
} verify {
assert_eq!(out, i / 2);
}
hashing {
let mut hash = T::Hash::default();
}: {
(0..=100_000u32).for_each(|j| hash = T::Hashing::hash(&j.to_be_bytes()));
} verify {
assert!(hash != T::Hash::default());
}
sr25519_verification {
let i in 0 .. 100;
let public = SignerId::generate_pair(None);
let sigs_count: u8 = i.try_into().unwrap();
let msg_and_sigs: Vec<_> = (0..sigs_count).map(|j| {
let msg = vec![j, j];
(msg.clone(), public.sign(&msg).unwrap())
})
.collect();
}: {
msg_and_sigs.iter().for_each(|(msg, sig)| {
assert!(sig.verify(&msg[..], &public));
});
}
impl_benchmark_test_suite!(
Pallet,
mock::new_test_ext(),
mock::Test,
);
}
#[cfg(test)]
pub mod mock {
use pezframe_support::derive_impl;
use pezsp_runtime::{testing::H256, BuildStorage};
type AccountId = u64;
type Nonce = u32;
type Block = pezframe_system::mocking::MockBlock<Test>;
pezframe_support::construct_runtime!(
pub enum Test
{
System: pezframe_system,
}
);
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type BaseCallFilter = pezframe_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type Nonce = Nonce;
type RuntimeCall = RuntimeCall;
type Hash = H256;
type Hashing = ::pezsp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = pezsp_runtime::traits::IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = pezframe_support::traits::ConstU32<16>;
}
impl super::Config for Test {}
pub fn new_test_ext() -> pezsp_io::TestExternalities {
use pezsp_keystore::{testing::MemoryKeystore, KeystoreExt};
let t = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let mut ext = pezsp_io::TestExternalities::new(t);
ext.register_extension(KeystoreExt::new(MemoryKeystore::new()));
ext
}
}
+432
View File
@@ -0,0 +1,432 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Macro for benchmarking a FRAME runtime.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
#[cfg(feature = "std")]
mod analysis;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod tests_instance;
mod utils;
pub mod baseline;
pub mod v1;
/// Private exports that are being used by macros.
///
/// The exports are not stable and should not be relied on.
#[doc(hidden)]
pub mod __private {
pub use alloc::{boxed::Box, str, vec, vec::Vec};
pub use codec;
pub use pezframe_support::{storage, traits};
pub use log;
pub use paste;
pub use pezsp_core::defer;
pub use pezsp_io::storage::root as storage_root;
pub use pezsp_runtime::{traits::Zero, StateVersion};
pub use pezsp_storage::{well_known_keys, TrackedStorageKey};
}
#[cfg(feature = "std")]
pub use analysis::{Analysis, AnalysisChoice, BenchmarkSelector};
pub use utils::*;
pub use v1::*;
/// Contains macros, structs, and traits associated with v2 of the pallet benchmarking syntax.
///
/// The [`v2::benchmarks`] and [`v2::instance_benchmarks`] macros can be used to designate a
/// module as a benchmarking module that can contain benchmarks and benchmark tests. The
/// `#[benchmarks]` variant will set up a regular, non-instance benchmarking module, and the
/// `#[instance_benchmarks]` variant will set up the module in instance benchmarking mode.
///
/// Benchmarking modules should be gated behind a `#[cfg(feature = "runtime-benchmarks")]`
/// feature gate to ensure benchmarking code that is only compiled when the
/// `runtime-benchmarks` feature is enabled is not referenced.
///
/// The following is the general syntax for a benchmarks (or instance benchmarks) module:
///
/// ## General Syntax
///
/// ```ignore
/// #![cfg(feature = "runtime-benchmarks")]
///
/// use super::{mock_helpers::*, Pallet as MyPallet};
/// use pezframe_benchmarking::v2::*;
///
/// #[benchmarks]
/// mod benchmarks {
/// use super::*;
///
/// #[benchmark]
/// fn bench_name_1(x: Linear<7, 1_000>, y: Linear<1_000, 100_0000>) {
/// // setup code
/// let z = x + y;
/// let caller = whitelisted_caller();
///
/// #[extrinsic_call]
/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments);
///
/// // verification code
/// assert_eq!(MyPallet::<T>::my_var(), z);
/// }
///
/// #[benchmark]
/// fn bench_name_2() {
/// // setup code
/// let caller = whitelisted_caller();
///
/// #[block]
/// {
/// something(some, thing);
/// my_extrinsic(RawOrigin::Signed(caller), some, argument);
/// something_else(foo, bar);
/// }
///
/// // verification code
/// assert_eq!(MyPallet::<T>::something(), 37);
/// }
/// }
/// ```
///
/// ## Benchmark Definitions
///
/// Within a `#[benchmarks]` or `#[instance_benchmarks]` module, you can define individual
/// benchmarks using the `#[benchmark]` attribute, as shown in the example above.
///
/// The `#[benchmark]` attribute expects a function definition with a blank return type (or a
/// return type compatible with `Result<(), BenchmarkError>`, as discussed below) and zero or
/// more arguments whose names are valid [BenchmarkParameter](`crate::BenchmarkParameter`)
/// parameters, such as `x`, `y`, `a`, `b`, etc., and whose param types must implement
/// [ParamRange](`v2::ParamRange`). At the moment the only valid type that implements
/// [ParamRange](`v2::ParamRange`) is [Linear](`v2::Linear`).
///
/// The valid syntax for defining a [Linear](`v2::Linear`) is `Linear<A, B>` where `A`, and `B`
/// are valid integer literals (that fit in a `u32`), such that `B` >= `A`.
///
/// Anywhere within a benchmark function you may use the generic `T: Config` parameter as well
/// as `I` in the case of an `#[instance_benchmarks]` module. You should not add these to the
/// function signature as this will be handled automatically for you based on whether this is a
/// `#[benchmarks]` or `#[instance_benchmarks]` module and whatever [where clause](#where-clause)
/// you have defined for the module. You should not manually add any generics to the
/// signature of your benchmark function.
///
/// Also note that the `// setup code` and `// verification code` comments shown above are not
/// required and are included simply for demonstration purposes.
///
/// ### `#[extrinsic_call]` and `#[block]`
///
/// Within the benchmark function body, either an `#[extrinsic_call]` or a `#[block]`
/// annotation is required. These attributes should be attached to a block (shown in
/// `bench_name_2` above) or a one-line function call (shown in `bench_name_1` above, in `syn`
/// parlance this should be an `ExprCall`), respectively.
///
/// The `#[block]` syntax is broad and will benchmark any code contained within the block the
/// attribute is attached to. If `#[block]` is attached to something other than a block, a
/// compiler error will be emitted.
///
/// The one-line `#[extrinsic_call]` syntax must consist of a function call to an extrinsic,
/// where the first argument is the origin. If `#[extrinsic_call]` is attached to an item that
/// doesn't meet these requirements, a compiler error will be emitted.
///
/// As a short-hand, you may substitute the name of the extrinsic call with `_`, such as the
/// following:
///
/// ```ignore
/// #[extrinsic_call]
/// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0);
/// ```
///
/// The underscore will be substituted with the name of the benchmark (i.e. the name of the
/// function in the benchmark function definition).
///
/// In case of a `force_origin` where you want to elevate the privileges of the provided origin,
/// this is the general syntax:
/// ```ignore
/// #[extrinsic_call]
/// _(force_origin as T::RuntimeOrigin, 0u32.into(), 0);
/// ```
///
/// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves
/// the purpose of designating the boundary between the setup code portion of the benchmark
/// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification
/// stage (everything after the item that the `#[extrinsic_call]` or `#[block]` attribute is
/// attached to). The setup code section should contain any code that needs to execute before
/// the measured portion of the benchmark executes. The verification section is where you can
/// perform assertions to verify that the extrinsic call (or whatever is happening in your
/// block, if you used the `#[block]` syntax) executed successfully.
///
/// Note that neither `#[extrinsic_call]` nor `#[block]` are real attribute macros and are
/// instead consumed by the outer macro pattern as part of the enclosing benchmark function
/// definition. This is why we are able to use `#[extrinsic_call]` and `#[block]` within a
/// function definition even though this behavior has not been stabilized
/// yet—`#[extrinsic_call]` and `#[block]` are parsed and consumed as part of the benchmark
/// definition parsing code, so they never expand as their own attribute macros.
///
/// ### Optional Attributes
///
/// The keywords `extra` and `skip_meta` can be provided as optional arguments to the
/// `#[benchmark]` attribute, i.e. `#[benchmark(extra, skip_meta)]`. Including either of these
/// will enable the `extra` or `skip_meta` option, respectively. These options enable the same
/// behavior they did in the old benchmarking syntax in `pezframe_benchmarking`, namely:
///
/// #### `extra`
///
/// Specifies that this benchmark should not normally run. To run benchmarks marked with
/// `extra`, you will need to invoke the `pezframe-benchmarking-cli` with `--extra`.
///
/// #### `skip_meta`
///
/// Specifies that the benchmarking framework should not analyze the storage keys that the
/// benchmarked code read or wrote. This useful to suppress the prints in the form of unknown
/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis of
/// all accesses, not just ones without metadata.
///
/// ## Where Clause
///
/// Some pallets require a where clause specifying constraints on their generics to make
/// writing benchmarks feasible. To accommodate this situation, you can provide such a where
/// clause as the (only) argument to the `#[benchmarks]` or `#[instance_benchmarks]` attribute
/// macros. Below is an example of this taken from the `message-queue` pallet.
///
/// ```ignore
/// #[benchmarks(
/// where
/// <<T as Config>::MessageProcessor as ProcessMessage>::Origin: From<u32> + PartialEq,
/// <T as Config>::Size: From<u32>,
/// )]
/// mod benchmarks {
/// use super::*;
/// // ...
/// }
/// ```
///
/// ## Benchmark Tests
///
/// Benchmark tests can be generated using the old syntax in `pezframe_benchmarking`,
/// including the `pezframe_benchmarking::impl_benchmark_test_suite` macro.
///
/// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module):
/// ```ignore
/// #[benchmarks]
/// mod benchmarks {
/// use super::*;
/// // ...
/// impl_benchmark_test_suite!(
/// MessageQueue,
/// crate::mock::new_test_ext::<crate::integration_test::Test>(),
/// crate::integration_test::Test
/// );
/// }
/// ```
///
/// ## Benchmark Function Generation
///
/// The benchmark function definition that you provide is used to automatically create a number
/// of impls and structs required by the benchmarking engine. Additionally, a benchmark
/// function is also generated that resembles the function definition you provide, with a few
/// modifications:
/// 1. The function name is transformed from i.e. `original_name` to `_original_name` so as not to
/// collide with the struct `original_name` that is created for some of the benchmarking engine
/// impls.
/// 2. Appropriate `T: Config` and `I` (if this is an instance benchmark) generics are added to the
/// function automatically during expansion, so you should not add these manually on your
/// function definition (but you may make use of `T` and `I` anywhere within your benchmark
/// function, in any of the three sections (setup, call, verification).
/// 3. Arguments such as `u: Linear<10, 100>` are converted to `u: u32` to make the function
/// directly callable.
/// 4. A `verify: bool` param is added as the last argument. Specifying `true` will result in the
/// verification section of your function executing, while a value of `false` will skip
/// verification.
/// 5. If you specify a return type on the function definition, it must conform to the [rules
/// below](#support-for-result-benchmarkerror-and-the--operator), and the last statement of the
/// function definition must resolve to something compatible with `Result<(), BenchmarkError>`.
///
/// The reason we generate an actual function as part of the expansion is to allow the compiler
/// to enforce several constraints that would otherwise be difficult to enforce and to reduce
/// developer confusion (especially regarding the use of the `?` operator, as covered below).
///
/// Note that any attributes, comments, and doc comments attached to your benchmark function
/// definition are also carried over onto the resulting benchmark function and the struct for
/// that benchmark. As a result you should be careful about what attributes you attach here as
/// they will be replicated in multiple places.
///
/// ### Support for `Result<(), BenchmarkError>` and the `?` operator
///
/// You may optionally specify `Result<(), BenchmarkError>` as the return type of your
/// benchmark function definition. If you do so, you must return a compatible `Result<(),
/// BenchmarkError>` as the *last statement* of your benchmark function definition. You may
/// also use the `?` operator throughout your benchmark function definition if you choose to
/// follow this route. See the example below:
///
/// ```ignore
/// #![cfg(feature = "runtime-benchmarks")]
///
/// use super::{mock_helpers::*, Pallet as MyPallet};
/// use pezframe_benchmarking::v2::*;
///
/// #[benchmarks]
/// mod benchmarks {
/// use super::*;
///
/// #[benchmark]
/// fn bench_name(x: Linear<5, 25>) -> Result<(), BenchmarkError> {
/// // setup code
/// let z = x + 4;
/// let caller = whitelisted_caller();
///
/// // note we can make use of the ? operator here because of the return type
/// something(z)?;
///
/// #[extrinsic_call]
/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments);
///
/// // verification code
/// assert_eq!(MyPallet::<T>::my_var(), z);
///
/// // we must return a valid `Result<(), BenchmarkError>` as the last line of our benchmark
/// // function definition. This line is not included as part of the verification code that
/// // appears above it.
/// Ok(())
/// }
/// }
/// ```
///
/// ## Migrate from v1 to v2
///
/// To migrate your code from benchmarking v1 to benchmarking v2, you may follow these
/// steps:
/// 1. Change the import from `pezframe_benchmarking::v1::` to `pezframe_benchmarking::v2::*`, or
/// `frame::benchmarking::prelude::*` under the umbrella crate;
/// 2. Move the code inside the v1 `benchmarks! { ... }` block to the v2 benchmarks module `mod
/// benchmarks { ... }` under the benchmarks macro (`#[benchmarks]` for a regular module, or
/// `#[instance_benchmarks]` to set up the module in instance benchmarking mode);
/// 3. Turn each v1 benchmark into a function inside the v2 benchmarks module with the same name,
/// having either a blank return type or a return type compatible with `Result<(),
/// BenchmarkError>`. For instance, `foo { ... }` can become `fn foo() -> Result<(),
/// BenchmarkError>`. More in detail:
/// 1. Move all the v1 complexity parameters as [ParamRange](`v2::ParamRange`) arguments to the
/// v2 function, and their setup code to the body of the function. For instance, `let y in 0
/// .. 10 => setup(y)?;` from v1 will give a `y: Linear<0, 10>` argument to the corresponding
/// function in v2, while `setup(y)?;` will be moved to the body of the function;
/// 2. Move all the v1 setup code to the body of the v2 function;
/// 3. Move the benchmarked code to the body of the v2 function under the appropriate macro
/// attribute: `#[extrinsic_call]` for extrinsic pallet calls and `#[block]` for blocks of
/// code;
/// 4. Move the v1 verify code block to the body of the v2 function, after the
/// `#[extrinsic_call]` or `#[block]` attribute.
/// 5. If the function returns a `Result<(), BenchmarkError>`, end with `Ok(())`.
///
/// As for tests, the code is the same as v1 (see [Benchmark Tests](#benchmark-tests)).
///
/// As an example migration, the following v1 code
///
/// ```ignore
/// #![cfg(feature = "runtime-benchmarks")]
///
/// use pezframe_benchmarking::v1::*;
///
/// benchmarks! {
///
/// // first dispatchable: this is a user dispatchable and operates on a `u8` vector of
/// // size `l`
/// foo {
/// let caller = funded_account::<T>(b"caller", 0);
/// let l in 1 .. 10_000 => initialize_l(l);
/// }: {
/// _(RuntimeOrigin::Signed(caller), vec![0u8; l])
/// } verify {
/// assert_last_event::<T>(Event::FooExecuted { result: Ok(()) }.into());
/// }
/// }
/// ```
///
/// would become the following v2 code:
///
/// ```ignore
/// #![cfg(feature = "runtime-benchmarks")]
///
/// use pezframe_benchmarking::v2::*;
///
/// #[benchmarks]
/// mod benchmarks {
/// use super::*;
///
/// // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of
/// // size `l`
/// #[benchmark]
/// fn foo(l: Linear<1 .. 10_000>) -> Result<(), BenchmarkError> {
/// let caller = funded_account::<T>(b"caller", 0);
/// initialize_l(l);
///
/// #[extrinsic_call]
/// _(RuntimeOrigin::Signed(caller), vec![0u8; l]);
///
/// // Everything onwards will be treated as test.
/// assert_last_event::<T>(Event::FooExecuted { result: Ok(()) }.into());
/// Ok(())
/// }
/// }
/// ```
pub mod v2 {
pub use super::*;
pub use pezframe_support_procedural::{
benchmark, benchmarks, block, extrinsic_call, instance_benchmarks,
};
// Used in #[benchmark] implementation to ensure that benchmark function arguments
// implement [`ParamRange`].
#[doc(hidden)]
pub use static_assertions::{assert_impl_all, assert_type_eq_all};
/// Used by the new benchmarking code to specify that a benchmarking variable is linear
/// over some specified range, i.e. `Linear<0, 1_000>` means that the corresponding variable
/// is allowed to range from `0` to `1000`, inclusive.
///
/// See [`v2`] for more info.
pub struct Linear<const A: u32, const B: u32>;
/// Trait that must be implemented by all structs that can be used as parameter range types
/// in the new benchmarking code (i.e. `Linear<0, 1_000>`). Right now there is just
/// [`Linear`] but this could later be extended to support additional non-linear parameter
/// ranges.
///
/// See [`v2`] for more info.
pub trait ParamRange {
/// Represents the (inclusive) starting number of this `ParamRange`.
fn start(&self) -> u32;
/// Represents the (inclusive) ending number of this `ParamRange`.
fn end(&self) -> u32;
}
impl<const A: u32, const B: u32> ParamRange for Linear<A, B> {
fn start(&self) -> u32 {
A
}
fn end(&self) -> u32 {
B
}
}
}
@@ -0,0 +1,423 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Tests for the module.
#![cfg(test)]
use pezframe_support::{derive_impl, parameter_types, traits::ConstU32};
use pezsp_runtime::{
testing::H256,
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};
use std::cell::RefCell;
#[pezframe_support::pallet(dev_mode)]
mod pezpallet_test {
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {
type LowerBound: Get<u32>;
type UpperBound: Get<u32>;
type MaybeItem: Get<Option<u32>>;
}
#[pallet::storage]
pub(crate) type Value<T: Config> = StorageValue<_, u32, OptionQuery>;
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn set_value(origin: OriginFor<T>, n: u32) -> DispatchResult {
let _sender = ensure_signed(origin)?;
Value::<T>::put(n);
Ok(())
}
pub fn dummy(origin: OriginFor<T>, _n: u32) -> DispatchResult {
let _sender = ensure_none(origin)?;
Ok(())
}
pub fn always_error(_origin: OriginFor<T>) -> DispatchResult {
return Err("I always fail".into());
}
}
}
type Block = pezframe_system::mocking::MockBlock<Test>;
pezframe_support::construct_runtime!(
pub enum Test
{
System: pezframe_system,
TestPallet: pezpallet_test,
}
);
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type BaseCallFilter = pezframe_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type Nonce = u64;
type Hash = H256;
type RuntimeCall = RuntimeCall;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = pezframe_support::traits::ConstU32<16>;
}
parameter_types! {
pub const MaybeItem: Option<u32> = None;
}
impl pezpallet_test::Config for Test {
type LowerBound = ConstU32<1>;
type UpperBound = ConstU32<100>;
type MaybeItem = MaybeItem;
}
fn new_test_ext() -> pezsp_io::TestExternalities {
RuntimeGenesisConfig::default().build_storage().unwrap().into()
}
thread_local! {
/// Tracks the used components per value. Needs to be a thread local since the
/// benchmarking clears the storage after each run.
static VALUES_PER_COMPONENT: RefCell<Vec<u32>> = RefCell::new(vec![]);
}
// NOTE: This attribute is only needed for the `modify_in_` functions.
#[allow(unreachable_code)]
mod benchmarks {
use super::{new_test_ext, pezpallet_test::Value, Test, VALUES_PER_COMPONENT};
use crate::{account, BenchmarkError, BenchmarkParameter, BenchmarkResult, BenchmarkingSetup};
use pezframe_support::{assert_err, assert_ok, ensure, traits::Get};
use pezframe_system::RawOrigin;
use rusty_fork::rusty_fork_test;
// Additional used internally by the benchmark macro.
use super::pezpallet_test::{Call, Config, Pallet};
crate::benchmarks! {
where_clause {
where
crate::tests::RuntimeOrigin: From<RawOrigin<<T as pezframe_system::Config>::AccountId>>,
}
set_value {
let b in 1 .. 1000;
let caller = account::<T::AccountId>("caller", 0, 0);
}: _ (RawOrigin::Signed(caller), b.into())
verify {
assert_eq!(Value::<T>::get(), Some(b));
}
other_name {
let b in 1 .. 1000;
}: dummy (RawOrigin::None, b.into())
sort_vector {
let x in 1 .. 10000;
let mut m = Vec::<u32>::new();
for i in (0..x).rev() {
m.push(i);
}
}: {
m.sort();
} verify {
ensure!(m[0] == 0, "You forgot to sort!")
}
bad_origin {
let b in 1 .. 1000;
let caller = account::<T::AccountId>("caller", 0, 0);
}: dummy (RawOrigin::Signed(caller), b.into())
bad_verify {
let x in 1 .. 10000;
let mut m = Vec::<u32>::new();
for i in (0..x).rev() {
m.push(i);
}
}: { }
verify {
ensure!(m[0] == 0, "You forgot to sort!")
}
no_components {
let caller = account::<T::AccountId>("caller", 0, 0);
}: set_value(RawOrigin::Signed(caller), 0)
variable_components {
let b in ( T::LowerBound::get() ) .. T::UpperBound::get();
}: dummy (RawOrigin::None, b.into())
#[extra]
extra_benchmark {
let b in 1 .. 1000;
let caller = account::<T::AccountId>("caller", 0, 0);
}: set_value(RawOrigin::Signed(caller), b.into())
verify {
assert_eq!(Value::<T>::get(), Some(b));
}
#[skip_meta]
skip_meta_benchmark {
let b in 1 .. 1000;
let caller = account::<T::AccountId>("caller", 0, 0);
}: set_value(RawOrigin::Signed(caller), b.into())
verify {
assert_eq!(Value::<T>::get(), Some(b));
}
override_benchmark {
let b in 1 .. 1000;
let caller = account::<T::AccountId>("caller", 0, 0);
}: {
Err(BenchmarkError::Override(
BenchmarkResult {
extrinsic_time: 1_234_567_890,
reads: 1337,
writes: 420,
..Default::default()
}
))?;
}
skip_benchmark {
let value = T::MaybeItem::get().ok_or(BenchmarkError::Skip)?;
}: {
// This should never be reached.
assert!(value > 100);
}
modify_in_setup_then_error {
Value::<T>::set(Some(123));
return Err(BenchmarkError::Stop("Should error"));
}: { }
modify_in_call_then_error {
}: {
Value::<T>::set(Some(123));
return Err(BenchmarkError::Stop("Should error"));
}
modify_in_verify_then_error {
}: {
} verify {
Value::<T>::set(Some(123));
return Err(BenchmarkError::Stop("Should error"));
}
// Stores all component values in the thread-local storage.
values_per_component {
let n in 0 .. 10;
}: {
VALUES_PER_COMPONENT.with(|v| v.borrow_mut().push(n));
}
}
#[test]
fn benchmarks_macro_works() {
// Check benchmark creation for `set_value`.
let selected = SelectedBenchmark::set_value;
let components = <SelectedBenchmark as BenchmarkingSetup<Test>>::components(&selected);
assert_eq!(components, vec![(BenchmarkParameter::b, 1, 1000)]);
new_test_ext().execute_with(|| {
assert_ok!(<SelectedBenchmark as BenchmarkingSetup<Test>>::unit_test_instance(
&selected,
&[(BenchmarkParameter::b, 1)],
));
});
}
#[test]
fn benchmarks_macro_rename_works() {
// Check benchmark creation for `other_dummy`.
let selected = SelectedBenchmark::other_name;
let components = <SelectedBenchmark as BenchmarkingSetup<Test>>::components(&selected);
assert_eq!(components, vec![(BenchmarkParameter::b, 1, 1000)]);
new_test_ext().execute_with(|| {
assert_ok!(<SelectedBenchmark as BenchmarkingSetup<Test>>::unit_test_instance(
&selected,
&[(BenchmarkParameter::b, 1)],
));
});
}
#[test]
fn benchmarks_macro_works_for_non_dispatchable() {
let selected = SelectedBenchmark::sort_vector;
let components = <SelectedBenchmark as BenchmarkingSetup<Test>>::components(&selected);
assert_eq!(components, vec![(BenchmarkParameter::x, 1, 10000)]);
new_test_ext().execute_with(|| {
assert_ok!(<SelectedBenchmark as BenchmarkingSetup<Test>>::unit_test_instance(
&selected,
&[(BenchmarkParameter::x, 1)],
));
});
}
#[test]
fn benchmarks_macro_verify_works() {
// Check postcondition for benchmark `set_value` is valid.
let selected = SelectedBenchmark::set_value;
new_test_ext().execute_with(|| {
assert_ok!(<SelectedBenchmark as BenchmarkingSetup<Test>>::unit_test_instance(
&selected,
&[(BenchmarkParameter::b, 1)],
));
});
// Check postcondition for benchmark `bad_verify` is invalid.
let selected = SelectedBenchmark::bad_verify;
new_test_ext().execute_with(|| {
assert_err!(
<SelectedBenchmark as BenchmarkingSetup<Test>>::unit_test_instance(
&selected,
&[(BenchmarkParameter::x, 10000)],
),
"You forgot to sort!"
);
});
}
#[test]
fn benchmark_override_works() {
let selected = SelectedBenchmark::override_benchmark;
new_test_ext().execute_with(|| {
let result = <SelectedBenchmark as BenchmarkingSetup<Test>>::unit_test_instance(
&selected,
&[(BenchmarkParameter::b, 1)],
);
assert!(matches!(result, Err(BenchmarkError::Override(_))));
});
}
#[test]
fn benchmarks_generate_unit_tests() {
new_test_ext().execute_with(|| {
assert_ok!(Pallet::<Test>::test_benchmark_set_value());
assert_ok!(Pallet::<Test>::test_benchmark_other_name());
assert_ok!(Pallet::<Test>::test_benchmark_sort_vector());
assert_err!(Pallet::<Test>::test_benchmark_bad_origin(), "Bad origin");
assert_err!(Pallet::<Test>::test_benchmark_bad_verify(), "You forgot to sort!");
assert_ok!(Pallet::<Test>::test_benchmark_no_components());
assert_ok!(Pallet::<Test>::test_benchmark_variable_components());
assert!(matches!(
Pallet::<Test>::test_benchmark_override_benchmark(),
Err(BenchmarkError::Override(_)),
));
assert_eq!(Pallet::<Test>::test_benchmark_skip_benchmark(), Err(BenchmarkError::Skip),);
});
}
/// An error return of a benchmark test function still causes the db to be wiped.
#[test]
fn benchmark_error_wipes_storage() {
new_test_ext().execute_with(|| {
// It resets when the error happens in the setup:
assert_err!(
Pallet::<Test>::test_benchmark_modify_in_setup_then_error(),
"Should error"
);
assert_eq!(Value::<Test>::get(), None);
// It resets when the error happens in the call:
assert_err!(Pallet::<Test>::test_benchmark_modify_in_call_then_error(), "Should error");
assert_eq!(Value::<Test>::get(), None);
// It resets when the error happens in the verify:
assert_err!(
Pallet::<Test>::test_benchmark_modify_in_verify_then_error(),
"Should error"
);
assert_eq!(Value::<Test>::get(), None);
});
}
rusty_fork_test! {
/// Test that the benchmarking uses the correct values for each component and
/// that the number of components can be controlled with `VALUES_PER_COMPONENT`.
///
/// NOTE: This test needs to run in its own process, since it
/// otherwise messes up the env variable for the other tests.
#[test]
fn test_values_per_component() {
let tests = vec![
(Some("1"), Err("`VALUES_PER_COMPONENT` must be at least 2".into())),
(Some("asdf"), Err("Could not parse env var `VALUES_PER_COMPONENT` as u32.".into())),
(None, Ok(vec![0, 2, 4, 6, 8, 10])),
(Some("2"), Ok(vec![0, 10])),
(Some("4"), Ok(vec![0, 3, 6, 10])),
(Some("6"), Ok(vec![0, 2, 4, 6, 8, 10])),
(Some("10"), Ok(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 10])),
(Some("11"), Ok(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])),
(Some("99"), Ok(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])),
];
for (num, expected) in tests {
run_test_values_per_component(num, expected);
}
}
}
/// Helper for [`test_values_per_component`].
fn run_test_values_per_component(num: Option<&str>, output: Result<Vec<u32>, BenchmarkError>) {
VALUES_PER_COMPONENT.with(|v| v.borrow_mut().clear());
match num {
Some(n) => std::env::set_var("VALUES_PER_COMPONENT", n),
None => std::env::remove_var("VALUES_PER_COMPONENT"),
}
new_test_ext().execute_with(|| {
let got = Pallet::<Test>::test_benchmark_values_per_component()
.map(|_| VALUES_PER_COMPONENT.with(|v| v.borrow().clone()));
assert_eq!(got, output);
});
}
}
@@ -0,0 +1,235 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Tests for the benchmark macro for instantiable modules
#![cfg(test)]
use pezframe_support::{derive_impl, traits::ConstU32};
use pezsp_runtime::{
testing::H256,
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};
#[pezframe_support::pallet]
mod pezpallet_test {
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
pub trait OtherConfig {
type OtherEvent;
}
#[pallet::config]
pub trait Config<I: 'static = ()>: pezframe_system::Config + OtherConfig {
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self, I>>
+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
type LowerBound: Get<u32>;
type UpperBound: Get<u32>;
}
#[pallet::storage]
pub(crate) type Value<T: Config<I>, I: 'static = ()> = StorageValue<_, u32, OptionQuery>;
#[pallet::event]
pub enum Event<T: Config<I>, I: 'static = ()> {}
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I>
where
<T as OtherConfig>::OtherEvent: Into<<T as Config<I>>::RuntimeEvent>,
{
#[pallet::call_index(0)]
#[pallet::weight({0})]
pub fn set_value(origin: OriginFor<T>, n: u32) -> DispatchResult {
let _sender = ensure_signed(origin)?;
assert!(n >= T::LowerBound::get());
Value::<T, I>::put(n);
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight({0})]
pub fn dummy(origin: OriginFor<T>, _n: u32) -> DispatchResult {
let _sender = ensure_none(origin)?;
Ok(())
}
}
}
type Block = pezframe_system::mocking::MockBlock<Test>;
pezframe_support::construct_runtime!(
pub enum Test
{
System: pezframe_system,
TestPallet: pezpallet_test,
TestPallet2: pezpallet_test::<Instance2>,
}
);
crate::define_benchmarks!(
[pezpallet_test, TestPallet]
[pezpallet_test, TestPallet2]
);
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type BaseCallFilter = pezframe_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Nonce = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ();
type DbWeight = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
impl pezpallet_test::Config for Test {
type RuntimeEvent = RuntimeEvent;
type LowerBound = ConstU32<1>;
type UpperBound = ConstU32<100>;
}
impl pezpallet_test::Config<pezpallet_test::Instance2> for Test {
type RuntimeEvent = RuntimeEvent;
type LowerBound = ConstU32<50>;
type UpperBound = ConstU32<100>;
}
impl pezpallet_test::OtherConfig for Test {
type OtherEvent = RuntimeEvent;
}
fn new_test_ext() -> pezsp_io::TestExternalities {
RuntimeGenesisConfig::default().build_storage().unwrap().into()
}
mod benchmarks {
use super::pezpallet_test::{self, Value};
use crate::account;
use pezframe_support::ensure;
use pezframe_system::RawOrigin;
use pezsp_core::Get;
// Additional used internally by the benchmark macro.
use super::pezpallet_test::{Call, Config, Pallet};
crate::benchmarks_instance_pallet! {
where_clause {
where
<T as pezpallet_test::OtherConfig>::OtherEvent: Clone
+ Into<<T as pezpallet_test::Config<I>>::RuntimeEvent>,
<T as pezpallet_test::Config<I>>::RuntimeEvent: Clone,
}
set_value {
let b in ( <T as Config<I>>::LowerBound::get() ) .. ( <T as Config<I>>::UpperBound::get() );
let caller = account::<T::AccountId>("caller", 0, 0);
}: _ (RawOrigin::Signed(caller), b.into())
verify {
assert_eq!(Value::<T, I>::get(), Some(b));
}
other_name {
let b in 1 .. 1000;
}: dummy (RawOrigin::None, b.into())
sort_vector {
let x in 1 .. 10000;
let mut m = Vec::<u32>::new();
for i in (0..x).rev() {
m.push(i);
}
}: {
m.sort();
} verify {
ensure!(m[0] == 0, "You forgot to sort!")
}
impl_benchmark_test_suite!(
Pallet,
crate::tests_instance::new_test_ext(),
crate::tests_instance::Test
)
}
}
#[test]
fn ensure_correct_instance_is_selected() {
use crate::utils::Benchmarking;
let whitelist = vec![];
let mut batches = Vec::<crate::BenchmarkBatch>::new();
let config = crate::BenchmarkConfig {
pallet: "pezpallet_test".bytes().collect::<Vec<_>>(),
// We only want that this `instance` is used.
// Otherwise the wrong components are used.
instance: "TestPallet".bytes().collect::<Vec<_>>(),
benchmark: "set_value".bytes().collect::<Vec<_>>(),
selected_components: TestPallet::benchmarks(false)
.into_iter()
.find_map(|b| {
if b.name == "set_value".as_bytes() {
Some(b.components.into_iter().map(|c| (c.0, c.1)).collect::<Vec<_>>())
} else {
None
}
})
.unwrap(),
verify: false,
internal_repeats: 1,
};
let params = (&config, &whitelist);
let state = pezsc_client_db::BenchmarkingState::<pezsp_runtime::traits::BlakeTwo256>::new(
Default::default(),
None,
false,
false,
)
.unwrap();
let mut overlay = Default::default();
let mut ext = pezsp_state_machine::Ext::new(&mut overlay, &state, None);
pezsp_externalities::set_and_run_with_externalities(&mut ext, || {
add_benchmarks!(params, batches);
Ok::<_, crate::BenchmarkError>(())
})
.unwrap();
}
@@ -0,0 +1,510 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Interfaces, types and utils for benchmarking a FRAME runtime.
use alloc::vec::Vec;
use codec::{Decode, Encode};
use pezframe_support::{dispatch::DispatchErrorWithPostInfo, pezpallet_prelude::*, traits::StorageInfo};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use pezsp_io::hashing::blake2_256;
use pezsp_runtime::{
traits::TrailingZeroInput, transaction_validity::TransactionValidityError, DispatchError,
};
use pezsp_runtime_interface::pass_by::{
AllocateAndReturnByCodec, AllocateAndReturnPointer, PassFatPointerAndDecode,
PassFatPointerAndRead,
};
use pezsp_storage::TrackedStorageKey;
/// An alphabet of possible parameters to use for benchmarking.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, Copy, PartialEq, Debug, TypeInfo)]
#[allow(missing_docs)]
#[allow(non_camel_case_types)]
pub enum BenchmarkParameter {
a,
b,
c,
d,
e,
f,
g,
h,
i,
j,
k,
l,
m,
n,
o,
p,
q,
r,
s,
t,
u,
v,
w,
x,
y,
z,
}
#[cfg(feature = "std")]
impl std::fmt::Display for BenchmarkParameter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
/// The results of a single of benchmark.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, PartialEq, Debug, TypeInfo)]
pub struct BenchmarkBatch {
/// The pallet containing this benchmark.
#[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
pub pallet: Vec<u8>,
/// The instance of this pallet being benchmarked.
#[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
pub instance: Vec<u8>,
/// The extrinsic (or benchmark name) of this benchmark.
#[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
pub benchmark: Vec<u8>,
/// The results from this benchmark.
pub results: Vec<BenchmarkResult>,
}
// TODO: could probably make API cleaner here.
/// The results of a single of benchmark, where time and db results are separated.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, PartialEq, Debug)]
pub struct BenchmarkBatchSplitResults {
/// The pallet containing this benchmark.
#[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
pub pallet: Vec<u8>,
/// The instance of this pallet being benchmarked.
#[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
pub instance: Vec<u8>,
/// The extrinsic (or benchmark name) of this benchmark.
#[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
pub benchmark: Vec<u8>,
/// The extrinsic timing results from this benchmark.
pub time_results: Vec<BenchmarkResult>,
/// The db tracking results from this benchmark.
pub db_results: Vec<BenchmarkResult>,
}
/// Result from running benchmarks on a FRAME pallet.
/// Contains duration of the function call in nanoseconds along with the benchmark parameters
/// used for that benchmark result.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
pub struct BenchmarkResult {
pub components: Vec<(BenchmarkParameter, u32)>,
pub extrinsic_time: u128,
pub storage_root_time: u128,
pub reads: u32,
pub repeat_reads: u32,
pub writes: u32,
pub repeat_writes: u32,
pub proof_size: u32,
#[cfg_attr(feature = "std", serde(skip))]
pub keys: Vec<(Vec<u8>, u32, u32, bool)>,
}
impl BenchmarkResult {
pub fn from_weight(w: Weight) -> Self {
Self { extrinsic_time: (w.ref_time() / 1_000) as u128, ..Default::default() }
}
}
/// Helper module to make serde serialize `Vec<u8>` as strings.
#[cfg(feature = "std")]
mod serde_as_str {
pub fn serialize<S>(value: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = std::str::from_utf8(value).map_err(serde::ser::Error::custom)?;
serializer.collect_str(s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s: &str = serde::de::Deserialize::deserialize(deserializer)?;
Ok(s.into())
}
}
/// Possible errors returned from the benchmarking pipeline.
#[derive(Clone, PartialEq, Debug)]
pub enum BenchmarkError {
/// The benchmarking pipeline should stop and return the inner string.
Stop(&'static str),
/// The benchmarking pipeline is allowed to fail here, and we should use the
/// included weight instead.
Override(BenchmarkResult),
/// The benchmarking pipeline is allowed to fail here, and we should simply
/// skip processing these results.
Skip,
/// No weight can be determined; set the weight of this call to zero.
///
/// You can also use `Override` instead, but this is easier to use since `Override` expects the
/// correct components to be present.
Weightless,
}
impl From<BenchmarkError> for &'static str {
fn from(e: BenchmarkError) -> Self {
match e {
BenchmarkError::Stop(s) => s,
BenchmarkError::Override(_) => "benchmark override",
BenchmarkError::Skip => "benchmark skip",
BenchmarkError::Weightless => "benchmark weightless",
}
}
}
impl From<&'static str> for BenchmarkError {
fn from(s: &'static str) -> Self {
Self::Stop(s)
}
}
impl From<DispatchErrorWithPostInfo> for BenchmarkError {
fn from(e: DispatchErrorWithPostInfo) -> Self {
Self::Stop(e.into())
}
}
impl From<DispatchError> for BenchmarkError {
fn from(e: DispatchError) -> Self {
Self::Stop(e.into())
}
}
impl From<TransactionValidityError> for BenchmarkError {
fn from(e: TransactionValidityError) -> Self {
Self::Stop(e.into())
}
}
/// Configuration used to setup and run runtime benchmarks.
#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
pub struct BenchmarkConfig {
/// The encoded name of the pallet to benchmark.
pub pallet: Vec<u8>,
/// The encoded name of the pallet instance to benchmark.
pub instance: Vec<u8>,
/// The encoded name of the benchmark/extrinsic to run.
pub benchmark: Vec<u8>,
/// The selected component values to use when running the benchmark.
pub selected_components: Vec<(BenchmarkParameter, u32)>,
/// Enable an extra benchmark iteration which runs the verification logic for a benchmark.
pub verify: bool,
/// Number of times to repeat benchmark within the Wasm environment. (versus in the client)
pub internal_repeats: u32,
}
/// A list of benchmarks available for a particular pallet and instance.
///
/// All `Vec<u8>` must be valid utf8 strings.
#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
pub struct BenchmarkList {
pub pallet: Vec<u8>,
pub instance: Vec<u8>,
pub benchmarks: Vec<BenchmarkMetadata>,
}
#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
pub struct BenchmarkMetadata {
pub name: Vec<u8>,
pub components: Vec<(BenchmarkParameter, u32, u32)>,
pub pov_modes: Vec<(Vec<u8>, Vec<u8>)>,
}
pezsp_api::decl_runtime_apis! {
/// Runtime api for benchmarking a FRAME runtime.
#[api_version(2)]
pub trait Benchmark {
/// Get the benchmark metadata available for this runtime.
///
/// Parameters
/// - `extra`: Also list benchmarks marked "extra" which would otherwise not be
/// needed for weight calculation.
fn benchmark_metadata(extra: bool) -> (Vec<BenchmarkList>, Vec<StorageInfo>);
/// Dispatch the given benchmark.
fn dispatch_benchmark(config: BenchmarkConfig) -> Result<Vec<BenchmarkBatch>, alloc::string::String>;
}
}
/// Get the number of nanoseconds passed since the UNIX epoch
///
/// WARNING! This is a non-deterministic call. Do not use this within
/// consensus critical logic.
pub fn current_time() -> u128 {
u128::from_le_bytes(self::benchmarking::current_time())
}
/// Interface that provides functions for benchmarking the runtime.
#[pezsp_runtime_interface::runtime_interface]
pub trait Benchmarking {
/// Get the number of nanoseconds passed since the UNIX epoch, as u128 le-bytes.
///
/// You may want to use the standalone function [`current_time`].
///
/// WARNING! This is a non-deterministic call. Do not use this within
/// consensus critical logic.
fn current_time() -> AllocateAndReturnPointer<[u8; 16], 16> {
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("Unix time doesn't go backwards; qed")
.as_nanos()
.to_le_bytes()
}
/// Reset the trie database to the genesis state.
fn wipe_db(&mut self) {
self.wipe()
}
/// Commit pending storage changes to the trie database and clear the database cache.
fn commit_db(&mut self) {
self.commit()
}
/// Get the read/write count.
fn read_write_count(&self) -> AllocateAndReturnByCodec<(u32, u32, u32, u32)> {
self.read_write_count()
}
/// Reset the read/write count.
fn reset_read_write_count(&mut self) {
self.reset_read_write_count()
}
/// Get the DB whitelist.
fn get_whitelist(&self) -> AllocateAndReturnByCodec<Vec<TrackedStorageKey>> {
self.get_whitelist()
}
/// Set the DB whitelist.
fn set_whitelist(&mut self, new: PassFatPointerAndDecode<Vec<TrackedStorageKey>>) {
self.set_whitelist(new)
}
// Add a new item to the DB whitelist.
fn add_to_whitelist(&mut self, add: PassFatPointerAndDecode<TrackedStorageKey>) {
let mut whitelist = self.get_whitelist();
match whitelist.iter_mut().find(|x| x.key == add.key) {
// If we already have this key in the whitelist, update to be the most constrained
// value.
Some(item) => {
item.reads += add.reads;
item.writes += add.writes;
item.whitelisted = item.whitelisted || add.whitelisted;
},
// If the key does not exist, add it.
None => {
whitelist.push(add);
},
}
self.set_whitelist(whitelist);
}
// Remove an item from the DB whitelist.
fn remove_from_whitelist(&mut self, remove: PassFatPointerAndRead<Vec<u8>>) {
let mut whitelist = self.get_whitelist();
whitelist.retain(|x| x.key != remove);
self.set_whitelist(whitelist);
}
fn get_read_and_written_keys(
&self,
) -> AllocateAndReturnByCodec<Vec<(Vec<u8>, u32, u32, bool)>> {
self.get_read_and_written_keys()
}
/// Get current estimated proof size.
fn proof_size(&self) -> AllocateAndReturnByCodec<Option<u32>> {
self.proof_size()
}
}
/// The pallet benchmarking trait.
pub trait Benchmarking {
/// Get the benchmarks available for this pallet. Generally there is one benchmark per
/// extrinsic, so these are sometimes just called "extrinsics".
///
/// Parameters
/// - `extra`: Also return benchmarks marked "extra" which would otherwise not be needed for
/// weight calculation.
fn benchmarks(extra: bool) -> Vec<BenchmarkMetadata>;
/// Run the benchmarks for this pallet.
fn run_benchmark(
name: &[u8],
selected_components: &[(BenchmarkParameter, u32)],
whitelist: &[TrackedStorageKey],
verify: bool,
internal_repeats: u32,
) -> Result<Vec<BenchmarkResult>, BenchmarkError>;
}
/// The recording trait used to mark the start and end of a benchmark.
pub trait Recording {
/// Start the benchmark.
fn start(&mut self) {}
// Stop the benchmark.
fn stop(&mut self) {}
}
/// A no-op recording, used for unit test.
struct NoopRecording;
impl Recording for NoopRecording {}
/// A no-op recording, used for tests that should setup some state before running the benchmark.
struct TestRecording<'a> {
on_before_start: Option<&'a dyn Fn()>,
}
impl<'a> TestRecording<'a> {
fn new(on_before_start: &'a dyn Fn()) -> Self {
Self { on_before_start: Some(on_before_start) }
}
}
impl<'a> Recording for TestRecording<'a> {
fn start(&mut self) {
(self.on_before_start.take().expect("start called more than once"))();
}
}
/// Records the time and proof size of a single benchmark iteration.
pub struct BenchmarkRecording<'a> {
on_before_start: Option<&'a dyn Fn()>,
start_extrinsic: Option<u128>,
finish_extrinsic: Option<u128>,
start_pov: Option<u32>,
end_pov: Option<u32>,
}
impl<'a> BenchmarkRecording<'a> {
pub fn new(on_before_start: &'a dyn Fn()) -> Self {
Self {
on_before_start: Some(on_before_start),
start_extrinsic: None,
finish_extrinsic: None,
start_pov: None,
end_pov: None,
}
}
}
impl<'a> Recording for BenchmarkRecording<'a> {
fn start(&mut self) {
(self.on_before_start.take().expect("start called more than once"))();
self.start_pov = crate::benchmarking::proof_size();
self.start_extrinsic = Some(current_time());
}
fn stop(&mut self) {
self.finish_extrinsic = Some(current_time());
self.end_pov = crate::benchmarking::proof_size();
}
}
impl<'a> BenchmarkRecording<'a> {
pub fn start_pov(&self) -> Option<u32> {
self.start_pov
}
pub fn end_pov(&self) -> Option<u32> {
self.end_pov
}
pub fn diff_pov(&self) -> Option<u32> {
self.start_pov.zip(self.end_pov).map(|(start, end)| end.saturating_sub(start))
}
pub fn elapsed_extrinsic(&self) -> Option<u128> {
self.start_extrinsic
.zip(self.finish_extrinsic)
.map(|(start, end)| end.saturating_sub(start))
}
}
/// The required setup for creating a benchmark.
///
/// Instance generic parameter is optional and can be used in order to capture unused generics for
/// instantiable pallets.
pub trait BenchmarkingSetup<T, I = ()> {
/// Return the components and their ranges which should be tested in this benchmark.
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>;
/// Set up the storage, and prepare a closure to run the benchmark.
fn instance(
&self,
recording: &mut impl Recording,
components: &[(BenchmarkParameter, u32)],
verify: bool,
) -> Result<(), BenchmarkError>;
/// Same as `instance` but passing a closure to run before the benchmark starts.
fn test_instance(
&self,
components: &[(BenchmarkParameter, u32)],
on_before_start: &dyn Fn(),
) -> Result<(), BenchmarkError> {
return self.instance(&mut TestRecording::new(on_before_start), components, true);
}
/// Same as `instance` but passing a no-op recording for unit tests.
fn unit_test_instance(
&self,
components: &[(BenchmarkParameter, u32)],
) -> Result<(), BenchmarkError> {
return self.instance(&mut NoopRecording {}, components, true);
}
}
/// Grab an account, seeded by a name and index.
pub fn account<AccountId: Decode>(name: &'static str, index: u32, seed: u32) -> AccountId {
let entropy = (name, index, seed).using_encoded(blake2_256);
Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
.expect("infinite length input; no invalid inputs for type; qed")
}
/// This caller account is automatically whitelisted for DB reads/writes by the benchmarking macro.
pub fn whitelisted_caller<AccountId: Decode>() -> AccountId {
account::<AccountId>("whitelisted_caller", 0, 0)
}
#[macro_export]
macro_rules! whitelist_account {
($acc:ident) => {
pezframe_benchmarking::benchmarking::add_to_whitelist(
pezframe_system::Account::<T>::hashed_key_for(&$acc).into(),
);
};
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,188 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Autogenerated weights for `pezframe_benchmarking`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 32.0.0
//! DATE: 2025-02-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `4563561839a5`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024`
// Executed Command:
// frame-omni-bencher
// v1
// benchmark
// pallet
// --extrinsic=*
// --runtime=target/production/wbuild/kitchensink-runtime/kitchensink_runtime.wasm
// --pallet=pezframe_benchmarking
// --header=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/HEADER-APACHE2
// --output=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/pezframe/benchmarking/src/weights.rs
// --wasm-execution=compiled
// --steps=50
// --repeat=20
// --heap-pages=4096
// --template=bizinikiwi/.maintain/frame-weight-template.hbs
// --no-storage-info
// --no-min-squares
// --no-median-slopes
// --genesis-builder-policy=none
// --exclude-pallets=pezpallet_xcm,pezpallet_xcm_benchmarks::fungible,pezpallet_xcm_benchmarks::generic,pezpallet_nomination_pools,pezpallet_remark,pezpallet_transaction_storage,pezpallet_election_provider_multi_block,pezpallet_election_provider_multi_block::signed,pezpallet_election_provider_multi_block::unsigned,pezpallet_election_provider_multi_block::verifier
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
#![allow(dead_code)]
use pezframe_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use core::marker::PhantomData;
/// Weight functions needed for `pezframe_benchmarking`.
pub trait WeightInfo {
fn addition(i: u32, ) -> Weight;
fn subtraction(i: u32, ) -> Weight;
fn multiplication(i: u32, ) -> Weight;
fn division(i: u32, ) -> Weight;
fn hashing() -> Weight;
fn sr25519_verification(i: u32, ) -> Weight;
}
/// Weights for `pezframe_benchmarking` using the Bizinikiwi node and recommended hardware.
pub struct BizinikiwiWeight<T>(PhantomData<T>);
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
/// The range of component `i` is `[0, 1000000]`.
fn addition(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 164_000 picoseconds.
Weight::from_parts(180_275, 0)
}
/// The range of component `i` is `[0, 1000000]`.
fn subtraction(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 166_000 picoseconds.
Weight::from_parts(182_392, 0)
}
/// The range of component `i` is `[0, 1000000]`.
fn multiplication(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 166_000 picoseconds.
Weight::from_parts(184_211, 0)
}
/// The range of component `i` is `[0, 1000000]`.
fn division(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 166_000 picoseconds.
Weight::from_parts(182_779, 0)
}
fn hashing() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 24_751_696_000 picoseconds.
Weight::from_parts(24_775_498_000, 0)
}
/// The range of component `i` is `[0, 100]`.
fn sr25519_verification(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 171_000 picoseconds.
Weight::from_parts(4_020_225, 0)
// Standard Error: 4_782
.saturating_add(Weight::from_parts(40_986_205, 0).saturating_mul(i.into()))
}
}
// For backwards compatibility and tests.
impl WeightInfo for () {
/// The range of component `i` is `[0, 1000000]`.
fn addition(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 164_000 picoseconds.
Weight::from_parts(180_275, 0)
}
/// The range of component `i` is `[0, 1000000]`.
fn subtraction(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 166_000 picoseconds.
Weight::from_parts(182_392, 0)
}
/// The range of component `i` is `[0, 1000000]`.
fn multiplication(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 166_000 picoseconds.
Weight::from_parts(184_211, 0)
}
/// The range of component `i` is `[0, 1000000]`.
fn division(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 166_000 picoseconds.
Weight::from_parts(182_779, 0)
}
fn hashing() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 24_751_696_000 picoseconds.
Weight::from_parts(24_775_498_000, 0)
}
/// The range of component `i` is `[0, 100]`.
fn sr25519_verification(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 171_000 picoseconds.
Weight::from_parts(4_020_225, 0)
// Standard Error: 4_782
.saturating_add(Weight::from_parts(40_986_205, 0).saturating_mul(i.into()))
}
}