mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 09:51:10 +00:00
contracts: Add automated weights for wasm instructions (#7361)
* pallet_contracts: Inline benchmark helper that is only used once * Move all max_* Schedule items into a new struct * Limit the number of globals a module can declare * The current limits are too high for wasmi to even execute * Limit the amount of parameters any wasm function is allowed to have * Limit the size the BrTable's immediate value * Add instruction benchmarks * Add new benchmarks to the schedule and make use of it * Add Benchmark Results generated by the bench bot * Add proc macro that implements `Debug` for `Schedule` * Add missing imports necessary for no_std build * Make the WeightDebug macro available for no_std In this case a dummy implementation is derived in order to not blow up the code size akin to the RuntimeDebug macro. * Rework instr_memory_grow benchmark to use only the maximum amount of pages allowed * Add maximum amount of memory when benching (seal_)call/instantiate * cargo run --release --features runtime-benchmarks --manifest-path bin/node/cli/Cargo.toml -- benchmark --chain dev --steps 50 --repeat 20 --extrinsic * --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output ./bin/node/runtime/src/weights --header ./HEADER --pallet pallet_contracts * Added utility benchmark that allows pretty printing of the real schedule * review: Add missing header to the proc-macro lib.rs * review: Clarify why #[allow(dead_code)] attribute is there * review: Fix pwasm-utils line * review: Fixup rand usage * review: Fix typo * review: Imported -> Exported * cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * contracts: Adapt to new weight structure * contracts: Fixup runtime WeightInfo * contracts: Remove unneeded fullpath of WeightInfo type * Apply suggestions from code review Co-authored-by: Andrew Jones <ascjones@gmail.com> * Fix typo in schedule.rs Co-authored-by: Andrew Jones <ascjones@gmail.com> * Fix docs in schedule.rs * Apply suggestions from code review Co-authored-by: Nikolay Volf <nikvolf@gmail.com> * Don't publish proc-macro crate until 3.0.0 is ready * Optimize imports for less repetition * Break overlong line Co-authored-by: Parity Benchmarking Bot <admin@parity.io> Co-authored-by: Andrew Jones <ascjones@gmail.com> Co-authored-by: Nikolay Volf <nikvolf@gmail.com>
This commit is contained in:
committed by
GitHub
parent
9704c204e6
commit
51c67fe881
@@ -0,0 +1,142 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 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.
|
||||
|
||||
//! Proc macros used in the contracts module.
|
||||
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, Ident};
|
||||
use alloc::string::ToString;
|
||||
|
||||
/// This derives `Debug` for a struct where each field must be of some numeric type.
|
||||
/// It interprets each field as its represents some weight and formats it as times so that
|
||||
/// it is readable by humans.
|
||||
#[proc_macro_derive(WeightDebug)]
|
||||
pub fn derive_weight_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
derive_debug(input, format_weight)
|
||||
}
|
||||
|
||||
/// This is basically identical to the std libs Debug derive but without adding any
|
||||
/// bounds to existing generics.
|
||||
#[proc_macro_derive(ScheduleDebug)]
|
||||
pub fn derive_schedule_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
derive_debug(input, format_default)
|
||||
}
|
||||
|
||||
fn derive_debug(
|
||||
input: proc_macro::TokenStream,
|
||||
fmt: impl Fn(&Ident) -> TokenStream
|
||||
) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = &input.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let data = if let Data::Struct(data) = &input.data {
|
||||
data
|
||||
} else {
|
||||
return quote_spanned! {
|
||||
name.span() =>
|
||||
compile_error!("WeightDebug is only supported for structs.");
|
||||
}.into();
|
||||
};
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
let fields = iterate_fields(data, fmt);
|
||||
|
||||
#[cfg(not(feature = "full"))]
|
||||
let fields = {
|
||||
drop(fmt);
|
||||
drop(data);
|
||||
TokenStream::new()
|
||||
};
|
||||
|
||||
let tokens = quote! {
|
||||
impl #impl_generics core::fmt::Debug for #name #ty_generics #where_clause {
|
||||
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
use ::sp_runtime::{FixedPointNumber, FixedU128 as Fixed};
|
||||
let mut formatter = formatter.debug_struct(stringify!(#name));
|
||||
#fields
|
||||
formatter.finish()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
/// This is only used then the `full` feature is activated.
|
||||
#[cfg(feature = "full")]
|
||||
fn iterate_fields(data: &DataStruct, fmt: impl Fn(&Ident) -> TokenStream) -> TokenStream {
|
||||
match &data.fields {
|
||||
Fields::Named(fields) => {
|
||||
let recurse = fields.named
|
||||
.iter()
|
||||
.filter_map(|f| {
|
||||
let name = f.ident.as_ref()?;
|
||||
if name.to_string().starts_with('_') {
|
||||
return None;
|
||||
}
|
||||
let value = fmt(name);
|
||||
let ret = quote_spanned!{ f.span() =>
|
||||
formatter.field(stringify!(#name), #value);
|
||||
};
|
||||
Some(ret)
|
||||
});
|
||||
quote!{
|
||||
#( #recurse )*
|
||||
}
|
||||
}
|
||||
Fields::Unnamed(fields) => quote_spanned!{
|
||||
fields.span() =>
|
||||
compile_error!("Unnamed fields are not supported")
|
||||
},
|
||||
Fields::Unit => quote!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_weight(field: &Ident) -> TokenStream {
|
||||
quote_spanned! { field.span() =>
|
||||
&if self.#field > 1_000_000_000 {
|
||||
format!(
|
||||
"{:.1?} ms",
|
||||
Fixed::saturating_from_rational(self.#field, 1_000_000_000).to_fraction()
|
||||
)
|
||||
} else if self.#field > 1_000_000 {
|
||||
format!(
|
||||
"{:.1?} µs",
|
||||
Fixed::saturating_from_rational(self.#field, 1_000_000).to_fraction()
|
||||
)
|
||||
} else if self.#field > 1_000 {
|
||||
format!(
|
||||
"{:.1?} ns",
|
||||
Fixed::saturating_from_rational(self.#field, 1_000).to_fraction()
|
||||
)
|
||||
} else {
|
||||
format!("{} ps", self.#field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_default(field: &Ident) -> TokenStream {
|
||||
quote_spanned! { field.span() =>
|
||||
&self.#field
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user