diff --git a/.gitignore b/.gitignore index c71ddf5..715ba0f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ gas_costs.txt # Move Build Output build/ + +Calibration.move +gen_smove_instr.sh diff --git a/doc/tech_guide.md b/doc/tech_guide.md index b8db7f8..04e1db9 100644 --- a/doc/tech_guide.md +++ b/doc/tech_guide.md @@ -96,7 +96,7 @@ impl pallet_move::Config for Test { // Runtime event of this blockchain. type RuntimeEvent = RuntimeEvent; // Weight info for this pallet. - type WeightInfo = (); // or use pallet_move::weights::SubstrateWeights; + type WeightInfo = pallet_move::weights::SubstrateWeight; } ``` diff --git a/pallet/src/assets/move-projects/gas-costs/build.sh b/pallet/src/assets/move-projects/gas-costs/build.sh index f801103..00b9a4d 100755 --- a/pallet/src/assets/move-projects/gas-costs/build.sh +++ b/pallet/src/assets/move-projects/gas-costs/build.sh @@ -1,6 +1,7 @@ #!/bin/sh cd $(dirname $0) ALICE=5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +sh ./gen_cal_scripts.sh # Build the project smove build # Create all Script-Transactions @@ -10,3 +11,4 @@ smove create-transaction --compiled-script-path build/gas-costs/bytecode_scripts mv build/gas-costs/script_transactions/long_script.mvt build/gas-costs/script_transactions/long_cheap_script.mvt smove create-transaction --compiled-script-path build/gas-costs/bytecode_scripts/long_script.mv --args signer:$ALICE bool:false mv build/gas-costs/script_transactions/long_script.mvt build/gas-costs/script_transactions/long_expensive_script.mvt +sh ./gen_smove_instr.sh diff --git a/pallet/src/assets/move-projects/gas-costs/gen_cal_scripts.sh b/pallet/src/assets/move-projects/gas-costs/gen_cal_scripts.sh new file mode 100755 index 0000000..0619bbd --- /dev/null +++ b/pallet/src/assets/move-projects/gas-costs/gen_cal_scripts.sh @@ -0,0 +1,41 @@ +#!/bin/sh +cd $(dirname $0) + +MOVE_SRC="./sources/Calibration.move" +BASH_SH="./gen_smove_instr.sh" +ITERATIONS=25 + +function write_method() { + printf "script {\n" >> $MOVE_SRC + printf " fun cal_gas_cost_$1(v0: u8" >> $MOVE_SRC + if [ $1 -gt 0 ] + then + for i in $(seq 1 $1) + do + printf ", v$i: u8" >> $MOVE_SRC + done + fi + printf ") {\n" >> $MOVE_SRC + for i in $(seq 0 $1) + do + printf " assert!(v$i == $i, $i);\n" >> $MOVE_SRC + done + printf " }\n" >> $MOVE_SRC + printf "}\n" >> $MOVE_SRC +} + +function write_smove_cmd() { + printf "\nsmove create-transaction -c build/gas-costs/bytecode_scripts/cal_gas_cost_$1.mv --args" >> $BASH_SH + for i in $(seq 0 $1) + do + printf " u8:$i" >> $BASH_SH + done +} + +printf "" > $MOVE_SRC +printf "#!/bin/sh" > $BASH_SH +for i in $(seq 0 $(($ITERATIONS-1))) +do + write_method $i + write_smove_cmd $i +done diff --git a/pallet/src/benchmarking.rs b/pallet/src/benchmarking.rs index 2689ac3..0d2588b 100644 --- a/pallet/src/benchmarking.rs +++ b/pallet/src/benchmarking.rs @@ -2,144 +2,76 @@ use frame_benchmarking::v2::*; use frame_system::{Config as SysConfig, RawOrigin}; -use pallet_balances::{Config as BalancesConfig, Pallet as Balances}; use sp_core::crypto::Ss58Codec; use crate::{mock_utils as utils, *}; -const LIMIT: u128 = 60_000_000_000_000; - -const MAX_GAS_AMOUNT: u32 = u32::MAX; - type SourceOf = <::Lookup as sp_runtime::traits::StaticLookup>::Source; +macro_rules! impl_gas_costs_cal_fns { + ($name:tt) => { + pub fn $name() -> &'static [u8] { + core::include_bytes!(concat!( + "assets/move-projects/gas-costs/build/gas-costs/script_transactions/", + stringify!($name), + ".mvt" + )) + } + }; +} + #[benchmarks( where - T: Config + SysConfig + BalancesConfig, + T: Config + SysConfig, T::AccountId: Ss58Codec, - T::Balance: From, SourceOf: From, )] mod benchmarks { use super::*; + /// Because it is challenging to determine a reliable and fixed relation between gas costs and + /// Substrate weights, we created Move scripts with known gas costs and increasing steps of 20. + /// Twenty-five scripts with rising gas costs of about 20 for each iteration step were used as + /// input for this benchmark. Therefore, the original output was divided by 20 afterwards. #[benchmark] - fn execute(n: Linear<0, 7>) { - let bob_32 = utils::account::(utils::BOB_ADDR); + fn execute(n: Linear<0, 24>) { let alice_32 = utils::account::(utils::ALICE_ADDR); - let dave_32 = utils::account::(utils::DAVE_ADDR); - let eve_32 = utils::account::(utils::EVE_ADDR); // Our benchmark plan (each is a test scenario with different parameters). let script_bcs = [ - car_wash_initial_coin_miniting().to_vec(), - car_wash_register_new_user().to_vec(), - car_wash_buy_coin().to_vec(), - car_wash_wash_car().to_vec(), - gas_costs_short_cheap_script().to_vec(), - gas_costs_long_cheap_script().to_vec(), - multiple_signers_init_module().to_vec(), - multiple_signers_rent_apartment().to_vec(), - ]; - // Sequence of account-IDs who will execute each extrinsic call. - let accounts = [ - bob_32.clone(), // car-wash-example - alice_32.clone(), - alice_32.clone(), - alice_32.clone(), - alice_32.clone(), // gas-costs - alice_32.clone(), - bob_32.clone(), // multiple-signers - eve_32, + cal_gas_cost_0().to_vec(), + cal_gas_cost_1().to_vec(), + cal_gas_cost_2().to_vec(), + cal_gas_cost_3().to_vec(), + cal_gas_cost_4().to_vec(), + cal_gas_cost_5().to_vec(), + cal_gas_cost_6().to_vec(), + cal_gas_cost_7().to_vec(), + cal_gas_cost_8().to_vec(), + cal_gas_cost_9().to_vec(), + cal_gas_cost_10().to_vec(), + cal_gas_cost_11().to_vec(), + cal_gas_cost_12().to_vec(), + cal_gas_cost_13().to_vec(), + cal_gas_cost_14().to_vec(), + cal_gas_cost_15().to_vec(), + cal_gas_cost_16().to_vec(), + cal_gas_cost_17().to_vec(), + cal_gas_cost_18().to_vec(), + cal_gas_cost_19().to_vec(), + cal_gas_cost_20().to_vec(), + cal_gas_cost_21().to_vec(), + cal_gas_cost_22().to_vec(), + cal_gas_cost_23().to_vec(), + cal_gas_cost_24().to_vec(), ]; - // Needed gas amounts for each script, estimated by smove. - let gas = [343, 197, 795, 425, 1, 8, 308, 1377]; - // Balance limit to be used. - let regular = [LIMIT; 2]; - let max = [u128::MAX; 2]; - let limit = [regular, regular, max, regular].concat(); - - // Now we have to prepare each script execution with a proper setup. - // Publish both modules always. - Pallet::::publish_module( - RawOrigin::Signed(bob_32.clone()).into(), - car_wash_example_module().to_vec(), - MAX_GAS_AMOUNT, - ) - .unwrap(); - Pallet::::publish_module( - RawOrigin::Signed(bob_32.clone()).into(), - multiple_signers_module().to_vec(), - MAX_GAS_AMOUNT, - ) - .unwrap(); - - // Now prepare individual situations for proper script sequences. - if n > 0 && n < 6 { - Pallet::::execute( - RawOrigin::Signed(bob_32.clone()).into(), - car_wash_initial_coin_miniting().to_vec(), - MAX_GAS_AMOUNT, - LIMIT.into(), - ) - .unwrap(); - if n > 1 { - Pallet::::execute( - RawOrigin::Signed(alice_32.clone()).into(), - car_wash_register_new_user().to_vec(), - MAX_GAS_AMOUNT, - LIMIT.into(), - ) - .unwrap(); - } - if n > 2 && n < 4 { - Pallet::::execute( - RawOrigin::Signed(alice_32.clone()).into(), - car_wash_buy_coin().to_vec(), - MAX_GAS_AMOUNT, - LIMIT.into(), - ) - .unwrap(); - } - if n > 3 { - Balances::::force_set_balance( - RawOrigin::Root.into(), - alice_32.clone().into(), - u128::MAX.into(), - ) - .unwrap(); - } - } - if n > 6 { - Pallet::::execute( - RawOrigin::Signed(bob_32.clone()).into(), - multiple_signers_init_module().to_vec(), - MAX_GAS_AMOUNT, - LIMIT.into(), - ) - .unwrap(); - Pallet::::execute( - RawOrigin::Signed(alice_32.clone()).into(), - multiple_signers_rent_apartment().to_vec(), - MAX_GAS_AMOUNT, - LIMIT.into(), - ) - .unwrap(); - Pallet::::execute( - RawOrigin::Signed(dave_32).into(), - multiple_signers_rent_apartment().to_vec(), - MAX_GAS_AMOUNT, - LIMIT.into(), - ) - .unwrap(); - } #[extrinsic_call] execute( - RawOrigin::Signed(accounts[n as usize].clone()), + RawOrigin::Signed(alice_32), script_bcs[n as usize].clone(), - gas[n as usize], - limit[n as usize].into(), + (n + 1) * 20, + 0u128.into(), ) } @@ -243,26 +175,6 @@ mod benchmark_only { ) } - pub fn car_wash_initial_coin_miniting() -> &'static [u8] { - core::include_bytes!("assets/move-projects/car-wash-example/build/car-wash-example/script_transactions/initial_coin_minting.mvt") - } - - pub fn car_wash_register_new_user() -> &'static [u8] { - core::include_bytes!("assets/move-projects/car-wash-example/build/car-wash-example/script_transactions/register_new_user.mvt") - } - - pub fn car_wash_buy_coin() -> &'static [u8] { - core::include_bytes!( - "assets/move-projects/car-wash-example/build/car-wash-example/script_transactions/buy_coin.mvt" - ) - } - - pub fn car_wash_wash_car() -> &'static [u8] { - core::include_bytes!( - "assets/move-projects/car-wash-example/build/car-wash-example/script_transactions/wash_car.mvt" - ) - } - // Multiple Signers Example pub fn multiple_signers_module() -> &'static [u8] { core::include_bytes!( @@ -275,19 +187,29 @@ mod benchmark_only { ) } - pub fn multiple_signers_init_module() -> &'static [u8] { - core::include_bytes!("assets/move-projects/multiple-signers/build/multiple-signers/script_transactions/init_module.mvt") - } - - pub fn multiple_signers_rent_apartment() -> &'static [u8] { - core::include_bytes!("assets/move-projects/multiple-signers/build/multiple-signers/script_transactions/rent_apartment.mvt") - } - - pub fn gas_costs_short_cheap_script() -> &'static [u8] { - core::include_bytes!("assets/move-projects/gas-costs/build/gas-costs/script_transactions/short_cheap_script.mvt") - } - - pub fn gas_costs_long_cheap_script() -> &'static [u8] { - core::include_bytes!("assets/move-projects/gas-costs/build/gas-costs/script_transactions/long_cheap_script.mvt") - } + impl_gas_costs_cal_fns!(cal_gas_cost_0); + impl_gas_costs_cal_fns!(cal_gas_cost_1); + impl_gas_costs_cal_fns!(cal_gas_cost_2); + impl_gas_costs_cal_fns!(cal_gas_cost_3); + impl_gas_costs_cal_fns!(cal_gas_cost_4); + impl_gas_costs_cal_fns!(cal_gas_cost_5); + impl_gas_costs_cal_fns!(cal_gas_cost_6); + impl_gas_costs_cal_fns!(cal_gas_cost_7); + impl_gas_costs_cal_fns!(cal_gas_cost_8); + impl_gas_costs_cal_fns!(cal_gas_cost_9); + impl_gas_costs_cal_fns!(cal_gas_cost_10); + impl_gas_costs_cal_fns!(cal_gas_cost_11); + impl_gas_costs_cal_fns!(cal_gas_cost_12); + impl_gas_costs_cal_fns!(cal_gas_cost_13); + impl_gas_costs_cal_fns!(cal_gas_cost_14); + impl_gas_costs_cal_fns!(cal_gas_cost_15); + impl_gas_costs_cal_fns!(cal_gas_cost_16); + impl_gas_costs_cal_fns!(cal_gas_cost_17); + impl_gas_costs_cal_fns!(cal_gas_cost_18); + impl_gas_costs_cal_fns!(cal_gas_cost_19); + impl_gas_costs_cal_fns!(cal_gas_cost_20); + impl_gas_costs_cal_fns!(cal_gas_cost_21); + impl_gas_costs_cal_fns!(cal_gas_cost_22); + impl_gas_costs_cal_fns!(cal_gas_cost_23); + impl_gas_costs_cal_fns!(cal_gas_cost_24); } diff --git a/pallet/src/mock.rs b/pallet/src/mock.rs index d762640..b31e0d8 100644 --- a/pallet/src/mock.rs +++ b/pallet/src/mock.rs @@ -106,7 +106,7 @@ impl pallet_move::Config for Test { type MultisigReqExpireTime = MultisigReqExpireTime; type MaxScriptSigners = MaxScriptSigners; type RuntimeEvent = RuntimeEvent; - type WeightInfo = crate::weights::SubstrateWeight; + type WeightInfo = pallet_move::weights::SubstrateWeight; } /// Test Externalities Builder for an easier test setup. diff --git a/pallet/src/mock_utils.rs b/pallet/src/mock_utils.rs index 13f245a..12ecd0e 100644 --- a/pallet/src/mock_utils.rs +++ b/pallet/src/mock_utils.rs @@ -16,8 +16,6 @@ mod always_used { // Reusable constants for test accounts. pub const BOB_ADDR: &str = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"; pub const ALICE_ADDR: &str = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"; - pub const DAVE_ADDR: &str = "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy"; - pub const EVE_ADDR: &str = "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw"; /// Creates a native 32-byte address from a given ss58 string. pub fn account(ss58addr: &str) -> T::AccountId @@ -67,6 +65,8 @@ mod tests_only { use crate::{Config, Pallet}; // Reusable constants for test accounts. + pub const DAVE_ADDR: &str = "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy"; + pub const EVE_ADDR: &str = "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw"; // equivalent to 0xCAFE pub const CAFE_ADDR: &str = "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSv4fmh4G"; // equivalent to 0x1 diff --git a/pallet/src/weights.rs b/pallet/src/weights.rs index 7823778..d37b754 100644 --- a/pallet/src/weights.rs +++ b/pallet/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for `pallet_move` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `michaeleberhardts-MacBook-Pro.local`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: 1024 @@ -33,32 +33,31 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; +// Note: The weights have not only been generated by the Substrate benchmarks with a template-node. +// A couple of manual and additional modifications have been done which are marked in the +// comments. + /// Weight functions for `pallet_move`. pub struct SubstrateWeight(PhantomData); impl crate::weight_info::WeightInfo for SubstrateWeight { - /// Storage: `MoveModule::MultisigStorage` (r:1 w:1) - /// Proof: `MoveModule::MultisigStorage` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:3 w:3) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:3 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:4 w:4) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `MoveModule::VMStorage` (r:2 w:1) - /// Proof: `MoveModule::VMStorage` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 7]`. + /// Because it is challenging to determine a reliable and fixed relation between gas costs and + /// Substrate weights, we created Move scripts with known gas costs and increasing steps of 20. + /// Twenty-five scripts with rising gas costs of about 20 for each iteration step were used as + /// input for this benchmark. Therefore, the original output was divided by 20 afterwards. + /// The range of component `n` is `[0, 24]`. fn execute(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `9479` - // Estimated: `17285 + n * (1902 ±16)` - // Minimum execution time: 33_000_000 picoseconds. - Weight::from_parts(33_000_000, 0) - .saturating_add(Weight::from_parts(0, 17285)) - // Standard Error: 70_255_180 - .saturating_add(Weight::from_parts(989_536_443, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) - .saturating_add(Weight::from_parts(0, 1902).saturating_mul(n.into())) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 46_000_000 picoseconds. + Weight::from_parts(44_730_881, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 9_543 + // Manually divided original output by 20: 13_002_180 / 20 = 650_109 + .saturating_add(Weight::from_parts(650_109, 0).saturating_mul(n.into())) + // Manuelly added: + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `MoveModule::VMStorage` (r:1 w:1) /// Proof: `MoveModule::VMStorage` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -68,10 +67,10 @@ impl crate::weight_info::WeightInfo for SubstrateWeight // Measured: `7796` // Estimated: `14073` // Minimum execution time: 44_000_000 picoseconds. - Weight::from_parts(316_123_480, 0) + Weight::from_parts(318_442_817, 0) .saturating_add(Weight::from_parts(0, 14073)) - // Standard Error: 3_617_919 - .saturating_add(Weight::from_parts(3_931_767, 0).saturating_mul(n.into())) + // Standard Error: 3_649_708 + .saturating_add(Weight::from_parts(4_313_812, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -83,10 +82,10 @@ impl crate::weight_info::WeightInfo for SubstrateWeight // Measured: `7796` // Estimated: `14073` // Minimum execution time: 44_000_000 picoseconds. - Weight::from_parts(316_786_740, 0) + Weight::from_parts(319_610_220, 0) .saturating_add(Weight::from_parts(0, 14073)) - // Standard Error: 3_624_642 - .saturating_add(Weight::from_parts(3_790_883, 0).saturating_mul(n.into())) + // Standard Error: 3_670_322 + .saturating_add(Weight::from_parts(4_522_651, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -97,7 +96,7 @@ impl crate::weight_info::WeightInfo for SubstrateWeight // Measured: `7796` // Estimated: `11261` // Minimum execution time: 204_000_000 picoseconds. - Weight::from_parts(205_000_000, 0) + Weight::from_parts(206_000_000, 0) .saturating_add(Weight::from_parts(0, 11261)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1))