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,77 @@
[package]
name = "pezframe-benchmarking"
version = "28.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Macro for benchmarking a FRAME runtime."
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true }
pezframe-support = { workspace = true }
pezframe-support-procedural = { workspace = true }
pezframe-system = { workspace = true }
linregress = { optional = true, workspace = true }
log = { workspace = true }
paste = { workspace = true, default-features = true }
scale-info = { features = ["derive"], workspace = true }
serde = { optional = true, workspace = true, default-features = true }
pezsp-api = { workspace = true }
pezsp-application-crypto = { workspace = true }
pezsp-core = { workspace = true }
pezsp-io = { workspace = true }
pezsp-runtime = { workspace = true }
pezsp-runtime-interface = { workspace = true }
pezsp-storage = { workspace = true }
static_assertions = { workspace = true, default-features = true }
[dev-dependencies]
array-bytes = { workspace = true, default-features = true }
rusty-fork = { workspace = true }
pezsc-client-db = { workspace = true }
pezsp-externalities = { workspace = true }
pezsp-keystore = { workspace = true, default-features = true }
pezsp-state-machine = { workspace = true }
[features]
default = ["std"]
std = [
"codec/std",
"pezframe-support-procedural/std",
"pezframe-support/std",
"pezframe-system/std",
"linregress",
"log/std",
"scale-info/std",
"serde",
"pezsp-api/std",
"pezsp-application-crypto/std",
"pezsp-core/std",
"pezsp-externalities/std",
"pezsp-io/std",
"pezsp-keystore/std",
"pezsp-runtime-interface/std",
"pezsp-runtime/std",
"pezsp-state-machine/std",
"pezsp-storage/std",
]
runtime-benchmarks = [
"pezframe-support-procedural/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezsc-client-db/runtime-benchmarks",
"pezsp-api/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime-interface/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-state-machine/runtime-benchmarks",
]
+196
View File
@@ -0,0 +1,196 @@
# Bizinikiwi Runtime Benchmarking Framework
This crate contains a set of utilities that can be used to benchmark and weigh FRAME pallets that you develop for your
Bizinikiwi Runtime.
## Overview
Bizinikiwi's FRAME framework allows you to develop custom logic for your blockchain that can be included in your runtime.
This flexibility is key to help you design complex and interactive pallets, but without accurate weights assigned to
dispatchables, your blockchain may become vulnerable to denial of service (DoS) attacks by malicious actors.
The Bizinikiwi Runtime Benchmarking Framework is a tool you can use to mitigate DoS attacks against your blockchain
network by benchmarking the computational resources required to execute different functions in the runtime, for example
extrinsics, `on_initialize`, `verify_unsigned`, etc...
The general philosophy behind the benchmarking system is: If your node can know ahead of time how long it will take to
execute an extrinsic, it can safely make decisions to include or exclude that extrinsic based on its available
resources. By doing this, it can keep the block production and import process running smoothly.
To achieve this, we need to model how long it takes to run each function in the runtime by:
* Creating custom benchmarking logic that executes a specific code path of a function.
* Executing the benchmark in the Wasm execution environment, on a specific set of hardware, with a custom runtime
configuration, etc...
* Executing the benchmark across controlled ranges of possible values that may affect the result of the benchmark
(called "components").
* Executing the benchmark multiple times at each point in order to isolate and remove outliers.
* Using the results of the benchmark to create a linear model of the function across its components.
With this linear model, we are able to estimate ahead of time how long it takes to execute some logic, and thus make
informed decisions without actually spending any significant resources at runtime.
Note that we assume that all extrinsics are assumed to be of linear complexity, which is why we are able to always fit
them to a linear model. Quadratic or higher complexity functions are, in general, considered to be dangerous to the
runtime as the weight of these functions may explode as the runtime state or input becomes too complex.
The benchmarking framework comes with the following tools:
* [A set of macros](./src/lib.rs) (`benchmarks!`, `add_benchmark!`, etc...) to make it easy to write, test, and add
runtime benchmarks.
* [A set of linear regression analysis functions](./src/analysis.rs) for processing benchmark data.
* [A CLI extension](../../utils/pezframe/benchmarking-cli/README.md) to make it easy to execute benchmarks on your node.
The end-to-end benchmarking pipeline is disabled by default when compiling a node. If you want to run benchmarks, you
need to enable it by compiling with a Rust feature flag `runtime-benchmarks`. More details about this below.
### Weight
Bizinikiwi represents computational resources using a generic unit of measurement called "Weight". It defines 10^12
Weight as 1 second of computation on the physical machine used for benchmarking. This means that the weight of a
function may change based on the specific hardware used to benchmark the runtime functions.
By modeling the expected weight of each runtime function, the blockchain is able to calculate how many transactions or
system level functions it will be able to execute within a certain period of time. Often, the limiting factor for a
blockchain is the fixed block production time for the network.
Within FRAME, each dispatchable function must have a `#[weight]` annotation with a function that can return the expected
weight for the worst case scenario execution of that function given its inputs. This benchmarking framework will result
in a file that automatically generates those formulas for you, which you can then use in your pallet.
## Writing Benchmarks
Writing a runtime benchmark is much like writing a unit test for your pallet. It needs to be carefully crafted to
execute a certain logical path in your code. In tests you want to check for various success and failure conditions, but
with benchmarks you specifically look for the **most computationally heavy** path, a.k.a the "worst case scenario".
This means that if there are certain storage items or runtime state that may affect the complexity of the function, for
example triggering more iterations in a `for` loop, to get an accurate result, you must set up your benchmark to trigger
this.
It may be that there are multiple paths your function can go down, and it is not clear which one is the heaviest. In
this case, you should just create a benchmark for each scenario! You may find that there are paths in your code where
complexity may become unbounded depending on user input. This may be a hint that you should enforce sane boundaries for
how a user can use your pallet. For example: limiting the number of elements in a vector, limiting the number of
iterations in a `for` loop, etc...
Examples of end-to-end benchmarks can be found in the [pallets provided by Bizinikiwi](../), and the specific details on
how to use the `benchmarks!` macro can be found in [its documentation](./src/lib.rs).
## Testing Benchmarks
You can test your benchmarks using the same test runtime that you created for your pallet's unit tests. By creating your
benchmarks in the `benchmarks!` macro, it automatically generates test functions for you:
```rust
fn test_benchmark_[benchmark_name]<T>::() -> Result<(), &'static str>
```
Simply add these functions to a unit test and ensure that the result of the function is `Ok(())`.
> **Note:** If your test runtime and production runtime have different configurations, you may get different results
when testing your benchmark and actually running it.
In general, benchmarks returning `Ok(())` is all you need to check for since it signals the executed extrinsic has
completed successfully. However, you can optionally include a `verify` block with your benchmark, which can additionally
verify any final conditions, such as the final state of your runtime.
These additional `verify` blocks will not affect the results of your final benchmarking process.
To run the tests, you need to enable the `runtime-benchmarks` feature flag. This may also mean you need to move into
your node's binary folder. For example, with the Bizinikiwi repository, this is how you would test the Balances pallet's
benchmarks:
```bash
cargo test -p pezpallet-balances --features runtime-benchmarks
```
> NOTE: Bizinikiwi uses a virtual workspace which does not allow you to compile with feature flags.
> ```
> error: --features is not allowed in the root of a virtual workspace`
> ```
> To solve this, navigate to the folder of the node (`cd bin/node/cli`) or pallet (`cd frame/pallet`) and run the
> command there.
This will instance each linear component with different values. The number of values per component is set to six and can
be changed with the `VALUES_PER_COMPONENT` environment variable.
## Adding Benchmarks
The benchmarks included with each pallet are not automatically added to your node. To actually execute these benchmarks,
you need to implement the `frame_benchmarking::Benchmark` trait. You can see an example of how to do this in the
[included Bizinikiwi node](../../bin/node/runtime/src/lib.rs).
Assuming there are already some benchmarks set up on your node, you just need to add another instance of the
`add_benchmark!` macro:
```rust
/// configuration for running benchmarks
/// | name of your pallet's crate (as imported)
/// v v
add_benchmark!(params, batches, pallet_balances, Balances);
/// ^ ^
/// where all benchmark results are saved |
/// the `struct` created for your pallet by `construct_runtime!`
```
Once you have done this, you will need to compile your node binary with the `runtime-benchmarks` feature flag:
```bash
cd bin/node/cli
cargo build --profile=production --features runtime-benchmarks
```
The production profile applies various compiler optimizations.
These optimizations slow down the compilation process *a lot*.
If you are just testing things out and don't need final numbers, don't include `--profile=production`.
## Running Benchmarks
Finally, once you have a node binary with benchmarks enabled, you need to execute your various benchmarks.
You can get a list of the available benchmarks by running:
```bash
./target/production/bizinikiwi benchmark pallet --chain dev --pallet "*" --extrinsic "*" --repeat 0
```
Then you can run a benchmark like so:
```bash
./target/production/bizinikiwi benchmark pallet \
--chain dev \ # Configurable Chain Spec
--wasm-execution=compiled \ # Always used `wasm-time`
--pallet pallet_balances \ # Select the pallet
--extrinsic transfer \ # Select the extrinsic
--steps 50 \ # Number of samples across component ranges
--repeat 20 \ # Number of times we repeat a benchmark
--output <path> \ # Output benchmark results into a folder or file
```
This will output a file `pallet_name.rs` which implements the `WeightInfo` trait you should include in your pallet.
Double colons `::` will be replaced with a `_` in the output name if you specify a directory. Each blockchain should
generate their own benchmark file with their custom implementation of the `WeightInfo` trait. This means that you will
be able to use these modular Bizinikiwi pallets while still keeping your network safe for your specific configuration and
requirements.
The benchmarking CLI uses a Handlebars template to format the final output file. You can optionally pass the flag
`--template` pointing to a custom template that can be used instead. Within the template, you have access to all the
data provided by the `TemplateData` struct in the [benchmarking CLI
writer](../../utils/pezframe/benchmarking-cli/src/pallet/writer.rs). You can find the default template used
[here](../../utils/pezframe/benchmarking-cli/src/pallet/template.hbs).
There are some custom Handlebars helpers included with our output generation:
* `underscore`: Add an underscore to every 3rd character from the right of a string. Primarily to be used for delimiting
large numbers.
* `join`: Join an array of strings into a space-separated string for the template. Primarily to be used for joining all
the arguments passed to the CLI.
To get a full list of available options when running benchmarks, run:
```bash
./target/production/bizinikiwi benchmark --help
```
License: Apache-2.0
@@ -0,0 +1,48 @@
[package]
name = "pezframe-benchmarking-pezpallet-pov"
version = "18.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Pallet for testing FRAME PoV benchmarking"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true }
pezframe-benchmarking = { workspace = true }
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
pezsp-io = { workspace = true }
pezsp-runtime = { workspace = true }
[features]
default = ["std"]
std = [
"codec/std",
"pezframe-benchmarking/std",
"pezframe-support/std",
"pezframe-system/std",
"scale-info/std",
"pezsp-io/std",
"pezsp-runtime/std",
]
runtime-benchmarks = [
"pezframe-benchmarking/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
]
try-runtime = [
"pezframe-support/try-runtime",
"pezframe-system/try-runtime",
"pezsp-runtime/try-runtime",
]
@@ -0,0 +1,450 @@
// 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.
//! All benchmarks in this file are just for debugging the PoV calculation logic, they are unused.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use pezframe_benchmarking::v2::*;
use pezframe_support::traits::UnfilteredDispatchable;
use pezframe_system::{Pallet as System, RawOrigin};
use pezsp_runtime::traits::Hash;
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn storage_single_value_read() {
Value::<T>::put(123);
#[block]
{
assert_eq!(Value::<T>::get(), Some(123));
}
}
#[benchmark(pov_mode = Ignored)]
fn storage_single_value_ignored_read() {
Value::<T>::put(123);
#[block]
{
assert_eq!(Value::<T>::get(), Some(123));
}
}
#[benchmark(pov_mode = MaxEncodedLen {
Pov::Value2: Ignored
})]
fn storage_single_value_ignored_some_read() {
Value::<T>::put(123);
Value2::<T>::put(123);
#[block]
{
assert_eq!(Value::<T>::get(), Some(123));
assert_eq!(Value2::<T>::get(), Some(123));
}
}
#[benchmark]
fn storage_single_value_read_twice() {
Value::<T>::put(123);
#[block]
{
assert_eq!(Value::<T>::get(), Some(123));
assert_eq!(Value::<T>::get(), Some(123));
}
}
#[benchmark]
fn storage_single_value_write() {
#[block]
{
Value::<T>::put(123);
}
assert_eq!(Value::<T>::get(), Some(123));
}
#[benchmark]
fn storage_single_value_kill() {
Value::<T>::put(123);
#[block]
{
Value::<T>::kill();
}
assert!(!Value::<T>::exists());
}
// This benchmark and the following are testing a storage map with adjacent storage items.
//
// First a storage map is filled and a specific number of other storage items is
// created. Then the one value is read from the map. This demonstrates that the number of other
// nodes in the Trie influences the proof size. The number of inserted nodes can be interpreted
// as the number of `StorageMap`/`StorageValue` in the whole runtime.
#[benchmark(pov_mode = Measured)]
fn storage_1m_map_read_one_value_two_additional_layers() {
(0..(1 << 10)).for_each(|i| Map1M::<T>::insert(i, i));
// Assume there are 16-256 other storage items.
(0..(1u32 << 4)).for_each(|i| {
let k = T::Hashing::hash(&i.to_be_bytes());
pezframe_support::storage::unhashed::put(k.as_ref(), &i);
});
#[block]
{
assert_eq!(Map1M::<T>::get(1 << 9), Some(1 << 9));
}
}
#[benchmark(pov_mode = Measured)]
fn storage_1m_map_read_one_value_three_additional_layers() {
(0..(1 << 10)).for_each(|i| Map1M::<T>::insert(i, i));
// Assume there are 256-4096 other storage items.
(0..(1u32 << 8)).for_each(|i| {
let k = T::Hashing::hash(&i.to_be_bytes());
pezframe_support::storage::unhashed::put(k.as_ref(), &i);
});
#[block]
{
assert_eq!(Map1M::<T>::get(1 << 9), Some(1 << 9));
}
}
#[benchmark(pov_mode = Measured)]
fn storage_1m_map_read_one_value_four_additional_layers() {
(0..(1 << 10)).for_each(|i| Map1M::<T>::insert(i, i));
// Assume there are 4096-65536 other storage items.
(0..(1u32 << 12)).for_each(|i| {
let k = T::Hashing::hash(&i.to_be_bytes());
pezframe_support::storage::unhashed::put(k.as_ref(), &i);
});
#[block]
{
assert_eq!(Map1M::<T>::get(1 << 9), Some(1 << 9));
}
}
// Reads from both storage maps each `n` and `m` times. Should result in two linear components.
#[benchmark]
fn storage_map_read_per_component(n: Linear<0, 100>, m: Linear<0, 100>) {
(0..m * 10).for_each(|i| Map1M::<T>::insert(i, i));
(0..n * 10).for_each(|i| Map16M::<T>::insert(i, i));
#[block]
{
(0..m).for_each(|i| assert_eq!(Map1M::<T>::get(i * 10), Some(i * 10)));
(0..n).for_each(|i| assert_eq!(Map16M::<T>::get(i * 10), Some(i * 10)));
}
}
#[benchmark(pov_mode = MaxEncodedLen {
Pov::Map1M: Ignored
})]
fn storage_map_read_per_component_one_ignored(n: Linear<0, 100>, m: Linear<0, 100>) {
(0..m * 10).for_each(|i| Map1M::<T>::insert(i, i));
(0..n * 10).for_each(|i| Map16M::<T>::insert(i, i));
#[block]
{
(0..m).for_each(|i| assert_eq!(Map1M::<T>::get(i * 10), Some(i * 10)));
(0..n).for_each(|i| assert_eq!(Map16M::<T>::get(i * 10), Some(i * 10)));
}
}
// Reads the same value from a storage map. Should not result in a component.
#[benchmark]
fn storage_1m_map_one_entry_repeated_read(n: Linear<0, 100>) {
Map1M::<T>::insert(0, 0);
#[block]
{
(0..n).for_each(|_| assert_eq!(Map1M::<T>::get(0), Some(0)));
}
}
// Reads the same values from a storage map. Should result in a `1x` linear component.
#[benchmark]
fn storage_1m_map_multiple_entry_repeated_read(n: Linear<0, 100>) {
(0..n).for_each(|i| Map1M::<T>::insert(i, i));
#[block]
{
(0..n).for_each(|i| {
// Reading the same value 10 times does nothing.
(0..10).for_each(|_| assert_eq!(Map1M::<T>::get(i), Some(i)));
});
}
}
#[benchmark]
fn storage_1m_double_map_read_per_component(n: Linear<0, 1024>) {
(0..(1 << 10)).for_each(|i| DoubleMap1M::<T>::insert(i, i, i));
#[block]
{
(0..n).for_each(|i| assert_eq!(DoubleMap1M::<T>::get(i, i), Some(i)));
}
}
#[benchmark]
fn storage_value_bounded_read() {
#[block]
{
assert!(BoundedValue::<T>::get().is_none());
}
}
// Reading unbounded values will produce no mathematical worst case PoV size for this component.
#[benchmark]
fn storage_value_unbounded_read() {
#[block]
{
assert!(UnboundedValue::<T>::get().is_none());
}
}
#[benchmark(pov_mode = Ignored)]
fn storage_value_unbounded_ignored_read() {
#[block]
{
assert!(UnboundedValue::<T>::get().is_none());
}
}
// Same as above, but we still expect a mathematical worst case PoV size for the bounded one.
#[benchmark]
fn storage_value_bounded_and_unbounded_read() {
(0..1024).for_each(|i| Map1M::<T>::insert(i, i));
#[block]
{
assert!(UnboundedValue::<T>::get().is_none());
assert!(BoundedValue::<T>::get().is_none());
}
}
#[benchmark(pov_mode = Measured)]
fn measured_storage_value_read_linear_size(l: Linear<0, { 1 << 22 }>) {
let v: pezsp_runtime::BoundedVec<u8, _> = alloc::vec![0u8; l as usize].try_into().unwrap();
LargeValue::<T>::put(&v);
#[block]
{
assert!(LargeValue::<T>::get().is_some());
}
}
#[benchmark(pov_mode = MaxEncodedLen)]
fn mel_storage_value_read_linear_size(l: Linear<0, { 1 << 22 }>) {
let v: pezsp_runtime::BoundedVec<u8, _> = alloc::vec![0u8; l as usize].try_into().unwrap();
LargeValue::<T>::put(&v);
#[block]
{
assert!(LargeValue::<T>::get().is_some());
}
}
#[benchmark(pov_mode = Measured)]
fn measured_storage_double_value_read_linear_size(l: Linear<0, { 1 << 22 }>) {
let v: pezsp_runtime::BoundedVec<u8, _> = alloc::vec![0u8; l as usize].try_into().unwrap();
LargeValue::<T>::put(&v);
LargeValue2::<T>::put(&v);
#[block]
{
assert!(LargeValue::<T>::get().is_some());
assert!(LargeValue2::<T>::get().is_some());
}
}
#[benchmark(pov_mode = MaxEncodedLen)]
fn mel_storage_double_value_read_linear_size(l: Linear<0, { 1 << 22 }>) {
let v: pezsp_runtime::BoundedVec<u8, _> = alloc::vec![0u8; l as usize].try_into().unwrap();
LargeValue::<T>::put(&v);
LargeValue2::<T>::put(&v);
#[block]
{
assert!(LargeValue::<T>::get().is_some());
assert!(LargeValue2::<T>::get().is_some());
}
}
#[benchmark(pov_mode = MaxEncodedLen {
Pov::LargeValue2: Measured
})]
fn mel_mixed_storage_double_value_read_linear_size(l: Linear<0, { 1 << 22 }>) {
let v: pezsp_runtime::BoundedVec<u8, _> = alloc::vec![0u8; l as usize].try_into().unwrap();
LargeValue::<T>::put(&v);
LargeValue2::<T>::put(&v);
#[block]
{
assert!(LargeValue::<T>::get().is_some());
assert!(LargeValue2::<T>::get().is_some());
}
}
#[benchmark(pov_mode = Measured {
Pov::LargeValue2: MaxEncodedLen
})]
fn measured_mixed_storage_double_value_read_linear_size(l: Linear<0, { 1 << 22 }>) {
let v: pezsp_runtime::BoundedVec<u8, _> = alloc::vec![0u8; l as usize].try_into().unwrap();
LargeValue::<T>::put(&v);
LargeValue2::<T>::put(&v);
#[block]
{
assert!(LargeValue::<T>::get().is_some());
assert!(LargeValue2::<T>::get().is_some());
}
}
#[benchmark(pov_mode = Measured)]
fn storage_map_unbounded_both_measured_read(i: Linear<0, 1000>) {
UnboundedMap::<T>::insert(i, alloc::vec![0; i as usize]);
UnboundedMap2::<T>::insert(i, alloc::vec![0; i as usize]);
#[block]
{
assert!(UnboundedMap::<T>::get(i).is_some());
assert!(UnboundedMap2::<T>::get(i).is_some());
}
}
#[benchmark(pov_mode = MaxEncodedLen {
Pov::UnboundedMap: Measured
})]
fn storage_map_partial_unbounded_read(i: Linear<0, 1000>) {
Map1M::<T>::insert(i, 0);
UnboundedMap::<T>::insert(i, alloc::vec![0; i as usize]);
#[block]
{
assert!(Map1M::<T>::get(i).is_some());
assert!(UnboundedMap::<T>::get(i).is_some());
}
}
#[benchmark(pov_mode = MaxEncodedLen {
Pov::UnboundedMap: Ignored
})]
fn storage_map_partial_unbounded_ignored_read(i: Linear<0, 1000>) {
Map1M::<T>::insert(i, 0);
UnboundedMap::<T>::insert(i, alloc::vec![0; i as usize]);
#[block]
{
assert!(Map1M::<T>::get(i).is_some());
assert!(UnboundedMap::<T>::get(i).is_some());
}
}
// Emitting an event will not incur any PoV.
#[benchmark]
fn emit_event() {
// Emit a single event.
let call = Call::<T>::emit_event {};
#[block]
{
call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap();
}
assert_eq!(System::<T>::events().len(), 1);
}
// A No-OP will not incur any PoV.
#[benchmark]
fn noop() {
let call = Call::<T>::noop {};
#[block]
{
call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap();
}
}
#[benchmark]
fn storage_iteration() {
for i in 0..65000 {
UnboundedMapTwox::<T>::insert(i, alloc::vec![0; 64]);
}
#[block]
{
for (key, value) in UnboundedMapTwox::<T>::iter() {
unsafe {
core::ptr::read_volatile(&key);
core::ptr::read_volatile(value.as_ptr());
}
}
}
}
impl_benchmark_test_suite!(Pallet, super::mock::new_test_ext(), super::mock::Test,);
}
#[cfg(test)]
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,
Baseline: crate,
}
);
#[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 crate::Config for Test {
type RuntimeEvent = RuntimeEvent;
}
pub fn new_test_ext() -> pezsp_io::TestExternalities {
pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
}
}
@@ -0,0 +1,138 @@
// 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.
//! End-to-end testing pallet for PoV benchmarking. Should only be deployed in a testing runtime.
#![cfg_attr(not(feature = "std"), no_std)]
mod benchmarking;
mod tests;
mod weights;
extern crate alloc;
pub use pallet::*;
#[pezframe_support::pallet]
pub mod pallet {
use alloc::vec::Vec;
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self>> + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
}
#[pallet::storage]
pub(crate) type Value<T: Config> = StorageValue<Value = u32, QueryKind = OptionQuery>;
#[pallet::storage]
pub(crate) type Value2<T: Config> = StorageValue<Value = u32, QueryKind = OptionQuery>;
/// A value without a MEL bound.
#[pallet::storage]
#[pallet::unbounded]
pub(crate) type UnboundedValue<T: Config> =
StorageValue<Value = Vec<u8>, QueryKind = OptionQuery>;
/// A value with a MEL bound of 32 byte.
#[pallet::storage]
pub(crate) type BoundedValue<T: Config> =
StorageValue<Value = BoundedVec<u8, ConstU32<32>>, QueryKind = OptionQuery>;
/// 4MiB value.
#[pallet::storage]
pub(crate) type LargeValue<T: Config> =
StorageValue<Value = BoundedVec<u8, ConstU32<{ 1 << 22 }>>, QueryKind = OptionQuery>;
#[pallet::storage]
pub(crate) type LargeValue2<T: Config> =
StorageValue<Value = BoundedVec<u8, ConstU32<{ 1 << 22 }>>, QueryKind = OptionQuery>;
/// A map with a maximum of 1M entries.
#[pallet::storage]
pub(crate) type Map1M<T: Config> = StorageMap<
Hasher = Blake2_256,
Key = u32,
Value = u32,
QueryKind = OptionQuery,
MaxValues = ConstU32<1_000_000>,
>;
/// A map with a maximum of 16M entries.
#[pallet::storage]
pub(crate) type Map16M<T: Config> = StorageMap<
Hasher = Blake2_256,
Key = u32,
Value = u32,
QueryKind = OptionQuery,
MaxValues = ConstU32<16_000_000>,
>;
#[pallet::storage]
pub(crate) type DoubleMap1M<T: Config> = StorageDoubleMap<
Hasher1 = Blake2_256,
Hasher2 = Blake2_256,
Key1 = u32,
Key2 = u32,
Value = u32,
QueryKind = OptionQuery,
MaxValues = ConstU32<1_000_000>,
>;
#[pallet::storage]
#[pallet::unbounded]
pub(crate) type UnboundedMap<T: Config> =
StorageMap<Hasher = Blake2_256, Key = u32, Value = Vec<u32>, QueryKind = OptionQuery>;
#[pallet::storage]
#[pallet::unbounded]
pub(crate) type UnboundedMap2<T: Config> =
StorageMap<Hasher = Blake2_256, Key = u32, Value = Vec<u32>, QueryKind = OptionQuery>;
#[pallet::storage]
#[pallet::unbounded]
pub(crate) type UnboundedMapTwox<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = u32, Value = Vec<u32>, QueryKind = OptionQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
TestEvent,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight({0})]
pub fn emit_event(_origin: OriginFor<T>) -> DispatchResult {
Self::deposit_event(Event::TestEvent);
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight({0})]
pub fn noop(_origin: OriginFor<T>) -> DispatchResult {
Ok(())
}
}
}
@@ -0,0 +1,208 @@
// 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.
//! Test the produces weight functions.
#![cfg(test)]
use super::weights::WeightInfo;
use mock::Test as T;
type W = crate::weights::BizinikiwiWeight<T>;
#[test]
fn writing_is_free() {
let w = W::storage_single_value_write().proof_size();
assert_eq!(w, 0, "Writing is free");
}
#[test]
fn killing_is_free() {
// NOTE: This only applies to state version 1.
let w = W::storage_single_value_kill().proof_size();
assert_eq!(w, 0, "Killing is free");
}
#[test]
fn reading_twice_is_the_same_as_once() {
let w = W::storage_single_value_read().proof_size();
let w2 = W::storage_single_value_read_twice().proof_size();
assert_eq!(w, w2, "Reading twice is the same as once");
}
#[test]
fn storage_single_value_ignored_read_no_pov() {
let w = W::storage_single_value_ignored_read();
assert_eq!(w.proof_size(), 0, "Ignored PoV does not result in PoV");
}
#[test]
fn storage_single_value_ignored_some_read_has_pov() {
let w = W::storage_single_value_ignored_some_read();
assert!(w.proof_size() != 0, "Ignored some does result in PoV");
}
/// Reading the same value from a map does not increase the PoV.
#[test]
fn storage_1m_map_one_entry_repeated_read_const() {
let weight = W::storage_1m_map_one_entry_repeated_read;
let w0 = weight(0).proof_size();
assert!(w0 > 0, "There is a base weight");
let w1 = weight(1).proof_size();
assert_eq!(w0, w1, "Component does not matter");
}
/// Reading multiple values multiple times from a map increases the PoV by the number of reads.
#[test]
fn storage_1m_map_multiple_entry_repeated_read_single_linear() {
let weight = W::storage_1m_map_multiple_entry_repeated_read;
let w0 = weight(0).proof_size();
let w1 = weight(1).proof_size() - w0;
assert!(w1 > 0, "Component matters");
let wm = weight(1000).proof_size();
assert_eq!(w1 * 1000 + w0, wm, "x scales linearly");
}
/// Check that reading two maps at once increases the PoV linearly per map.
#[test]
fn storage_map_read_per_component_double_linear() {
let weight = W::storage_map_read_per_component;
let w00 = weight(0, 0).proof_size();
let w10 = weight(1, 0).proof_size() - w00;
let w01 = weight(0, 1).proof_size() - w00;
assert!(w10 > 0 && w01 > 0, "Components matter");
assert!(w10 != w01, "Each map has its own component");
let wm0 = weight(1000, 0).proof_size();
let w0m = weight(0, 1000).proof_size();
assert_eq!(w00 + w10 * 1000, wm0, "x scales linearly");
assert_eq!(w00 + w01 * 1000, w0m, "y scales linearly");
let wmm = weight(1000, 1000).proof_size();
assert_eq!(wmm + w00, wm0 + w0m, "x + y scales linearly");
}
/// The proof size estimation takes the measured sizes into account and therefore increases with the
/// number of layers.
#[test]
fn additional_layers_do_not_matter() {
let w2 = W::storage_1m_map_read_one_value_two_additional_layers().proof_size();
let w3 = W::storage_1m_map_read_one_value_three_additional_layers().proof_size();
let w4 = W::storage_1m_map_read_one_value_four_additional_layers().proof_size();
assert!(w3 > w2 && w4 > w3, "Additional layers do matter");
}
/// Check that the measured value size instead of the MEL is used.
#[test]
fn linear_measured_size_works() {
let weight = W::measured_storage_value_read_linear_size;
let w0 = weight(0).proof_size();
let w1 = weight(1).proof_size() - w0;
assert_eq!(w1, 1, "x scales with a factor of 1");
let wm = weight(1000).proof_size();
assert_eq!(w1 * 1000 + w0, wm, "x scales linearly");
}
// vice-versa of above `linear_measured_size_works`.
#[test]
fn linear_mel_size_works() {
let weight = W::mel_storage_value_read_linear_size;
let w1 = weight(1).proof_size();
let wm = weight(1000).proof_size();
assert_eq!(w1, wm, "PoV size is const");
}
/// Although there is no estimation possible, it uses the recorded proof size as best effort.
#[test]
fn unbounded_read_best_effort() {
let w = W::storage_value_unbounded_read().proof_size();
assert!(w > 0, "There is a weight");
}
/// For mixed unbounded and bounded reads, the bounded part still increases the PoV.
#[test]
fn partial_unbounded_read_best_effort() {
let w_unbounded = W::storage_value_unbounded_read().proof_size();
let w_bounded = W::storage_value_bounded_read().proof_size();
let w_both = W::storage_value_bounded_and_unbounded_read().proof_size();
assert!(w_both > w_bounded && w_both > w_unbounded, "The bounded part increases the PoV");
}
#[test]
fn emit_event_is_free() {
let w = W::emit_event().proof_size();
assert_eq!(w, 0, "Emitting an event is free");
}
#[test]
fn noop_is_free() {
let w = W::noop().proof_size();
assert_eq!(w, 0, "Noop is free");
}
mod mock {
use pezframe_support::derive_impl;
use pezsp_runtime::testing::H256;
type Block = pezframe_system::mocking::MockBlock<Test>;
pezframe_support::construct_runtime!(
pub enum Test
{
System: pezframe_system,
Baseline: crate,
}
);
#[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 = u32;
type RuntimeCall = RuntimeCall;
type Hash = H256;
type Hashing = ::pezsp_runtime::traits::BlakeTwo256;
type AccountId = u32;
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 crate::Config for Test {
type RuntimeEvent = RuntimeEvent;
}
}
@@ -0,0 +1,895 @@
// 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_pallet_pov`
//!
//! 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_pallet_pov
// --header=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/HEADER-APACHE2
// --output=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/pezframe/benchmarking/pov/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_pallet_pov`.
pub trait WeightInfo {
fn storage_single_value_read() -> Weight;
fn storage_single_value_ignored_read() -> Weight;
fn storage_single_value_ignored_some_read() -> Weight;
fn storage_single_value_read_twice() -> Weight;
fn storage_single_value_write() -> Weight;
fn storage_single_value_kill() -> Weight;
fn storage_1m_map_read_one_value_two_additional_layers() -> Weight;
fn storage_1m_map_read_one_value_three_additional_layers() -> Weight;
fn storage_1m_map_read_one_value_four_additional_layers() -> Weight;
fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight;
fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight;
fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight;
fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight;
fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight;
fn storage_value_bounded_read() -> Weight;
fn storage_value_unbounded_read() -> Weight;
fn storage_value_unbounded_ignored_read() -> Weight;
fn storage_value_bounded_and_unbounded_read() -> Weight;
fn measured_storage_value_read_linear_size(l: u32, ) -> Weight;
fn mel_storage_value_read_linear_size(l: u32, ) -> Weight;
fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight;
fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight;
fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight;
fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight;
fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight;
fn storage_map_partial_unbounded_read(i: u32, ) -> Weight;
fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight;
fn emit_event() -> Weight;
fn noop() -> Weight;
fn storage_iteration() -> Weight;
fn storage_root_is_the_same_every_time(i: u32, ) -> Weight;
}
/// Weights for `pezframe_benchmarking_pallet_pov` using the Bizinikiwi node and recommended hardware.
pub struct BizinikiwiWeight<T>(PhantomData<T>);
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
/// Storage: `Pov::Value` (r:1 w:0)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn storage_single_value_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `8`
// Estimated: `1489`
// Minimum execution time: 1_817_000 picoseconds.
Weight::from_parts(1_881_000, 1489)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Value` (r:1 w:0)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Ignored`)
fn storage_single_value_ignored_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `8`
// Estimated: `0`
// Minimum execution time: 1_782_000 picoseconds.
Weight::from_parts(1_910_000, 0)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Value` (r:1 w:0)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
/// Storage: `Pov::Value2` (r:1 w:0)
/// Proof: `Pov::Value2` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Ignored`)
fn storage_single_value_ignored_some_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `35`
// Estimated: `1489`
// Minimum execution time: 3_713_000 picoseconds.
Weight::from_parts(3_806_000, 1489)
.saturating_add(T::DbWeight::get().reads(2_u64))
}
/// Storage: `Pov::Value` (r:1 w:0)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn storage_single_value_read_twice() -> Weight {
// Proof Size summary in bytes:
// Measured: `8`
// Estimated: `1489`
// Minimum execution time: 2_252_000 picoseconds.
Weight::from_parts(2_394_000, 1489)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Value` (r:0 w:1)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn storage_single_value_write() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 376_000 picoseconds.
Weight::from_parts(448_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `Pov::Value` (r:0 w:1)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn storage_single_value_kill() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 311_000 picoseconds.
Weight::from_parts(371_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`)
fn storage_1m_map_read_one_value_two_additional_layers() -> Weight {
// Proof Size summary in bytes:
// Measured: `1182`
// Estimated: `4647`
// Minimum execution time: 11_814_000 picoseconds.
Weight::from_parts(12_286_000, 4647)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`)
fn storage_1m_map_read_one_value_three_additional_layers() -> Weight {
// Proof Size summary in bytes:
// Measured: `1485`
// Estimated: `4950`
// Minimum execution time: 15_590_000 picoseconds.
Weight::from_parts(16_034_000, 4950)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`)
fn storage_1m_map_read_one_value_four_additional_layers() -> Weight {
// Proof Size summary in bytes:
// Measured: `2018`
// Estimated: `5483`
// Minimum execution time: 14_940_000 picoseconds.
Weight::from_parts(15_410_000, 5483)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Map1M` (r:100 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// Storage: `Pov::Map16M` (r:100 w:0)
/// Proof: `Pov::Map16M` (`max_values`: Some(16000000), `max_size`: Some(36), added: 3006, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 100]`.
/// The range of component `m` is `[0, 100]`.
fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `390 + m * (188 ±0) + n * (188 ±0)`
// Estimated: `990 + m * (2511 ±0) + n * (3006 ±0)`
// Minimum execution time: 476_299_000 picoseconds.
Weight::from_parts(291_597_275, 990)
// Standard Error: 33_182
.saturating_add(Weight::from_parts(2_645_788, 0).saturating_mul(n.into()))
// Standard Error: 33_182
.saturating_add(Weight::from_parts(2_663_663, 0).saturating_mul(m.into()))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into())))
.saturating_add(Weight::from_parts(0, 2511).saturating_mul(m.into()))
.saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into()))
}
/// Storage: `Pov::Map1M` (r:100 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Ignored`)
/// Storage: `Pov::Map16M` (r:100 w:0)
/// Proof: `Pov::Map16M` (`max_values`: Some(16000000), `max_size`: Some(36), added: 3006, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 100]`.
/// The range of component `m` is `[0, 100]`.
fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `390 + m * (188 ±0) + n * (188 ±0)`
// Estimated: `1560 + m * (189 ±0) + n * (3006 ±0)`
// Minimum execution time: 478_548_000 picoseconds.
Weight::from_parts(286_747_135, 1560)
// Standard Error: 32_978
.saturating_add(Weight::from_parts(2_691_677, 0).saturating_mul(n.into()))
// Standard Error: 32_978
.saturating_add(Weight::from_parts(2_700_753, 0).saturating_mul(m.into()))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into())))
.saturating_add(Weight::from_parts(0, 189).saturating_mul(m.into()))
.saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into()))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 100]`.
fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `41`
// Estimated: `3501`
// Minimum execution time: 166_000 picoseconds.
Weight::from_parts(1_973_792, 3501)
// Standard Error: 952
.saturating_add(Weight::from_parts(1_147_407, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Map1M` (r:100 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 100]`.
fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `18 + n * (40 ±0)`
// Estimated: `990 + n * (2511 ±0)`
// Minimum execution time: 161_000 picoseconds.
Weight::from_parts(187_000, 990)
// Standard Error: 3_863
.saturating_add(Weight::from_parts(13_643_264, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(Weight::from_parts(0, 2511).saturating_mul(n.into()))
}
/// Storage: `Pov::DoubleMap1M` (r:1024 w:0)
/// Proof: `Pov::DoubleMap1M` (`max_values`: Some(1000000), `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 1024]`.
fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `21809 + n * (57 ±0)`
// Estimated: `990 + n * (2543 ±0)`
// Minimum execution time: 320_000 picoseconds.
Weight::from_parts(88_877_073, 990)
// Standard Error: 3_857
.saturating_add(Weight::from_parts(4_894_961, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(Weight::from_parts(0, 2543).saturating_mul(n.into()))
}
/// Storage: `Pov::BoundedValue` (r:1 w:0)
/// Proof: `Pov::BoundedValue` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
fn storage_value_bounded_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `1518`
// Minimum execution time: 670_000 picoseconds.
Weight::from_parts(720_000, 1518)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::UnboundedValue` (r:1 w:0)
/// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn storage_value_unbounded_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `1485`
// Minimum execution time: 655_000 picoseconds.
Weight::from_parts(698_000, 1485)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::UnboundedValue` (r:1 w:0)
/// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Ignored`)
fn storage_value_unbounded_ignored_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 680_000 picoseconds.
Weight::from_parts(713_000, 0)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::UnboundedValue` (r:1 w:0)
/// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Pov::BoundedValue` (r:1 w:0)
/// Proof: `Pov::BoundedValue` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
fn storage_value_bounded_and_unbounded_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `534`
// Estimated: `2019`
// Minimum execution time: 4_310_000 picoseconds.
Weight::from_parts(4_559_000, 2019)
.saturating_add(T::DbWeight::get().reads(2_u64))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// The range of component `l` is `[0, 4194304]`.
fn measured_storage_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `13 + l * (1 ±0)`
// Estimated: `1497 + l * (1 ±0)`
// Minimum execution time: 1_872_000 picoseconds.
Weight::from_parts(1_929_000, 1497)
// Standard Error: 132
.saturating_add(Weight::from_parts(11_970, 0).saturating_mul(l.into()))
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(Weight::from_parts(0, 1).saturating_mul(l.into()))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// The range of component `l` is `[0, 4194304]`.
fn mel_storage_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `13 + l * (1 ±0)`
// Estimated: `4195793`
// Minimum execution time: 1_826_000 picoseconds.
Weight::from_parts(1_899_000, 4195793)
// Standard Error: 135
.saturating_add(Weight::from_parts(12_037, 0).saturating_mul(l.into()))
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// Storage: `Pov::LargeValue2` (r:1 w:0)
/// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// The range of component `l` is `[0, 4194304]`.
fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `46 + l * (2 ±0)`
// Estimated: `1530 + l * (2 ±0)`
// Minimum execution time: 3_663_000 picoseconds.
Weight::from_parts(3_837_000, 1530)
// Standard Error: 265
.saturating_add(Weight::from_parts(23_779, 0).saturating_mul(l.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into()))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// Storage: `Pov::LargeValue2` (r:1 w:0)
/// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// The range of component `l` is `[0, 4194304]`.
fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `46 + l * (2 ±0)`
// Estimated: `4195793`
// Minimum execution time: 3_717_000 picoseconds.
Weight::from_parts(3_763_000, 4195793)
// Standard Error: 265
.saturating_add(Weight::from_parts(23_782, 0).saturating_mul(l.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// Storage: `Pov::LargeValue2` (r:1 w:0)
/// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// The range of component `l` is `[0, 4194304]`.
fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `46 + l * (2 ±0)`
// Estimated: `4195793 + l * (2 ±0)`
// Minimum execution time: 3_720_000 picoseconds.
Weight::from_parts(3_809_000, 4195793)
// Standard Error: 266
.saturating_add(Weight::from_parts(23_788, 0).saturating_mul(l.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into()))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// Storage: `Pov::LargeValue2` (r:1 w:0)
/// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// The range of component `l` is `[0, 4194304]`.
fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `46 + l * (2 ±0)`
// Estimated: `4195793 + l * (2 ±0)`
// Minimum execution time: 3_660_000 picoseconds.
Weight::from_parts(3_792_000, 4195793)
// Standard Error: 266
.saturating_add(Weight::from_parts(23_795, 0).saturating_mul(l.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into()))
}
/// Storage: `Pov::UnboundedMap` (r:1 w:0)
/// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Pov::UnboundedMap2` (r:1 w:0)
/// Proof: `Pov::UnboundedMap2` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `i` is `[0, 1000]`.
fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `104 + i * (8 ±0)`
// Estimated: `3568 + i * (8 ±0)`
// Minimum execution time: 7_143_000 picoseconds.
Weight::from_parts(7_945_447, 3568)
// Standard Error: 27
.saturating_add(Weight::from_parts(2_983, 0).saturating_mul(i.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 8).saturating_mul(i.into()))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// Storage: `Pov::UnboundedMap` (r:1 w:0)
/// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `i` is `[0, 1000]`.
fn storage_map_partial_unbounded_read(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `103 + i * (4 ±0)`
// Estimated: `3567 + i * (4 ±0)`
// Minimum execution time: 7_254_000 picoseconds.
Weight::from_parts(7_919_811, 3567)
// Standard Error: 22
.saturating_add(Weight::from_parts(1_670, 0).saturating_mul(i.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into()))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// Storage: `Pov::UnboundedMap` (r:1 w:0)
/// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Ignored`)
/// The range of component `i` is `[0, 1000]`.
fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `103 + i * (4 ±0)`
// Estimated: `3501 + i * (4 ±0)`
// Minimum execution time: 7_195_000 picoseconds.
Weight::from_parts(7_998_073, 3501)
// Standard Error: 23
.saturating_add(Weight::from_parts(1_576, 0).saturating_mul(i.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into()))
}
fn emit_event() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 3_961_000 picoseconds.
Weight::from_parts(4_092_000, 0)
}
fn noop() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_607_000 picoseconds.
Weight::from_parts(1_673_000, 0)
}
/// Storage: `Pov::UnboundedMapTwox` (r:65001 w:0)
/// Proof: `Pov::UnboundedMapTwox` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn storage_iteration() -> Weight {
// Proof Size summary in bytes:
// Measured: `17985119`
// Estimated: `178863584`
// Minimum execution time: 305_702_942_000 picoseconds.
Weight::from_parts(311_508_079_000, 178863584)
.saturating_add(T::DbWeight::get().reads(65001_u64))
}
/// Storage: UNKNOWN KEY `0x6b657932` (r:0 w:1)
/// Proof: UNKNOWN KEY `0x6b657932` (r:0 w:1)
/// The range of component `i` is `[0, 10]`.
fn storage_root_is_the_same_every_time(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 359_000 picoseconds.
Weight::from_parts(422_427, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
}
// For backwards compatibility and tests.
impl WeightInfo for () {
/// Storage: `Pov::Value` (r:1 w:0)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn storage_single_value_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `8`
// Estimated: `1489`
// Minimum execution time: 1_817_000 picoseconds.
Weight::from_parts(1_881_000, 1489)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Value` (r:1 w:0)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Ignored`)
fn storage_single_value_ignored_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `8`
// Estimated: `0`
// Minimum execution time: 1_782_000 picoseconds.
Weight::from_parts(1_910_000, 0)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Value` (r:1 w:0)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
/// Storage: `Pov::Value2` (r:1 w:0)
/// Proof: `Pov::Value2` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Ignored`)
fn storage_single_value_ignored_some_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `35`
// Estimated: `1489`
// Minimum execution time: 3_713_000 picoseconds.
Weight::from_parts(3_806_000, 1489)
.saturating_add(RocksDbWeight::get().reads(2_u64))
}
/// Storage: `Pov::Value` (r:1 w:0)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn storage_single_value_read_twice() -> Weight {
// Proof Size summary in bytes:
// Measured: `8`
// Estimated: `1489`
// Minimum execution time: 2_252_000 picoseconds.
Weight::from_parts(2_394_000, 1489)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Value` (r:0 w:1)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn storage_single_value_write() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 376_000 picoseconds.
Weight::from_parts(448_000, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `Pov::Value` (r:0 w:1)
/// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn storage_single_value_kill() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 311_000 picoseconds.
Weight::from_parts(371_000, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`)
fn storage_1m_map_read_one_value_two_additional_layers() -> Weight {
// Proof Size summary in bytes:
// Measured: `1182`
// Estimated: `4647`
// Minimum execution time: 11_814_000 picoseconds.
Weight::from_parts(12_286_000, 4647)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`)
fn storage_1m_map_read_one_value_three_additional_layers() -> Weight {
// Proof Size summary in bytes:
// Measured: `1485`
// Estimated: `4950`
// Minimum execution time: 15_590_000 picoseconds.
Weight::from_parts(16_034_000, 4950)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`)
fn storage_1m_map_read_one_value_four_additional_layers() -> Weight {
// Proof Size summary in bytes:
// Measured: `2018`
// Estimated: `5483`
// Minimum execution time: 14_940_000 picoseconds.
Weight::from_parts(15_410_000, 5483)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Map1M` (r:100 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// Storage: `Pov::Map16M` (r:100 w:0)
/// Proof: `Pov::Map16M` (`max_values`: Some(16000000), `max_size`: Some(36), added: 3006, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 100]`.
/// The range of component `m` is `[0, 100]`.
fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `390 + m * (188 ±0) + n * (188 ±0)`
// Estimated: `990 + m * (2511 ±0) + n * (3006 ±0)`
// Minimum execution time: 476_299_000 picoseconds.
Weight::from_parts(291_597_275, 990)
// Standard Error: 33_182
.saturating_add(Weight::from_parts(2_645_788, 0).saturating_mul(n.into()))
// Standard Error: 33_182
.saturating_add(Weight::from_parts(2_663_663, 0).saturating_mul(m.into()))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into())))
.saturating_add(Weight::from_parts(0, 2511).saturating_mul(m.into()))
.saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into()))
}
/// Storage: `Pov::Map1M` (r:100 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Ignored`)
/// Storage: `Pov::Map16M` (r:100 w:0)
/// Proof: `Pov::Map16M` (`max_values`: Some(16000000), `max_size`: Some(36), added: 3006, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 100]`.
/// The range of component `m` is `[0, 100]`.
fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `390 + m * (188 ±0) + n * (188 ±0)`
// Estimated: `1560 + m * (189 ±0) + n * (3006 ±0)`
// Minimum execution time: 478_548_000 picoseconds.
Weight::from_parts(286_747_135, 1560)
// Standard Error: 32_978
.saturating_add(Weight::from_parts(2_691_677, 0).saturating_mul(n.into()))
// Standard Error: 32_978
.saturating_add(Weight::from_parts(2_700_753, 0).saturating_mul(m.into()))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into())))
.saturating_add(Weight::from_parts(0, 189).saturating_mul(m.into()))
.saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into()))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 100]`.
fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `41`
// Estimated: `3501`
// Minimum execution time: 166_000 picoseconds.
Weight::from_parts(1_973_792, 3501)
// Standard Error: 952
.saturating_add(Weight::from_parts(1_147_407, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::Map1M` (r:100 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 100]`.
fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `18 + n * (40 ±0)`
// Estimated: `990 + n * (2511 ±0)`
// Minimum execution time: 161_000 picoseconds.
Weight::from_parts(187_000, 990)
// Standard Error: 3_863
.saturating_add(Weight::from_parts(13_643_264, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(Weight::from_parts(0, 2511).saturating_mul(n.into()))
}
/// Storage: `Pov::DoubleMap1M` (r:1024 w:0)
/// Proof: `Pov::DoubleMap1M` (`max_values`: Some(1000000), `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 1024]`.
fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `21809 + n * (57 ±0)`
// Estimated: `990 + n * (2543 ±0)`
// Minimum execution time: 320_000 picoseconds.
Weight::from_parts(88_877_073, 990)
// Standard Error: 3_857
.saturating_add(Weight::from_parts(4_894_961, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(Weight::from_parts(0, 2543).saturating_mul(n.into()))
}
/// Storage: `Pov::BoundedValue` (r:1 w:0)
/// Proof: `Pov::BoundedValue` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
fn storage_value_bounded_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `1518`
// Minimum execution time: 670_000 picoseconds.
Weight::from_parts(720_000, 1518)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::UnboundedValue` (r:1 w:0)
/// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn storage_value_unbounded_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `1485`
// Minimum execution time: 655_000 picoseconds.
Weight::from_parts(698_000, 1485)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::UnboundedValue` (r:1 w:0)
/// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Ignored`)
fn storage_value_unbounded_ignored_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 680_000 picoseconds.
Weight::from_parts(713_000, 0)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::UnboundedValue` (r:1 w:0)
/// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Pov::BoundedValue` (r:1 w:0)
/// Proof: `Pov::BoundedValue` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
fn storage_value_bounded_and_unbounded_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `534`
// Estimated: `2019`
// Minimum execution time: 4_310_000 picoseconds.
Weight::from_parts(4_559_000, 2019)
.saturating_add(RocksDbWeight::get().reads(2_u64))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// The range of component `l` is `[0, 4194304]`.
fn measured_storage_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `13 + l * (1 ±0)`
// Estimated: `1497 + l * (1 ±0)`
// Minimum execution time: 1_872_000 picoseconds.
Weight::from_parts(1_929_000, 1497)
// Standard Error: 132
.saturating_add(Weight::from_parts(11_970, 0).saturating_mul(l.into()))
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(Weight::from_parts(0, 1).saturating_mul(l.into()))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// The range of component `l` is `[0, 4194304]`.
fn mel_storage_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `13 + l * (1 ±0)`
// Estimated: `4195793`
// Minimum execution time: 1_826_000 picoseconds.
Weight::from_parts(1_899_000, 4195793)
// Standard Error: 135
.saturating_add(Weight::from_parts(12_037, 0).saturating_mul(l.into()))
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// Storage: `Pov::LargeValue2` (r:1 w:0)
/// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// The range of component `l` is `[0, 4194304]`.
fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `46 + l * (2 ±0)`
// Estimated: `1530 + l * (2 ±0)`
// Minimum execution time: 3_663_000 picoseconds.
Weight::from_parts(3_837_000, 1530)
// Standard Error: 265
.saturating_add(Weight::from_parts(23_779, 0).saturating_mul(l.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into()))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// Storage: `Pov::LargeValue2` (r:1 w:0)
/// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// The range of component `l` is `[0, 4194304]`.
fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `46 + l * (2 ±0)`
// Estimated: `4195793`
// Minimum execution time: 3_717_000 picoseconds.
Weight::from_parts(3_763_000, 4195793)
// Standard Error: 265
.saturating_add(Weight::from_parts(23_782, 0).saturating_mul(l.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// Storage: `Pov::LargeValue2` (r:1 w:0)
/// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// The range of component `l` is `[0, 4194304]`.
fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `46 + l * (2 ±0)`
// Estimated: `4195793 + l * (2 ±0)`
// Minimum execution time: 3_720_000 picoseconds.
Weight::from_parts(3_809_000, 4195793)
// Standard Error: 266
.saturating_add(Weight::from_parts(23_788, 0).saturating_mul(l.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into()))
}
/// Storage: `Pov::LargeValue` (r:1 w:0)
/// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`)
/// Storage: `Pov::LargeValue2` (r:1 w:0)
/// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`)
/// The range of component `l` is `[0, 4194304]`.
fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `46 + l * (2 ±0)`
// Estimated: `4195793 + l * (2 ±0)`
// Minimum execution time: 3_660_000 picoseconds.
Weight::from_parts(3_792_000, 4195793)
// Standard Error: 266
.saturating_add(Weight::from_parts(23_795, 0).saturating_mul(l.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into()))
}
/// Storage: `Pov::UnboundedMap` (r:1 w:0)
/// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Pov::UnboundedMap2` (r:1 w:0)
/// Proof: `Pov::UnboundedMap2` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `i` is `[0, 1000]`.
fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `104 + i * (8 ±0)`
// Estimated: `3568 + i * (8 ±0)`
// Minimum execution time: 7_143_000 picoseconds.
Weight::from_parts(7_945_447, 3568)
// Standard Error: 27
.saturating_add(Weight::from_parts(2_983, 0).saturating_mul(i.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 8).saturating_mul(i.into()))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// Storage: `Pov::UnboundedMap` (r:1 w:0)
/// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `i` is `[0, 1000]`.
fn storage_map_partial_unbounded_read(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `103 + i * (4 ±0)`
// Estimated: `3567 + i * (4 ±0)`
// Minimum execution time: 7_254_000 picoseconds.
Weight::from_parts(7_919_811, 3567)
// Standard Error: 22
.saturating_add(Weight::from_parts(1_670, 0).saturating_mul(i.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into()))
}
/// Storage: `Pov::Map1M` (r:1 w:0)
/// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
/// Storage: `Pov::UnboundedMap` (r:1 w:0)
/// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Ignored`)
/// The range of component `i` is `[0, 1000]`.
fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `103 + i * (4 ±0)`
// Estimated: `3501 + i * (4 ±0)`
// Minimum execution time: 7_195_000 picoseconds.
Weight::from_parts(7_998_073, 3501)
// Standard Error: 23
.saturating_add(Weight::from_parts(1_576, 0).saturating_mul(i.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into()))
}
fn emit_event() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 3_961_000 picoseconds.
Weight::from_parts(4_092_000, 0)
}
fn noop() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_607_000 picoseconds.
Weight::from_parts(1_673_000, 0)
}
/// Storage: `Pov::UnboundedMapTwox` (r:65001 w:0)
/// Proof: `Pov::UnboundedMapTwox` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn storage_iteration() -> Weight {
// Proof Size summary in bytes:
// Measured: `17985119`
// Estimated: `178863584`
// Minimum execution time: 305_702_942_000 picoseconds.
Weight::from_parts(311_508_079_000, 178863584)
.saturating_add(RocksDbWeight::get().reads(65001_u64))
}
/// Storage: UNKNOWN KEY `0x6b657932` (r:0 w:1)
/// Proof: UNKNOWN KEY `0x6b657932` (r:0 w:1)
/// The range of component `i` is `[0, 10]`.
fn storage_root_is_the_same_every_time(_i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 359_000 picoseconds.
Weight::from_parts(422_427, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}
@@ -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()))
}
}