Skip to content
Snippets Groups Projects
Unverified Commit cda5f646 authored by Ermal Kaleci's avatar Ermal Kaleci Committed by GitHub
Browse files

New weight meter PR (#478)


* init weight_meter

* update meter static var

* update workspace

* fmt

* clippy allow unused imports

* default std

* update cargo

* remove std

* revert until fix no_std issue

* improvements

* fmt

* update argument extract

* package name with orml prefix

* update meter to be Mutex

* use mutex only in std

* update README

* update README

* update property name

* separate method injector

* docs

* fmt

* clippy

* Orml bencher (#452)

* Use MultiLocation as xtokens transfer dest type. (#396)

* Use MultiLocation as xtokens transfer dest type.

* Make clippy happy.

* Use xcm-handler to execute XCM locally (#401)

* Use cumulus xcm-handler to execute XCM locally.

* Add docstring for xtokens config.

* Replace XcmError::Undefined usage. (#402)

* Replace XcmError::Undefined usage.

* make clippy happy

* Bump and unify serde versions. (#403)

* xtokens and xcm-support documentations (#404)

* Add xtokens module docstring.

* Add xtokens readme.

* Add documentations for xcm-support.

* Add xtokens and xcm-support entries in main readme.

* Add unit tests for xcm-support. (#405)

* Added Minterest to the list of users. (#406)

* update step guide link

* Handle unknown assets in TransactAsset impl (#409)

* Handle unknown assets in TransactAsset impl.

* More documentations.

* Clean code.

* Renaming.

* Should try to deposit known asset first.

* Return error if no UnknownAsset impl.

* Make clippy happy.

* Fix description and repo link. (#410)

* Unknown tokens pallet (#411)

* Impl unknown tokens pallet.

* Fix workspace pallet path.

* Make clippy happy.

* Clippy, be happy.

* Unit tests.

* Remove nonces from oracle pallet. (#413)

* refactor rewards (#412)

* Bump rococo v1 dependencies (#418)

* Fix mocks.

* Replace deprecated.

* Update orml-unknown-tokens unit tests. (#421)

* add build-script-utils from Substrate (#422)

* Update README.md (#420)

* Update README.md

* Update README.md

* Bump impl-trait-for-tuples to 0.2.1 (#424)

* update Cargo.toml (#429)

* bencher init commit

* split into files

* clean deps

* add docs

* use frame_benchmarking apis

* add macro bencher_use to export stuff for bench_runner

* * generate weights file
* refactor

* improvements

* CLI Options (#446)

* Added command line options for output, headers, and templates

* Fixed options to conform to standard cli

* Added weight-gen cli

* fixed dependencies

* Replaced unwraps with expects (#449)

* Orml bencher (#451)

* add Handler (#431)

* remove disable-tokens-by-owner (#434)

* Cross-chain transfer rework (#432)

* Reserve chain trait.

* Rework cross-chain transfer.

* Remove relay chain balance convert.

* Add 'Parse' trait.

* Change transfer_multiasset fn signature.

* Add transfer dispatchable call.

* Update doc.

* Use xcm-simulator to mock network.

* Send relay chain asset to sibling unit test.

* Move location traits into orml-traits.

* Add MultiNativeAsset filter for is reserve check.

* More unit tests.

* Failing edge case unit tests.

* Handle zero amount asset case.

* Fix mocks.

* Renaming.

* Update currency adapter to work with new xtokens impl (#436)

* Xcm support implementations rework.

* Update xtokens mock.

* Use CurrencyId convert. (#437)

* Use CurrencyId convert.

* Apply review suggestions.

* Update xtokens docs. (#438)

* Update xtokens docs.

* Fix typo.

* Update imbalances impl.

* Don't deposit failure event in orml-unknown-tokens. (#440)

* Don't deposit failure event in orml-unknown-tokens.

* Patch substrate/polkadot/cumulus.

* Fix patch.

* Update README.md (#441)

Include Zeitgeist into "Projects using ORML" section

* Add PoV size in benchmarking. (#442)

* Bump cumulus ref in cargo patch. (#443)

* fix missing features (#444)

* fix missing features

* test with benchmarks

* update auction weight (#445)

* Bump dependencies. (#448)

* Replaced unwraps with expects

Co-authored-by: default avatarXiliang Chen <xlchen1291@gmail.com>
Co-authored-by: default avatarShaun Wang <spxwang@gmail.com>
Co-authored-by: default avatarHarald Heckmann <harald.heckmann93@web.de>
Co-authored-by: default avatarwangjj9219 <183318287@qq.com>

Co-authored-by: default avatarShaun Wang <spxwang@gmail.com>
Co-authored-by: default avatardzianis.ramanouski <DenisRomanovsky@users.noreply.github.com>
Co-authored-by: default avatarBette <42193328+bette7@users.noreply.github.com>
Co-authored-by: default avatarwangjj9219 <183318287@qq.com>
Co-authored-by: default avatarXiliang Chen <xlchen1291@gmail.com>
Co-authored-by: default avatartransxask <68648225+transxask@users.noreply.github.com>
Co-authored-by: default avatarAaro Perämaa <aaro.peramaa@gmail.com>
Co-authored-by: default avatarErmal Kaleci <ermalkaleci@gmail.com>
Co-authored-by: default avatarHarald Heckmann <harald.heckmann93@web.de>

* fix bencher dependencies

* Weight meter (#459)

* Added argument to BenchmarkingState

* fix

* Updated frame-support version (#461)

* clippy

* fmt

* fix

* switched to thread_local

* Added checked add

* corrected versions

* weight_meter tests

* Removed starting weight

* Removed method_benchmark/updated inner docs

* clippy and fmt

* changed start_with to start

* Changed start_with to start in macro

* cleanup

* refactor

* fix clippy

Co-authored-by: default avatarbrettkolodny <brettkolodny@gmail.com>
Co-authored-by: default avatarShaun Wang <spxwang@gmail.com>
Co-authored-by: default avatardzianis.ramanouski <DenisRomanovsky@users.noreply.github.com>
Co-authored-by: default avatarBette <42193328+bette7@users.noreply.github.com>
Co-authored-by: default avatarwangjj9219 <183318287@qq.com>
Co-authored-by: default avatarXiliang Chen <xlchen1291@gmail.com>
Co-authored-by: default avatartransxask <68648225+transxask@users.noreply.github.com>
Co-authored-by: default avatarAaro Perämaa <aaro.peramaa@gmail.com>
Co-authored-by: default avatarHarald Heckmann <harald.heckmann93@web.de>
parent 8825fe43
No related branches found
No related tags found
No related merge requests found
Showing
with 1086 additions and 0 deletions
...@@ -4,6 +4,7 @@ cargo-features = ["resolver"] ...@@ -4,6 +4,7 @@ cargo-features = ["resolver"]
members = [ members = [
"auction", "auction",
"authority", "authority",
"bencher",
"benchmarking", "benchmarking",
"currencies", "currencies",
"gradually-update", "gradually-update",
...@@ -20,6 +21,8 @@ members = [ ...@@ -20,6 +21,8 @@ members = [
# "xcm-support", # "xcm-support",
# "unknown-tokens", # "unknown-tokens",
"build-script-utils", "build-script-utils",
"weight-gen",
"weight-meter",
] ]
resolver = "2" resolver = "2"
...@@ -67,6 +70,7 @@ sp-authority-discovery = { git = "https://github.com/paritytech//substrate", rev ...@@ -67,6 +70,7 @@ sp-authority-discovery = { git = "https://github.com/paritytech//substrate", rev
sc-executor-common = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" } sc-executor-common = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" }
sc-executor-wasmi = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" } sc-executor-wasmi = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" }
sc-executor = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" } sc-executor = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" }
sc-client-db = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" }
sc-client-api = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" } sc-client-api = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" }
sp-tasks = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" } sp-tasks = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" }
sp-authorship = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" } sp-authorship = { git = "https://github.com/paritytech//substrate", rev = "3f110196163b5ec03bac5ee188d60bedf3ebd91d" }
......
[package]
name = "orml-bencher"
description = "Provide macro to benchmark pallets."
repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/bencher"
license = "Apache-2.0"
version = "0.4.1-dev"
authors = ["Laminar Developers <hello@laminar.one>"]
edition = "2018"
[dependencies]
linregress = { version = "0.4.0", optional = true }
serde = { version = "1.0.119", optional = true, features = ['derive'] }
serde_json = {version = "1.0.64", optional = true }
codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"], default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }
sp-runtime-interface = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }
sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false, optional = true }
sc-executor = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false, features = ["wasmtime"], optional = true }
sc-client-db = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false, features = ["with-kvdb-rocksdb"], optional = true }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }
[features]
default = ["std"]
std = [
"linregress",
"serde/std",
"serde_json/std",
"codec/std",
"sp-core/std",
"sp-std/std",
"sp-io/std",
"sp-runtime-interface/std",
"sp-state-machine/std",
"sc-executor/std",
"sc-client-db",
"frame-benchmarking/std",
]
use frame_benchmarking::{
benchmarking,
frame_support::sp_runtime::traits::{Block, NumberFor},
};
use sc_client_db::BenchmarkingState;
use sc_executor::{sp_wasm_interface::HostFunctions, WasmExecutionMethod, WasmExecutor};
use sp_core::traits::{CallInWasm, MissingHostFunctions};
use sp_io::SubstrateHostFunctions;
use sp_state_machine::{Ext, OverlayedChanges, StorageTransactionCache};
/// Run benches
pub fn run<B: Block>(wasm_code: Vec<u8>) -> Vec<u8> {
let mut overlay = OverlayedChanges::default();
let mut cache = StorageTransactionCache::default();
let state = BenchmarkingState::<B>::new(Default::default(), Default::default(), false).unwrap();
let mut ext = Ext::<_, NumberFor<B>, _>::new(&mut overlay, &mut cache, &state, None, None);
let mut host_functions = benchmarking::HostFunctions::host_functions();
host_functions.append(&mut SubstrateHostFunctions::host_functions());
let executor = WasmExecutor::new(
WasmExecutionMethod::Compiled,
Default::default(),
host_functions,
1,
None,
);
executor
.call_in_wasm(
&wasm_code[..],
None,
"run_benches",
&[],
&mut ext,
MissingHostFunctions::Disallow,
)
.unwrap()
}
use crate::BenchResult;
use codec::Decode;
use linregress::{FormulaRegressionBuilder, RegressionDataBuilder};
use serde::{Deserialize, Serialize};
use std::io::Write;
#[derive(Serialize, Deserialize, Default, Debug, Clone)]
struct BenchData {
pub name: String,
pub base_weight: u64,
pub base_reads: u32,
pub base_writes: u32,
}
/// Handle bench results
pub fn handle(output: Vec<u8>) {
let results = <Vec<BenchResult> as Decode>::decode(&mut &output[..]).unwrap();
let data: Vec<BenchData> = results
.into_iter()
.map(|result| {
let name = String::from_utf8_lossy(&result.method).to_string();
eprintln!("{:#?}", result);
let y: Vec<f64> = result.elapses.into_iter().map(|x| x as f64).collect();
let x: Vec<f64> = (0..y.len()).into_iter().map(|x| x as f64).collect();
let data = vec![("Y", y), ("X", x)];
let data = RegressionDataBuilder::new().build_from(data).unwrap();
let formula = "Y ~ X";
let model = FormulaRegressionBuilder::new()
.data(&data)
.formula(formula)
.fit()
.unwrap();
BenchData {
name,
base_weight: model.parameters.intercept_value as u64 * 1_000,
base_reads: result.reads,
base_writes: result.writes,
}
})
.collect();
if let Ok(json) = serde_json::to_string(&data) {
let stdout = ::std::io::stdout();
let mut handle = stdout.lock();
handle.write_all(&json.as_bytes()).unwrap();
} else {
eprintln!("Could not write benchdata to JSON");
}
}
#![cfg_attr(not(feature = "std"), no_std)]
#[doc(hidden)]
pub extern crate frame_benchmarking;
#[doc(hidden)]
pub extern crate sp_core;
#[doc(hidden)]
pub extern crate sp_std;
use codec::{Decode, Encode};
use sp_std::prelude::Vec;
#[derive(Encode, Decode, Default, Clone, PartialEq, Debug)]
pub struct BenchResult {
pub method: Vec<u8>,
pub elapses: Vec<u128>,
pub reads: u32,
pub repeat_reads: u32,
pub writes: u32,
pub repeat_writes: u32,
}
mod macros;
#[cfg(feature = "std")]
pub mod bench_runner;
#[cfg(feature = "std")]
pub mod handler;
/// Run benches in WASM environment.
///
/// Configure your module to build the mock runtime into wasm code.
/// Create a `build.rs` like you do with your runtime.
/// ```.ignore
/// use substrate_wasm_builder::WasmBuilder;
/// fn main() {
/// WasmBuilder::new()
/// .with_current_project()
/// .export_heap_base()
/// .import_memory()
/// .build()
/// }
/// ```
///
/// Update mock runtime to be build into wasm code.
/// ```.ignore
/// #![cfg_attr(not(feature = "std"), no_std)]
///
/// #[cfg(feature = "std")]
/// include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
///
/// #[cfg(feature = "std")]
/// pub fn wasm_binary_unwrap() -> &'static [u8] { WASM_BINARY.unwrap() }
/// ..
/// ```
///
/// Create a file `bench_runner.rs` with following code:
/// ```.ignore
/// orml_bencher::run_benches!(my_module::benches);
/// ```
///
/// Update Cargo.toml by adding:
/// ```toml
/// ..
/// [package]
/// name = "my-module"
/// ..
/// build = 'build.rs'
///
/// [build-dependencies]
/// substrate-wasm-builder = '4.0.0'
///
/// [[bench]]
/// name = 'benches'
/// harness = false
/// path = 'bench_runner.rs'
/// required-features = ['bench']
///
/// [features]
/// bench = []
/// ..
/// ```
///
/// Run bench with features bench: `cargo bench --features=bench`
#[cfg(feature = "std")]
#[macro_export]
macro_rules! run_benches {
($benches:path) => {
use $benches::{wasm_binary_unwrap, Block};
pub fn main() {
let output = $crate::bench_runner::run::<Block>(wasm_binary_unwrap().to_vec());
$crate::handler::handle(output);
}
};
}
/// Define benches
///
/// Create a file `src/benches.rs`:
/// ```.ignore
/// #![cfg_attr(not(feature = "std"), no_std)]
/// #![allow(dead_code)]
///
/// #[cfg(feature = "std")] // Re-export for bench_runner
/// pub use crate::mock::{Block, wasm_binary_unwrap};
///
/// use crate::mock::YourModule;
///
/// fn foo(b: &mut Bencher) {
/// b.bench("foo", || {
/// YourModule::foo();
/// });
/// }
///
/// fn bar(b: &mut Bencher) {
/// b.bench("bar", || {
/// YourModule::bar();
/// });
/// }
///
/// orml_bencher::bench!(foo, bar);
/// ```
/// Update `src/lib.rs`:
/// ```.ignore
/// #[cfg(any(feature = "bench", test))]
/// pub mod mock; /* mock runtime needs to be compiled into wasm */
/// #[cfg(feature = "bench")]
/// pub mod benches;
/// ```
#[macro_export]
macro_rules! bench {
(
$($method:path),+
) => {
use $crate::BenchResult;
use $crate::sp_std::{cmp::max, prelude::Vec};
use $crate::frame_benchmarking::{benchmarking, BenchmarkResults};
#[derive(Default, Clone, PartialEq, Debug)]
struct Bencher {
pub results: Vec<BenchResult>,
}
impl Bencher {
pub fn bench<F: Fn() -> ()>(&mut self, name: &str, block: F) {
// Warm up the DB
benchmarking::commit_db();
benchmarking::wipe_db();
let mut result = BenchResult {
method: name.as_bytes().to_vec(),
..Default::default()
};
for _ in 0..50 {
benchmarking::commit_db();
benchmarking::reset_read_write_count();
let start_time = benchmarking::current_time();
block();
let end_time = benchmarking::current_time();
let elasped = end_time - start_time;
result.elapses.push(elasped);
benchmarking::commit_db();
let (reads, repeat_reads, writes, repeat_writes) = benchmarking::read_write_count();
result.reads = max(result.reads, reads);
result.repeat_reads = max(result.repeat_reads, repeat_reads);
result.writes = max(result.writes, writes);
result.repeat_writes = max(result.repeat_writes, repeat_writes);
benchmarking::wipe_db();
}
self.results.push(result);
}
}
$crate::sp_core::wasm_export_functions! {
fn run_benches() -> Vec<BenchResult> {
let mut bencher = Bencher::default();
$(
$method(&mut bencher);
)+
bencher.results
}
}
}
}
[package]
name = "weight-gen"
description = "CLI for generating weight from bencher output"
license = "Apache-2.0"
version = "0.4.1-dev"
authors = ["Laminar Developers <hello@laminar.one>"]
edition = "2018"
[dependencies]
serde = { version = "1.0.119", features = ['derive'] }
serde_json = "1.0"
clap = "3.0.0-beta.2"
handlebars = { version = "3.5.2" }
[features]
default = ["std"]
std = []
use clap::{AppSettings, Clap};
use serde::{Deserialize, Serialize};
use std::io::Read;
#[derive(Clap)]
#[clap(version = "01.0", author = "Laminar Developers <hello@laminar.one>")]
#[clap(setting = AppSettings::ColoredHelp)]
struct Opts {
input: Option<String>,
#[clap(short, long)]
template: Option<String>,
#[clap(short, long)]
header: Option<String>,
#[clap(short, long)]
out: Option<String>,
}
#[derive(Serialize, Deserialize, Default, Debug, Clone)]
pub struct BenchData {
pub name: String,
pub base_weight: u64,
pub base_reads: u32,
pub base_writes: u32,
}
#[derive(Serialize, Default, Debug, Clone)]
struct TemplateData {
pub header: String,
pub benchmarks: Vec<BenchData>,
}
// A Handlebars helper to add an underscore after every 3rd character,
// i.e. a separator for large numbers.
#[derive(Clone, Copy)]
struct UnderscoreHelper;
impl handlebars::HelperDef for UnderscoreHelper {
fn call<'reg: 'rc, 'rc>(
&self,
h: &handlebars::Helper,
_: &handlebars::Handlebars,
_: &handlebars::Context,
_rc: &mut handlebars::RenderContext,
out: &mut dyn handlebars::Output,
) -> handlebars::HelperResult {
use handlebars::JsonRender;
let param = h.param(0).expect("Unable to retrieve param from handlebars helper");
let underscore_param = underscore(param.value().render());
out.write(&underscore_param)?;
Ok(())
}
}
// Add an underscore after every 3rd character, i.e. a separator for large
// numbers.
fn underscore<Number>(i: Number) -> String
where
Number: std::string::ToString,
{
let mut s = String::new();
let i_str = i.to_string();
let a = i_str.chars().rev().enumerate();
for (idx, val) in a {
if idx != 0 && idx % 3 == 0 {
s.insert(0, '_');
}
s.insert(0, val);
}
s
}
// A helper to join a string of vectors.
#[derive(Clone, Copy)]
struct JoinHelper;
impl handlebars::HelperDef for JoinHelper {
fn call<'reg: 'rc, 'rc>(
&self,
h: &handlebars::Helper,
_: &handlebars::Handlebars,
_: &handlebars::Context,
_rc: &mut handlebars::RenderContext,
out: &mut dyn handlebars::Output,
) -> handlebars::HelperResult {
use handlebars::JsonRender;
let param = h.param(0).expect("Unable to retrieve param from handlebars helper");
let value = param.value();
let joined = if value.is_array() {
value
.as_array()
.unwrap()
.iter()
.map(|v| v.render())
.collect::<Vec<String>>()
.join(" ")
} else {
value.render()
};
out.write(&joined)?;
Ok(())
}
}
fn parse_stdio() -> Option<Vec<BenchData>> {
let mut buffer = String::new();
let stdin = std::io::stdin();
let mut handle = stdin.lock();
handle.read_to_string(&mut buffer).expect("Unable to read from stdin");
let lines: Vec<&str> = buffer.split('\n').collect();
for line in lines {
let json = serde_json::from_str(line);
if let Ok(data) = json {
return Some(data);
}
}
None
}
fn main() {
let opts: Opts = Opts::parse();
let benchmarks: Vec<BenchData> = {
if let Some(data) = opts.input {
serde_json::from_str(&data).expect("Could not parse JSON data")
} else {
parse_stdio().expect("Could not parse JSON data")
}
};
let mut handlebars = handlebars::Handlebars::new();
handlebars.register_helper("underscore", Box::new(UnderscoreHelper));
handlebars.register_helper("join", Box::new(JoinHelper));
// Don't HTML escape any characters.
handlebars.register_escape_fn(|s| -> String { s.to_string() });
// Use empty header if a header path is not given.
let header = {
if let Some(path) = opts.header {
::std::fs::read_to_string(&path).expect("Header file not found")
} else {
String::from("")
}
};
let hbs_data = TemplateData { header, benchmarks };
const DEFAULT_TEMPLATE: &str = include_str!("./template.hbs");
// Use default template if template path is not given.
let template = {
if let Some(path) = opts.template {
::std::fs::read_to_string(&path).expect("Template file not found")
} else {
String::from(DEFAULT_TEMPLATE)
}
};
// Write benchmark to file or print to terminal if output path is not given.
if let Some(path) = opts.out {
let mut output_file = ::std::fs::File::create(&path).expect("Could not create output file");
handlebars
.render_template_to_write(&template, &hbs_data, &mut output_file)
.expect("Unable to render template");
} else {
let template_string = handlebars
.render_template(&template, &hbs_data)
.expect("Unable to render template");
println!("{}", template_string);
}
}
{{header}}
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(dead_code)]
use frame_support::{traits::Get, weights::Weight};
use sp_std::marker::PhantomData;
pub struct ModuleWeights<T>(PhantomData<T>);
impl<T: frame_system::Config> ModuleWeights<T> {
{{~#each benchmarks as |benchmark|}}
pub fn {{benchmark.name~}} () -> Weight {
({{underscore benchmark.base_weight}} as Weight)
{{~#if (ne benchmark.base_reads "0")}}
.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as Weight))
{{~/if}}
{{~#if (ne benchmark.base_writes "0")}}
.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as Weight))
{{~/if}}
}
{{~/each}}
}
[package]
name = "orml-weight-meter"
version = "0.4.1-dev"
license = "Apache-2.0"
authors = ["Laminar Developers <hello@laminar.one>"]
edition = "2018"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
spin = "0.7.1"
frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false }
weight-meter-procedural = { path = "weight-meter-procedural", default-features = false }
[dev-dependencies]
serde = { version = "1.0.124" }
codec = { package = "parity-scale-codec", version = "2.0.0" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1"}
frame-system = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" }
[features]
default = ["std"]
std = [
"frame-support/std",
"weight-meter-procedural/std",
]
# Weight Meter
Include `WeightMeter` into your module Cargo.toml
```
[dependencies]
orml-weight-meter = { version = "..", default-features = false }
std = [
..
'orml-weight-meter/std',
]
```
\ No newline at end of file
#![cfg_attr(not(feature = "std"), no_std)]
//! 1. Add macro attribute on method you want to benchmark.
//! ```ignore
//! #[orml_weight_meter::weight(0)]
//! fn inner_do_something(something: u32) {
//! // Update storage.
//! Something::<T>::put(something);
//! }
//! ```
//! Start with `0` and after the weights is generated then it can be replaced
//! with generated weight. Macro will inject callable methods that wraps inner
//! methods. Generated call will start with prefix `method_` followed by method
//! name. This only works for methods with `orml_weight_meter::weight` attribute
//! and only when running benchmarks.
//!
//! 2. Create benchmarks using orml_bencher and generate the weights with
//! orml_weight_gen
//! After running the benchmarks and the weights have been generated then we can
//! replace
//! ```ignore
//! #[orml_weight_meter::weight(0)]
//! ```
//! with
//!```ignore
//! #[orml_weight_meter::weight(T::WeightInfo::method_inner_do_something())]
//! ```
//!
//! 3. Use WeightMeter on your calls by adding macro
//! `#[orml_weight_meter::start]` and at the end use
//! `orml_weight_meter::used_weight()` to get used weight.
//!```ignore
//! #[pallet::call]
//! impl<T: Config> Pallet<T> {
//! #[pallet::weight(T::WeightInfo::do_something())]
//! #[orml_weight_meter::start]
//! pub fn do_something(origin: OriginFor<T>, something: u32) ->
//! DispatchResultWithPostInfo {
//! let who = ensure_signed(origin)?;
//! Self::inner_do_something(something);
//! // Emit an event.
//! Self::deposit_event(Event::SomethingStored(something, who));
//! Ok(PostDispatchInfo::from(Some(orml_weight_meter::used_weight())))
//! }
//! }
//! ```
use frame_support::weights::Weight;
struct Meter {
used_weight: Weight,
// Depth gets incremented when entering call or a sub-call
// This is used to avoid miscalculation during sub-calls
depth: u8,
}
mod meter_no_std;
mod meter_std;
// For use in mock file
#[cfg(test)]
extern crate self as orml_weight_meter;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "std")]
pub use meter_std::*;
#[cfg(not(feature = "std"))]
pub use meter_no_std::*;
/// Start weight meter
pub use weight_meter_procedural::start;
/// Measure each methods weight
pub use weight_meter_procedural::weight;
// TODO: research if there's a better way
#![cfg(not(feature = "std"))]
use super::{Meter, Weight};
static mut METER: Meter = Meter {
used_weight: 0,
depth: 0,
};
pub fn start() {
unsafe {
if METER.depth == 0 {
METER.used_weight = 0;
}
METER.depth = METER.depth.saturating_add(1);
}
}
pub fn using(weight: Weight) {
unsafe {
METER.used_weight = METER.used_weight.saturating_add(weight);
}
}
pub fn finish() {
unsafe {
METER.depth.checked_sub(1).map_or_else(
|| {
debug_assert!(false);
0
},
|v| v,
);
}
}
pub fn used_weight() -> Weight {
unsafe { METER.used_weight }
}
// TODO: research if there's a better way
#![cfg(feature = "std")]
use super::{Meter, Weight};
use std::cell::RefCell;
thread_local! {
static METER: RefCell<Meter> = RefCell::new(Meter {
used_weight: 0,
depth: 0,
});
}
/// Start weight meter with base weight
pub fn start() {
METER.with(|v| {
let mut meter = v.borrow_mut();
if meter.depth == 0 {
meter.used_weight = 0;
}
meter.depth = meter.depth.saturating_add(1);
});
}
/// Increment used weight
pub fn using(weight: Weight) {
METER.with(|v| {
let mut meter = v.borrow_mut();
meter.used_weight = meter.used_weight.saturating_add(weight);
})
}
/// Finish weight meter
pub fn finish() {
METER.with(|v| {
let mut meter = v.borrow_mut();
meter.depth = meter.depth.saturating_sub(1);
})
}
/// Get used weight
pub fn used_weight() -> Weight {
METER.with(|v| v.borrow().used_weight)
}
#[frame_support::pallet]
pub mod test_module {
use frame_support::{dispatch::DispatchResultWithPostInfo, pallet_prelude::*, weights::Weight};
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {}
#[pallet::storage]
#[pallet::getter(fn something)]
pub type Something<T> = StorageValue<_, u32>;
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(50_000)]
#[orml_weight_meter::start]
pub fn expect_100(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
Self::put_100();
Ok(Some(orml_weight_meter::used_weight()).into())
}
#[pallet::weight(50_000)]
#[orml_weight_meter::start]
pub fn expect_500(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
Self::put_100();
Self::put_100();
Self::put_100();
Self::put_100();
Self::put_100();
Ok(Some(orml_weight_meter::used_weight()).into())
}
#[pallet::weight(50_000)]
#[orml_weight_meter::start]
pub fn expect_max_weight(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
Self::max_weight();
Self::put_100();
Ok(Some(orml_weight_meter::used_weight()).into())
}
#[pallet::weight(50_000)]
#[orml_weight_meter::start]
pub fn expect_100_or_200(origin: OriginFor<T>, branch: bool) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
if branch {
Self::put_200();
} else {
Self::put_100();
}
Ok(Some(orml_weight_meter::used_weight()).into())
}
#[pallet::weight(50_000)]
#[orml_weight_meter::start]
pub fn nested_inner_methods(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
Self::put_300_nested();
Ok(Some(orml_weight_meter::used_weight()).into())
}
#[pallet::weight(50_000)]
#[orml_weight_meter::start]
pub fn nested_extrinsic(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
ensure_signed(origin.clone())?;
// some module call
Self::put_300_nested();
// call extrinsic method
Self::expect_100(origin)?;
// some module call
Self::put_300_nested();
Ok(Some(orml_weight_meter::used_weight()).into())
}
}
impl<T: Config> Pallet<T> {
#[orml_weight_meter::weight(100)]
fn put_100() {
let something = Self::something();
if let Some(v) = something {
Something::<T>::put(v.checked_add(100).unwrap());
} else {
Something::<T>::put(100);
}
}
#[orml_weight_meter::weight(200)]
fn put_200() {
let something = Self::something();
if let Some(v) = something {
Something::<T>::put(v.checked_add(200).unwrap());
} else {
Something::<T>::put(100);
}
}
#[orml_weight_meter::weight(200)]
fn put_300_nested() {
Self::put_100();
}
#[orml_weight_meter::weight(Weight::MAX)]
fn max_weight() {
return;
}
}
}
use frame_support::sp_runtime::traits::IdentityLookup;
use sp_runtime::testing::{Header, H256};
pub type BlockNumber = u64;
frame_support::parameter_types! {
pub const BlockHashCount: u64 = 250;
}
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
type Block = frame_system::mocking::MockBlock<Runtime>;
type Balance = u128;
impl frame_system::Config for Runtime {
type Origin = Origin;
type Index = u64;
type BlockNumber = BlockNumber;
type Call = Call;
type Hash = H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = BlockHashCount;
type BlockWeights = ();
type BlockLength = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type BaseCallFilter = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
}
frame_support::parameter_types! {
pub const ExistentialDeposit: u64 = 1;
}
impl pallet_balances::Config for Runtime {
type Balance = Balance;
type Event = Event;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = frame_system::Pallet<Runtime>;
type MaxLocks = ();
type WeightInfo = ();
}
impl test_module::Config for Runtime {}
frame_support::construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Storage, Config, Event<T>},
TestModule: test_module::{Pallet, Call, Storage},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
}
);
pub struct ExtBuilder();
impl Default for ExtBuilder {
fn default() -> Self {
Self()
}
}
impl ExtBuilder {
pub fn build(self) -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::default()
.build_storage::<Runtime>()
.unwrap();
pallet_balances::GenesisConfig::<Runtime> {
balances: vec![(100, 100_000)],
}
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
}
pub fn new_test_ext() -> sp_io::TestExternalities {
ExtBuilder::default().build()
}
use crate::mock::*;
use frame_support::weights::PostDispatchInfo;
#[test]
fn used_weight_works() {
new_test_ext().execute_with(|| {
let result: PostDispatchInfo = TestModule::expect_100(Origin::signed(100)).unwrap();
// Check used weight is correct
assert_eq!(Some(100), result.actual_weight);
// Check that the method ran correctly
assert_eq!(Some(100), TestModule::something());
let result: PostDispatchInfo = TestModule::expect_500(Origin::signed(100)).unwrap();
assert_eq!(Some(500), result.actual_weight);
assert_eq!(Some(600), TestModule::something());
});
}
#[test]
fn used_weight_branch_works() {
new_test_ext().execute_with(|| {
let result: PostDispatchInfo = TestModule::expect_100_or_200(Origin::signed(100), false).unwrap();
// Check used weight is correct
assert_eq!(Some(100), result.actual_weight);
// Check that the method ran correctly
assert_eq!(Some(100), TestModule::something());
let result: PostDispatchInfo = TestModule::expect_100_or_200(Origin::signed(100), true).unwrap();
// Check used weight is correct
assert_eq!(Some(200), result.actual_weight);
// Check that the method ran correctly
assert_eq!(Some(300), TestModule::something());
});
}
#[test]
fn used_weight_nested_calls_works() {
new_test_ext().execute_with(|| {
let result: PostDispatchInfo = TestModule::nested_inner_methods(Origin::signed(100)).unwrap();
// Check used weight is correct
assert_eq!(Some(300), result.actual_weight);
});
}
#[test]
fn exceed_max_weight_works() {
new_test_ext().execute_with(|| {
let result: PostDispatchInfo = TestModule::expect_max_weight(Origin::signed(100)).unwrap();
// Check used weight is correct
assert_eq!(Some(u64::MAX), result.actual_weight);
});
}
#[test]
fn nested_module_calls_works() {
new_test_ext().execute_with(|| {
let result = TestModule::nested_extrinsic(Origin::signed(0)).unwrap();
assert_eq!(result.actual_weight, Some(700));
});
}
[package]
name = "weight-meter-procedural"
version = "0.1.0"
license = "Apache-2.0"
authors = ["Laminar Developers <hello@laminar.one>"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.6"
quote = "1.0.3"
syn = { version = "1.0.58", features = ["full"] }
[features]
default = ["std"]
std = []
\ No newline at end of file
use proc_macro::TokenStream;
use quote::quote;
use syn::ItemFn;
#[proc_macro_attribute]
pub fn start(_attr: TokenStream, item: TokenStream) -> TokenStream {
let ItemFn { attrs, vis, sig, block } = syn::parse(item).unwrap();
(quote! {
#(#attrs)*
#vis #sig {
::orml_weight_meter::start();
let result = #block;
::orml_weight_meter::finish();
result
}
})
.into()
}
#[proc_macro_attribute]
pub fn weight(attr: TokenStream, item: TokenStream) -> TokenStream {
let weight: syn::Expr = syn::parse(attr).unwrap();
let ItemFn { attrs, vis, sig, block } = syn::parse(item).unwrap();
(quote! {
#(#attrs)*
#vis #sig {
::orml_weight_meter::using(#weight);
#block
}
})
.into()
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment