Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 11 additions & 38 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::{
exec::{AccountIdOf, StorageKey},
schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE},
storage::Storage,
wasm::CallFlags,
Pallet as Contracts, *,
};
use codec::{Encode, MaxEncodedLen};
Expand Down Expand Up @@ -1526,44 +1527,21 @@ benchmarks! {
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, callee, 0u32.into(), Weight::MAX, None, vec![])

seal_call_per_transfer_input_output_kb {
seal_call_per_transfer_clone_kb {
let t in 0 .. 1;
let i in 0 .. code::max_pages::<T>() * 64;
let o in 0 .. (code::max_pages::<T>() - 1) * 64;
let callee_code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "seal0",
name: "seal_return",
params: vec![
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
return_type: None,
}],
call_body: Some(body::plain(vec![
Instruction::I32Const(0), // flags
Instruction::I32Const(0), // data_ptr
Instruction::I32Const((o * 1024) as i32), // data_len
Instruction::Call(0),
Instruction::End,
])),
.. Default::default()
});
let c in 0 .. code::max_pages::<T>() * 64;
let callees = (0..API_BENCHMARK_BATCH_SIZE)
.map(|i| Contract::with_index(i + 1, callee_code.clone(), vec![]))
.map(|i| Contract::with_index(i + 1, <WasmModule<T>>::dummy(), vec![]))
.collect::<Result<Vec<_>, _>>()?;
let callee_len = callees.get(0).map(|i| i.account_id.encode().len()).unwrap_or(0);
let callee_bytes = callees.iter().flat_map(|x| x.account_id.encode()).collect::<Vec<_>>();
let callees_len = callee_bytes.len();
let value: BalanceOf<T> = t.into();
let value_bytes = value.encode();
let value_len = value_bytes.len();
let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "seal0",
module: "seal1",
name: "seal_call",
params: vec![
ValueType::I32,
Expand All @@ -1574,7 +1552,6 @@ benchmarks! {
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
return_type: Some(ValueType::I32),
}],
Expand All @@ -1587,29 +1564,25 @@ benchmarks! {
offset: value_len as u32,
value: callee_bytes,
},
DataSegment {
offset: (value_len + callees_len) as u32,
value: (o * 1024).to_le_bytes().into(),
},
],
call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![
Regular(Instruction::I32Const(CallFlags::CLONE_INPUT.bits() as i32)), // flags
Counter(value_len as u32, callee_len as u32), // callee_ptr
Regular(Instruction::I32Const(callee_len as i32)), // callee_len
Regular(Instruction::I64Const(0)), // gas
Regular(Instruction::I32Const(0)), // value_ptr
Regular(Instruction::I32Const(value_len as i32)), // value_len
Regular(Instruction::I32Const(0)), // input_data_ptr
Regular(Instruction::I32Const((i * 1024) as i32)), // input_data_len
Regular(Instruction::I32Const((value_len + callees_len + 4) as i32)), // output_ptr
Regular(Instruction::I32Const((value_len + callees_len) as i32)), // output_len_ptr
Regular(Instruction::I32Const(0)), // input_data_len
Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr
Regular(Instruction::I32Const(0)), // output_len_ptr
Regular(Instruction::Call(0)),
Regular(Instruction::Drop),
])),
.. Default::default()
});
let instance = Contract::<T>::new(code, vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
let bytes = vec![42; (c * 1024) as usize];
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes)

