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:
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user