diff --git a/pallets/quota/src/benchmarking.rs b/pallets/quota/src/benchmarking.rs index 3237008785c1825cc2ffe3eb2fea24fcdf4b664a..4519eea02f754bef4fff507b183148cc825ecb20 100644 --- a/pallets/quota/src/benchmarking.rs +++ b/pallets/quota/src/benchmarking.rs @@ -18,6 +18,7 @@ use super::*; use frame_benchmarking::{account, benchmarks}; +use sp_runtime::traits::One; fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) { frame_system::Pallet::<T>::assert_has_event(generic_event.into()); @@ -54,17 +55,21 @@ benchmarks! { spend_quota { let idty_id: IdtyId<T> = 1u32.into(); let amount = 2u64; + let quota_amount = 10u64; IdtyQuota::<T>::insert( idty_id, Quota { last_use: T::BlockNumber::zero(), - amount: 10u64.into(), + amount: quota_amount.into(), }, ); }: { Pallet::<T>::spend_quota(idty_id.into(), amount.into()) } verify { - // Initially 10, updated to 11, minus 2 - assert_eq!(IdtyQuota::<T>::get(idty_id).unwrap().amount, 9u64.into()); + let quota_growth = sp_runtime::Perbill::from_rational( + T::BlockNumber::one(), + T::ReloadRate::get(), + ).mul_floor(T::MaxQuota::get()); + assert_eq!(IdtyQuota::<T>::get(idty_id).unwrap().amount, quota_growth +quota_amount.into() - amount.into()); } try_refund { let account: T::AccountId = account("Alice", 1, 1); @@ -89,7 +94,7 @@ benchmarks! { }; }: { Pallet::<T>::try_refund(refund) } verify { -assert_has_event::<T>(Event::<T>::RefundFailed ( account ).into()); + assert_has_event::<T>(Event::<T>::RefundFailed ( account ).into()); } do_refund { let account: T::AccountId = account("Alice", 1, 1); @@ -106,21 +111,45 @@ assert_has_event::<T>(Event::<T>::RefundFailed ( account ).into()); }; }: { Pallet::<T>::try_refund(refund) } verify { -assert_has_event::<T>(Event::<T>::RefundFailed ( account ).into()); + assert_has_event::<T>(Event::<T>::RefundFailed ( account ).into()); } - process_refund_queue { + // The base weight consumed on processing refund queue when empty. + on_process_refund_queue { + assert_eq!(RefundQueue::<T>::get().len() as u32, 0); + }: { Pallet::<T>::process_refund_queue(Weight::MAX) } + // The weight consumed on processing refund queue with one element. + // Can deduce the process_refund_queue overhead by subtracting try_refund weight. + #[pov_mode = Measured] + on_process_refund_queue_elements { + let i in 1..MAX_QUEUED_REFUNDS; let account: T::AccountId = account("Alice", 1, 1); - let dummy_refund = Refund { + let idty_id: IdtyId<T> = 1u32.into(); + IdtyQuota::<T>::insert( + idty_id, + Quota { + last_use: T::BlockNumber::zero(), + amount: 10u64.into(), + }, + ); + let _ = CurrencyOf::<T>:: make_free_balance_be( + &T::RefundAccount::get(),u32::MAX.into(), + ); + // The worst-case scenario is when the refund fails + // and can only be triggered if the account is dead, + // in this case by having no balance in the account. + let refund = Refund { account: account.clone(), - identity: 0u32.into(), - amount: 20u64.into(), + identity: 1u32.into(), + amount: 10u64.into(), }; - // Worst case scenario: the queue is full, and the available weight is maximal. - for i in 0 .. MAX_QUEUED_REFUNDS { - Pallet::<T>::queue_refund(dummy_refund.clone()) + for _ in 0..i { + Pallet::<T>::queue_refund(refund.clone()); } + assert_eq!(RefundQueue::<T>::get().len() as u32, i); }: { Pallet::<T>::process_refund_queue(Weight::MAX) } verify { - assert_eq!(RefundQueue::<T>::get().len(), 0); + assert_eq!(RefundQueue::<T>::get().len() as u32, 0); + assert_has_event::<T>(Event::<T>::RefundFailed ( account ).into()); } + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(crate::mock::QuotaConfig{identities: vec![1, 2]}), crate::mock::Test); } diff --git a/pallets/quota/src/lib.rs b/pallets/quota/src/lib.rs index d62371b1c5af8ac094992b65d682949efa408b98..91df0459e3440a6f4bbcfcb595fb3c6377d9c5aa 100644 --- a/pallets/quota/src/lib.rs +++ b/pallets/quota/src/lib.rs @@ -210,19 +210,31 @@ pub mod pallet { /// perform as many refunds as possible within the supplied weight limit pub fn process_refund_queue(weight_limit: Weight) -> Weight { RefundQueue::<T>::mutate(|queue| { + // The weight to process an empty queue + let mut total_weight = <T as pallet::Config>::WeightInfo::on_process_refund_queue(); + // The weight to process one element without the actual try_refund weight + let overhead = + <T as pallet::Config>::WeightInfo::on_process_refund_queue_elements(2) + .saturating_sub( + <T as pallet::Config>::WeightInfo::on_process_refund_queue_elements(1), + ) + .saturating_sub(<T as pallet::Config>::WeightInfo::try_refund()); + + // make sure that we have at least the time to handle one try_refund call if queue.is_empty() { - return Weight::zero(); + return total_weight; } - let mut total_weight = Weight::zero(); - // make sure that we have at least the time to handle one try_refund call - while total_weight.any_lt( - weight_limit.saturating_sub(<T as pallet::Config>::WeightInfo::try_refund()), - ) { + + while total_weight.any_lt(weight_limit.saturating_sub( + <T as pallet::Config>::WeightInfo::try_refund().saturating_add(overhead), + )) { let Some(queued_refund) = queue.pop() else { break; }; let consumed_weight = Self::try_refund(queued_refund); - total_weight = total_weight.saturating_add(consumed_weight); + total_weight = total_weight + .saturating_add(consumed_weight) + .saturating_add(overhead); } total_weight }) @@ -308,14 +320,7 @@ pub mod pallet { impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { // process refund queue if space left on block fn on_idle(_block: T::BlockNumber, remaining_weight: Weight) -> Weight { - // The overhead is computed as the worst-case scenario for process_refund_queue (i.e., when the queue is full), - // minus the length of the queue times the actual weight of one refund operation. - let overhead = <T as pallet::Config>::WeightInfo::process_refund_queue() - .saturating_sub( - <T as pallet::Config>::WeightInfo::try_refund() - .saturating_mul(MAX_QUEUED_REFUNDS.into()), - ); - Self::process_refund_queue(remaining_weight - overhead) + Self::process_refund_queue(remaining_weight) } } } diff --git a/pallets/quota/src/weights.rs b/pallets/quota/src/weights.rs index 321a69f15fec4a1a4e46ce54eaa63f5940607640..076daa4b1800aa0a9a90c82f2a64474c364700b7 100644 --- a/pallets/quota/src/weights.rs +++ b/pallets/quota/src/weights.rs @@ -7,7 +7,8 @@ pub trait WeightInfo { fn spend_quota() -> Weight; fn try_refund() -> Weight; fn do_refund() -> Weight; - fn process_refund_queue() -> Weight; + fn on_process_refund_queue() -> Weight; + fn on_process_refund_queue_elements(_i: u32) -> Weight; } impl WeightInfo for () { @@ -23,7 +24,10 @@ impl WeightInfo for () { fn do_refund() -> Weight { Weight::from_parts(25u64, 0) } - fn process_refund_queue() -> Weight { - Weight::from_parts(100u64, 0) + fn on_process_refund_queue() -> Weight { + Weight::from_parts(1u64, 0) + } + fn on_process_refund_queue_elements(_i: u32) -> Weight { + Weight::from_parts(1u64, 0) } } diff --git a/runtime/common/src/weights/pallet_quota.rs b/runtime/common/src/weights/pallet_quota.rs index e2b2273a839508da8371ba58caaa0a44be38cc01..398fed336bd28029084d161b69e413f570333d5d 100644 --- a/runtime/common/src/weights/pallet_quota.rs +++ b/runtime/common/src/weights/pallet_quota.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for `pallet_quota` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-11-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bgallois-ms7d43`, CPU: `12th Gen Intel(R) Core(TM) i3-12100F` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -41,8 +41,8 @@ impl<T: frame_system::Config> pallet_quota::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `11288` // Estimated: `12751` - // Minimum execution time: 7_447_000 picoseconds. - Weight::from_parts(7_619_000, 0) + // Minimum execution time: 7_021_000 picoseconds. + Weight::from_parts(7_228_000, 0) .saturating_add(Weight::from_parts(0, 12751)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -53,8 +53,8 @@ impl<T: frame_system::Config> pallet_quota::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `139` // Estimated: `3489` - // Minimum execution time: 3_617_000 picoseconds. - Weight::from_parts(3_727_000, 0) + // Minimum execution time: 3_635_000 picoseconds. + Weight::from_parts(3_768_000, 0) .saturating_add(Weight::from_parts(0, 3489)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -67,8 +67,8 @@ impl<T: frame_system::Config> pallet_quota::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `139` // Estimated: `3591` - // Minimum execution time: 10_892_000 picoseconds. - Weight::from_parts(11_314_000, 0) + // Minimum execution time: 11_415_000 picoseconds. + Weight::from_parts(11_717_000, 0) .saturating_add(Weight::from_parts(0, 3591)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -81,24 +81,42 @@ impl<T: frame_system::Config> pallet_quota::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `139` // Estimated: `3591` - // Minimum execution time: 10_876_000 picoseconds. - Weight::from_parts(11_384_000, 0) + // Minimum execution time: 10_849_000 picoseconds. + Weight::from_parts(11_263_000, 0) .saturating_add(Weight::from_parts(0, 3591)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: Quota RefundQueue (r:1 w:1) /// Proof: Quota RefundQueue (max_values: Some(1), max_size: Some(11266), added: 11761, mode: MaxEncodedLen) - /// Storage: Quota IdtyQuota (r:1 w:1) - /// Proof: Quota IdtyQuota (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) - fn process_refund_queue() -> Weight { + fn on_process_refund_queue() -> Weight { // Proof Size summary in bytes: - // Measured: `11428` + // Measured: `43` // Estimated: `12751` - // Minimum execution time: 262_104_000 picoseconds. - Weight::from_parts(275_319_000, 0) + // Minimum execution time: 1_530_000 picoseconds. + Weight::from_parts(1_646_000, 0) .saturating_add(Weight::from_parts(0, 12751)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Quota RefundQueue (r:1 w:1) + /// Proof: Quota RefundQueue (max_values: Some(1), max_size: Some(11266), added: 11761, mode: Measured) + /// Storage: Quota IdtyQuota (r:1 w:1) + /// Proof: Quota IdtyQuota (max_values: None, max_size: Some(24), added: 2499, mode: Measured) + /// Storage: System Account (r:1 w:0) + /// Proof: System Account (max_values: None, max_size: Some(126), added: 2601, mode: Measured) + /// The range of component `i` is `[1, 256]`. + fn on_process_refund_queue_elements(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + i * (44 ±0)` + // Estimated: `3628 + i * (44 ±0)` + // Minimum execution time: 13_465_000 picoseconds. + Weight::from_parts(19_579_671, 0) + .saturating_add(Weight::from_parts(0, 3628)) + // Standard Error: 1_313 + .saturating_add(Weight::from_parts(465_028, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(i.into())) } }