// We assume that every instantiate sends at least the minimum balance.
seal_instantiate {
Expand Down
27 changes: 4 additions & 23 deletions frame/contracts/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,8 @@ pub struct HostFnWeights<T: Config> {
/// Weight surcharge that is claimed if `seal_call` does a balance transfer.
pub call_transfer_surcharge: Weight,

/// Weight per input byte supplied to `seal_call`.
pub call_per_input_byte: Weight,

/// Weight per output byte received through `seal_call`.
pub call_per_output_byte: Weight,
/// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag.
pub call_per_cloned_byte: Weight,

/// Weight of calling `seal_instantiate`.
pub instantiate: Weight,
Expand Down Expand Up @@ -619,24 +616,8 @@ impl<T: Config> Default for HostFnWeights<T> {
transfer: cost_batched!(seal_transfer),
call: cost_batched!(seal_call),
delegate_call: cost_batched!(seal_delegate_call),
call_transfer_surcharge: cost_batched_args!(
seal_call_per_transfer_input_output_kb,
1,
0,
0
),
call_per_input_byte: cost_byte_batched_args!(
seal_call_per_transfer_input_output_kb,
0,
1,
0
),
call_per_output_byte: cost_byte_batched_args!(
seal_call_per_transfer_input_output_kb,
0,
0,
1
),
call_transfer_surcharge: cost_batched_args!(seal_call_per_transfer_clone_kb, 1, 0),
call_per_cloned_byte: cost_batched_args!(seal_call_per_transfer_clone_kb, 0, 1),
instantiate: cost_batched!(seal_instantiate),
instantiate_per_input_byte: cost_byte_batched_args!(
seal_instantiate_per_input_output_salt_kb,
Expand Down
2 changes: 1 addition & 1 deletion frame/contracts/src/wasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mod runtime;

#[cfg(feature = "runtime-benchmarks")]
pub use self::code_cache::reinstrument;
pub use self::runtime::{ReturnCode, Runtime, RuntimeCosts};
pub use self::runtime::{CallFlags, ReturnCode, Runtime, RuntimeCosts};
use crate::{
exec::{ExecResult, Executable, ExportedFunction, Ext},
gas::GasMeter,
Expand Down
73 changes: 40 additions & 33 deletions frame/contracts/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ pub enum RuntimeCosts {
/// Charge the gas meter with the cost of a metering block. The charged costs are
/// the supplied cost of the block plus the overhead of the metering itself.
MeteringBlock(u32),
/// Weight charged for copying data from the sandbox.
CopyIn(u32),
/// Weight charged for copying data to the sandbox.
CopyOut(u32),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these comments be swapped?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends what your point of view is. Those are written from the point of view of the pallet-contracts and not the contract. But I should rename them to make them independent of that point of view.

/// Weight of calling `seal_caller`.
Caller,
/// Weight of calling `seal_is_contract`.
Expand All @@ -162,8 +166,6 @@ pub enum RuntimeCosts {
WeightToFee,
/// Weight of calling `seal_input` without the weight of copying the input.
InputBase,
/// Weight of copying the input data for the given size.
InputCopyOut(u32),
/// Weight of calling `seal_return` for the given output size.
Return(u32),
/// Weight of calling `seal_terminate`.
Expand All @@ -188,21 +190,19 @@ pub enum RuntimeCosts {
TakeStorage(u32),
/// Weight of calling `seal_transfer`.
Transfer,
/// Weight of calling `seal_call` for the given input size.
CallBase(u32),
/// Base weight of calling `seal_call`.
CallBase,
/// Weight of calling `seal_delegate_call` for the given input size.
#[cfg(feature = "unstable-interface")]
DelegateCallBase(u32),
DelegateCallBase,
/// Weight of the transfer performed during a call.
CallSurchargeTransfer,
/// Weight of output received through `seal_call` for the given size.
CallCopyOut(u32),
/// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag.
CallInputCloned(u32),
/// Weight of calling `seal_instantiate` for the given input and salt without output weight.
/// This includes the transfer as an instantiate without a value will always be below
/// the existential deposit and is disregarded as corner case.
InstantiateBase { input_data_len: u32, salt_len: u32 },
/// Weight of output received through `seal_instantiate` for the given size.
InstantiateCopyOut(u32),
/// Weight of calling `seal_hash_sha_256` for the given input size.
HashSha256(u32),
/// Weight of calling `seal_hash_keccak_256` for the given input size.
Expand All @@ -216,9 +216,6 @@ pub enum RuntimeCosts {
EcdsaRecovery,
/// Weight charged by a chain extension through `seal_call_chain_extension`.
ChainExtension(u64),
/// Weight charged for copying data from the sandbox.
#[cfg(feature = "unstable-interface")]
CopyIn(u32),
/// Weight charged for calling into the runtime.
#[cfg(feature = "unstable-interface")]
CallRuntime(Weight),
Expand All @@ -233,6 +230,8 @@ impl RuntimeCosts {
use self::RuntimeCosts::*;
let weight = match *self {
MeteringBlock(amount) => s.gas.saturating_add(amount.into()),
CopyIn(len) => s.return_per_byte.saturating_mul(len.into()),
CopyOut(len) => s.input_per_byte.saturating_mul(len.into()),
Caller => s.caller,
#[cfg(feature = "unstable-interface")]
IsContract => s.is_contract,
Expand All @@ -247,7 +246,6 @@ impl RuntimeCosts {
Now => s.now,
WeightToFee => s.weight_to_fee,
InputBase => s.input,
InputCopyOut(len) => s.input_per_byte.saturating_mul(len.into()),
Return(len) => s.r#return.saturating_add(s.return_per_byte.saturating_mul(len.into())),
Terminate => s.terminate,
Random => s.random,
Expand All @@ -274,18 +272,15 @@ impl RuntimeCosts {
.take_storage
.saturating_add(s.take_storage_per_byte.saturating_mul(len.into())),
Transfer => s.transfer,
CallBase(len) =>
s.call.saturating_add(s.call_per_input_byte.saturating_mul(len.into())),
CallSurchargeTransfer => s.call_transfer_surcharge,
CallCopyOut(len) => s.call_per_output_byte.saturating_mul(len.into()),
CallBase => s.call,
#[cfg(feature = "unstable-interface")]
DelegateCallBase(len) =>
s.delegate_call.saturating_add(s.call_per_input_byte.saturating_mul(len.into())),
DelegateCallBase => s.delegate_call,
CallSurchargeTransfer => s.call_transfer_surcharge,
CallInputCloned(len) => s.call_per_cloned_byte.saturating_mul(len.into()),
InstantiateBase { input_data_len, salt_len } => s
.instantiate
.saturating_add(s.instantiate_per_input_byte.saturating_mul(input_data_len.into()))
.saturating_add(s.instantiate_per_salt_byte.saturating_mul(salt_len.into())),
InstantiateCopyOut(len) => s.instantiate_per_output_byte.saturating_mul(len.into()),
HashSha256(len) => s
.hash_sha2_256
.saturating_add(s.hash_sha2_256_per_byte.saturating_mul(len.into())),
Expand All @@ -301,8 +296,7 @@ impl RuntimeCosts {
#[cfg(feature = "unstable-interface")]
EcdsaRecovery => s.ecdsa_recover,
ChainExtension(amount) => amount,
#[cfg(feature = "unstable-interface")]
CopyIn(len) => s.return_per_byte.saturating_mul(len.into()),

#[cfg(feature = "unstable-interface")]
CallRuntime(weight) => weight,
};
Expand All @@ -314,6 +308,17 @@ impl RuntimeCosts {
}
}

/// Same as [`Runtime::charge_gas`].
///
/// We need this access as a macro because sometimes hiding the lifetimes behind
/// a function won't work out.
macro_rules! charge_gas {
($runtime:expr, $costs:expr) => {{
let token = $costs.token(&$runtime.ext.schedule().host_fn_weights);
$runtime.ext.gas_meter().charge(token)
}};
}

#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
struct RuntimeToken {
Expand All @@ -334,7 +339,7 @@ where

bitflags! {
/// Flags used to change the behaviour of `seal_call` and `seal_delegate_call`.
struct CallFlags: u32 {
pub struct CallFlags: u32 {
/// Forward the input of current function to the callee.
///
/// Supplied input pointers are ignored when set.
Expand Down Expand Up @@ -388,11 +393,11 @@ enum CallType {
}

impl CallType {
fn cost(&self, input_data_len: u32) -> RuntimeCosts {
fn cost(&self) -> RuntimeCosts {
match self {
CallType::Call { .. } => RuntimeCosts::CallBase(input_data_len),
CallType::Call { .. } => RuntimeCosts::CallBase,
#[cfg(feature = "unstable-interface")]
CallType::DelegateCall { .. } => RuntimeCosts::DelegateCallBase(input_data_len),
CallType::DelegateCall { .. } => RuntimeCosts::DelegateCallBase,
}
}
}
Expand Down Expand Up @@ -488,8 +493,7 @@ where
///
/// Returns `Err(HostError)` if there is not enough gas.
pub fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<ChargedAmount, DispatchError> {
let token = costs.token(&self.ext.schedule().host_fn_weights);
self.ext.gas_meter().charge(token)
charge_gas!(self, costs)
}

/// Adjust a previously charged amount down to its actual amount.
Expand Down Expand Up @@ -729,12 +733,15 @@ where
output_ptr: u32,
output_len_ptr: u32,
) -> Result<ReturnCode, TrapReason> {
self.charge_gas(call_type.cost(input_data_len))?;
self.charge_gas(call_type.cost())?;
let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
self.input_data.as_ref().ok_or_else(|| Error::<E::T>::InputForwarded)?.clone()
let input = self.input_data.as_ref().ok_or_else(|| Error::<E::T>::InputForwarded)?;
charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?;
input.clone()
} else if flags.contains(CallFlags::FORWARD_INPUT) {
self.input_data.take().ok_or_else(|| Error::<E::T>::InputForwarded)?
} else {
self.charge_gas(RuntimeCosts::CopyIn(input_data_len))?;
self.read_sandbox_memory(input_data_ptr, input_data_len)?
};

Expand Down Expand Up @@ -777,7 +784,7 @@ where

if let Ok(output) = &call_outcome {
self.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
Some(RuntimeCosts::CallCopyOut(len))
Some(RuntimeCosts::CopyOut(len))
})?;
}
Ok(Runtime::<E>::exec_into_return_code(call_outcome)?)
Expand Down Expand Up @@ -814,7 +821,7 @@ where
)?;
}
self.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
Some(RuntimeCosts::InstantiateCopyOut(len))
Some(RuntimeCosts::CopyOut(len))
})?;
}
Ok(Runtime::<E>::exec_into_return_code(instantiate_outcome.map(|(_, retval)| retval))?)
Expand Down Expand Up @@ -1297,7 +1304,7 @@ define_env!(Env, <E: Ext>,
ctx.charge_gas(RuntimeCosts::InputBase)?;
if let Some(input) = ctx.input_data.take() {
ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| {
Some(RuntimeCosts::InputCopyOut(len))
Some(RuntimeCosts::CopyOut(len))
})?;
ctx.input_data = Some(input);
Ok(())
Expand Down
8 changes: 3 additions & 5 deletions frame/contracts/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub trait WeightInfo {
fn seal_transfer(r: u32, ) -> Weight;
fn seal_call(r: u32, ) -> Weight;
fn seal_delegate_call(r: u32, ) -> Weight;
fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight;
fn seal_call_per_transfer_clone_kb(t: u32, i: u32) -> Weight;
fn seal_instantiate(r: u32, ) -> Weight;
fn seal_instantiate_per_input_output_salt_kb(i: u32, o: u32, s: u32, ) -> Weight;
fn seal_hash_sha2_256(r: u32, ) -> Weight;
Expand Down Expand Up @@ -633,14 +633,13 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: Contracts ContractInfoOf (r:101 w:101)
// Storage: Contracts CodeStorage (r:2 w:0)
// Storage: Timestamp Now (r:1 w:0)
fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight {
fn seal_call_per_transfer_clone_kb(t: u32, i: u32) -> Weight {
(20_190_331_000 as Weight)
// Standard Error: 75_647_000
.saturating_add((2_369_225_000 as Weight).saturating_mul(t as Weight))
// Standard Error: 27_000
.saturating_add((19_831_000 as Weight).saturating_mul(i as Weight))
// Standard Error: 28_000
.saturating_add((31_191_000 as Weight).saturating_mul(o as Weight))
.saturating_add(T::DbWeight::get().reads(105 as Weight))
.saturating_add(T::DbWeight::get().reads((101 as Weight).saturating_mul(t as Weight)))
.saturating_add(T::DbWeight::get().writes(101 as Weight))
Expand Down Expand Up @@ -1512,14 +1511,13 @@ impl WeightInfo for () {
// Storage: Contracts ContractInfoOf (r:101 w:101)
// Storage: Contracts CodeStorage (r:2 w:0)
// Storage: Timestamp Now (r:1 w:0)
fn seal_call_per_transfer_input_output_kb(t: u32, i: u32, o: u32, ) -> Weight {
fn seal_call_per_transfer_clone_kb(t: u32, i: u32) -> Weight {
(20_190_331_000 as Weight)
// Standard Error: 75_647_000
.saturating_add((2_369_225_000 as Weight).saturating_mul(t as Weight))
// Standard Error: 27_000
.saturating_add((19_831_000 as Weight).saturating_mul(i as Weight))
// Standard Error: 28_000
.saturating_add((31_191_000 as Weight).saturating_mul(o as Weight))
.saturating_add(RocksDbWeight::get().reads(105 as Weight))
.saturating_add(RocksDbWeight::get().reads((101 as Weight).saturating_mul(t as Weight)))
.saturating_add(RocksDbWeight::get().writes(101 as Weight))
Expand Down