sp_weights/
weight_v2.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use codec::{Decode, Encode, MaxEncodedLen};
19use core::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
20use sp_arithmetic::traits::{Bounded, CheckedAdd, CheckedSub, Zero};
21
22use super::*;
23
24#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Eq, PartialEq, Copy, Clone, Debug, Default)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
27pub struct Weight {
28	#[codec(compact)]
29	/// The weight of computational time used based on some reference hardware.
30	ref_time: u64,
31	#[codec(compact)]
32	/// The weight of storage space used by proof of validity.
33	proof_size: u64,
34}
35
36impl Weight {
37	/// Set the reference time part of the weight.
38	pub const fn set_ref_time(mut self, c: u64) -> Self {
39		self.ref_time = c;
40		self
41	}
42
43	/// Set the storage size part of the weight.
44	pub const fn set_proof_size(mut self, c: u64) -> Self {
45		self.proof_size = c;
46		self
47	}
48
49	/// Return the reference time part of the weight.
50	pub const fn ref_time(&self) -> u64 {
51		self.ref_time
52	}
53
54	/// Return the storage size part of the weight.
55	pub const fn proof_size(&self) -> u64 {
56		self.proof_size
57	}
58
59	/// Return a mutable reference to the reference time part of the weight.
60	pub fn ref_time_mut(&mut self) -> &mut u64 {
61		&mut self.ref_time
62	}
63
64	/// Return a mutable reference to the storage size part of the weight.
65	pub fn proof_size_mut(&mut self) -> &mut u64 {
66		&mut self.proof_size
67	}
68
69	/// The maximal weight in all dimensions.
70	pub const MAX: Self = Self { ref_time: u64::MAX, proof_size: u64::MAX };
71
72	/// Get the conservative min of `self` and `other` weight.
73	pub fn min(&self, other: Self) -> Self {
74		Self {
75			ref_time: self.ref_time.min(other.ref_time),
76			proof_size: self.proof_size.min(other.proof_size),
77		}
78	}
79
80	/// Get the aggressive max of `self` and `other` weight.
81	pub fn max(&self, other: Self) -> Self {
82		Self {
83			ref_time: self.ref_time.max(other.ref_time),
84			proof_size: self.proof_size.max(other.proof_size),
85		}
86	}
87
88	/// Try to add some `other` weight while upholding the `limit`.
89	pub fn try_add(&self, other: &Self, limit: &Self) -> Option<Self> {
90		let total = self.checked_add(other)?;
91		if total.any_gt(*limit) {
92			None
93		} else {
94			Some(total)
95		}
96	}
97
98	/// Construct [`Weight`] from weight parts, namely reference time and proof size weights.
99	pub const fn from_parts(ref_time: u64, proof_size: u64) -> Self {
100		Self { ref_time, proof_size }
101	}
102
103	/// Construct [`Weight`] from the same weight for all parts.
104	pub const fn from_all(value: u64) -> Self {
105		Self { ref_time: value, proof_size: value }
106	}
107
108	/// Saturating [`Weight`] addition. Computes `self + rhs`, saturating at the numeric bounds of
109	/// all fields instead of overflowing.
110	pub const fn saturating_add(self, rhs: Self) -> Self {
111		Self {
112			ref_time: self.ref_time.saturating_add(rhs.ref_time),
113			proof_size: self.proof_size.saturating_add(rhs.proof_size),
114		}
115	}
116
117	/// Saturating [`Weight`] subtraction. Computes `self - rhs`, saturating at the numeric bounds
118	/// of all fields instead of overflowing.
119	pub const fn saturating_sub(self, rhs: Self) -> Self {
120		Self {
121			ref_time: self.ref_time.saturating_sub(rhs.ref_time),
122			proof_size: self.proof_size.saturating_sub(rhs.proof_size),
123		}
124	}
125
126	/// Saturating [`Weight`] scalar multiplication. Computes `self.field * scalar` for all fields,
127	/// saturating at the numeric bounds of all fields instead of overflowing.
128	pub const fn saturating_mul(self, scalar: u64) -> Self {
129		Self {
130			ref_time: self.ref_time.saturating_mul(scalar),
131			proof_size: self.proof_size.saturating_mul(scalar),
132		}
133	}
134
135	/// Saturating [`Weight`] scalar division. Computes `self.field / scalar` for all fields,
136	/// saturating at the numeric bounds of all fields instead of overflowing.
137	pub const fn saturating_div(self, scalar: u64) -> Self {
138		Self {
139			ref_time: self.ref_time.saturating_div(scalar),
140			proof_size: self.proof_size.saturating_div(scalar),
141		}
142	}
143
144	/// Saturating [`Weight`] scalar exponentiation. Computes `self.field.pow(exp)` for all fields,
145	/// saturating at the numeric bounds of all fields instead of overflowing.
146	pub const fn saturating_pow(self, exp: u32) -> Self {
147		Self {
148			ref_time: self.ref_time.saturating_pow(exp),
149			proof_size: self.proof_size.saturating_pow(exp),
150		}
151	}
152
153	/// Increment [`Weight`] by `amount` via saturating addition.
154	pub fn saturating_accrue(&mut self, amount: Self) {
155		*self = self.saturating_add(amount);
156	}
157
158	/// Reduce [`Weight`] by `amount` via saturating subtraction.
159	pub fn saturating_reduce(&mut self, amount: Self) {
160		*self = self.saturating_sub(amount);
161	}
162
163	/// Checked [`Weight`] addition. Computes `self + rhs`, returning `None` if overflow occurred.
164	pub const fn checked_add(&self, rhs: &Self) -> Option<Self> {
165		let ref_time = match self.ref_time.checked_add(rhs.ref_time) {
166			Some(t) => t,
167			None => return None,
168		};
169		let proof_size = match self.proof_size.checked_add(rhs.proof_size) {
170			Some(s) => s,
171			None => return None,
172		};
173		Some(Self { ref_time, proof_size })
174	}
175
176	/// Checked [`Weight`] subtraction. Computes `self - rhs`, returning `None` if overflow
177	/// occurred.
178	pub const fn checked_sub(&self, rhs: &Self) -> Option<Self> {
179		let ref_time = match self.ref_time.checked_sub(rhs.ref_time) {
180			Some(t) => t,
181			None => return None,
182		};
183		let proof_size = match self.proof_size.checked_sub(rhs.proof_size) {
184			Some(s) => s,
185			None => return None,
186		};
187		Some(Self { ref_time, proof_size })
188	}
189
190	/// Checked [`Weight`] scalar multiplication. Computes `self.field * scalar` for each field,
191	/// returning `None` if overflow occurred.
192	pub const fn checked_mul(self, scalar: u64) -> Option<Self> {
193		let ref_time = match self.ref_time.checked_mul(scalar) {
194			Some(t) => t,
195			None => return None,
196		};
197		let proof_size = match self.proof_size.checked_mul(scalar) {
198			Some(s) => s,
199			None => return None,
200		};
201		Some(Self { ref_time, proof_size })
202	}
203
204	/// Checked [`Weight`] scalar division. Computes `self.field / scalar` for each field, returning
205	/// `None` if overflow occurred.
206	pub const fn checked_div(self, scalar: u64) -> Option<Self> {
207		let ref_time = match self.ref_time.checked_div(scalar) {
208			Some(t) => t,
209			None => return None,
210		};
211		let proof_size = match self.proof_size.checked_div(scalar) {
212			Some(s) => s,
213			None => return None,
214		};
215		Some(Self { ref_time, proof_size })
216	}
217
218	/// Calculates how many `other` fit into `self`.
219	///
220	/// Divides each component of `self` against the same component of `other`. Returns the minimum
221	/// of all those divisions. Returns `None` in case **all** components of `other` are zero.
222	///
223	/// This returns `Some` even if some components of `other` are zero as long as there is at least
224	/// one non-zero component in `other`. The division for this particular component will then
225	/// yield the maximum value (e.g u64::MAX). This is because we assume not every operation and
226	/// hence each `Weight` will necessarily use each resource.
227	pub const fn checked_div_per_component(self, other: &Self) -> Option<u64> {
228		let mut all_zero = true;
229		let ref_time = match self.ref_time.checked_div(other.ref_time) {
230			Some(ref_time) => {
231				all_zero = false;
232				ref_time
233			},
234			None => u64::MAX,
235		};
236		let proof_size = match self.proof_size.checked_div(other.proof_size) {
237			Some(proof_size) => {
238				all_zero = false;
239				proof_size
240			},
241			None => u64::MAX,
242		};
243		if all_zero {
244			None
245		} else {
246			Some(if ref_time < proof_size { ref_time } else { proof_size })
247		}
248	}
249
250	/// Try to increase `self` by `amount` via checked addition.
251	pub fn checked_accrue(&mut self, amount: Self) -> Option<()> {
252		self.checked_add(&amount).map(|new_self| *self = new_self)
253	}
254
255	/// Try to reduce `self` by `amount` via checked subtraction.
256	pub fn checked_reduce(&mut self, amount: Self) -> Option<()> {
257		self.checked_sub(&amount).map(|new_self| *self = new_self)
258	}
259
260	/// Return a [`Weight`] where all fields are zero.
261	pub const fn zero() -> Self {
262		Self { ref_time: 0, proof_size: 0 }
263	}
264
265	/// Constant version of Add for `ref_time` component with u64.
266	///
267	/// Is only overflow safe when evaluated at compile-time.
268	pub const fn add_ref_time(self, scalar: u64) -> Self {
269		Self { ref_time: self.ref_time + scalar, proof_size: self.proof_size }
270	}
271
272	/// Constant version of Add for `proof_size` component with u64.
273	///
274	/// Is only overflow safe when evaluated at compile-time.
275	pub const fn add_proof_size(self, scalar: u64) -> Self {
276		Self { ref_time: self.ref_time, proof_size: self.proof_size + scalar }
277	}
278
279	/// Constant version of Sub for `ref_time` component with u64.
280	///
281	/// Is only overflow safe when evaluated at compile-time.
282	pub const fn sub_ref_time(self, scalar: u64) -> Self {
283		Self { ref_time: self.ref_time - scalar, proof_size: self.proof_size }
284	}
285
286	/// Constant version of Sub for `proof_size` component with u64.
287	///
288	/// Is only overflow safe when evaluated at compile-time.
289	pub const fn sub_proof_size(self, scalar: u64) -> Self {
290		Self { ref_time: self.ref_time, proof_size: self.proof_size - scalar }
291	}
292
293	/// Constant version of Div with u64.
294	///
295	/// Is only overflow safe when evaluated at compile-time.
296	pub const fn div(self, scalar: u64) -> Self {
297		Self { ref_time: self.ref_time / scalar, proof_size: self.proof_size / scalar }
298	}
299
300	/// Constant version of Mul with u64.
301	///
302	/// Is only overflow safe when evaluated at compile-time.
303	pub const fn mul(self, scalar: u64) -> Self {
304		Self { ref_time: self.ref_time * scalar, proof_size: self.proof_size * scalar }
305	}
306
307	/// Returns true if any of `self`'s constituent weights is strictly greater than that of the
308	/// `other`'s, otherwise returns false.
309	pub const fn any_gt(self, other: Self) -> bool {
310		self.ref_time > other.ref_time || self.proof_size > other.proof_size
311	}
312
313	/// Returns true if all of `self`'s constituent weights is strictly greater than that of the
314	/// `other`'s, otherwise returns false.
315	pub const fn all_gt(self, other: Self) -> bool {
316		self.ref_time > other.ref_time && self.proof_size > other.proof_size
317	}
318
319	/// Returns true if any of `self`'s constituent weights is strictly less than that of the
320	/// `other`'s, otherwise returns false.
321	pub const fn any_lt(self, other: Self) -> bool {
322		self.ref_time < other.ref_time || self.proof_size < other.proof_size
323	}
324
325	/// Returns true if all of `self`'s constituent weights is strictly less than that of the
326	/// `other`'s, otherwise returns false.
327	pub const fn all_lt(self, other: Self) -> bool {
328		self.ref_time < other.ref_time && self.proof_size < other.proof_size
329	}
330
331	/// Returns true if any of `self`'s constituent weights is greater than or equal to that of the
332	/// `other`'s, otherwise returns false.
333	pub const fn any_gte(self, other: Self) -> bool {
334		self.ref_time >= other.ref_time || self.proof_size >= other.proof_size
335	}
336
337	/// Returns true if all of `self`'s constituent weights is greater than or equal to that of the
338	/// `other`'s, otherwise returns false.
339	pub const fn all_gte(self, other: Self) -> bool {
340		self.ref_time >= other.ref_time && self.proof_size >= other.proof_size
341	}
342
343	/// Returns true if any of `self`'s constituent weights is less than or equal to that of the
344	/// `other`'s, otherwise returns false.
345	pub const fn any_lte(self, other: Self) -> bool {
346		self.ref_time <= other.ref_time || self.proof_size <= other.proof_size
347	}
348
349	/// Returns true if all of `self`'s constituent weights is less than or equal to that of the
350	/// `other`'s, otherwise returns false.
351	pub const fn all_lte(self, other: Self) -> bool {
352		self.ref_time <= other.ref_time && self.proof_size <= other.proof_size
353	}
354
355	/// Returns true if any of `self`'s constituent weights is equal to that of the `other`'s,
356	/// otherwise returns false.
357	pub const fn any_eq(self, other: Self) -> bool {
358		self.ref_time == other.ref_time || self.proof_size == other.proof_size
359	}
360
361	// NOTE: `all_eq` does not exist, as it's simply the `eq` method from the `PartialEq` trait.
362}
363
364impl Zero for Weight {
365	fn zero() -> Self {
366		Self::zero()
367	}
368
369	fn is_zero(&self) -> bool {
370		self == &Self::zero()
371	}
372}
373
374impl Add for Weight {
375	type Output = Self;
376	fn add(self, rhs: Self) -> Self {
377		Self {
378			ref_time: self.ref_time + rhs.ref_time,
379			proof_size: self.proof_size + rhs.proof_size,
380		}
381	}
382}
383
384impl Sub for Weight {
385	type Output = Self;
386	fn sub(self, rhs: Self) -> Self {
387		Self {
388			ref_time: self.ref_time - rhs.ref_time,
389			proof_size: self.proof_size - rhs.proof_size,
390		}
391	}
392}
393
394impl<T> Mul<T> for Weight
395where
396	T: Mul<u64, Output = u64> + Copy,
397{
398	type Output = Self;
399	fn mul(self, b: T) -> Self {
400		Self { ref_time: b * self.ref_time, proof_size: b * self.proof_size }
401	}
402}
403
404#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))]
405impl From<u64> for Weight {
406	fn from(value: u64) -> Self {
407		Self::from_parts(value, value)
408	}
409}
410
411#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))]
412impl From<(u64, u64)> for Weight {
413	fn from(value: (u64, u64)) -> Self {
414		Self::from_parts(value.0, value.1)
415	}
416}
417
418macro_rules! weight_mul_per_impl {
419	($($t:ty),* $(,)?) => {
420		$(
421			impl Mul<Weight> for $t {
422				type Output = Weight;
423				fn mul(self, b: Weight) -> Weight {
424					Weight {
425						ref_time: self * b.ref_time,
426						proof_size: self * b.proof_size,
427					}
428				}
429			}
430		)*
431	}
432}
433weight_mul_per_impl!(
434	sp_arithmetic::Percent,
435	sp_arithmetic::PerU16,
436	sp_arithmetic::Permill,
437	sp_arithmetic::Perbill,
438	sp_arithmetic::Perquintill,
439);
440
441macro_rules! weight_mul_primitive_impl {
442	($($t:ty),* $(,)?) => {
443		$(
444			impl Mul<Weight> for $t {
445				type Output = Weight;
446				fn mul(self, b: Weight) -> Weight {
447					Weight {
448						ref_time: u64::from(self) * b.ref_time,
449						proof_size: u64::from(self) * b.proof_size,
450					}
451				}
452			}
453		)*
454	}
455}
456weight_mul_primitive_impl!(u8, u16, u32, u64);
457
458impl<T> Div<T> for Weight
459where
460	u64: Div<T, Output = u64>,
461	T: Copy,
462{
463	type Output = Self;
464	fn div(self, b: T) -> Self {
465		Self { ref_time: self.ref_time / b, proof_size: self.proof_size / b }
466	}
467}
468
469impl CheckedAdd for Weight {
470	fn checked_add(&self, rhs: &Self) -> Option<Self> {
471		self.checked_add(rhs)
472	}
473}
474
475impl CheckedSub for Weight {
476	fn checked_sub(&self, rhs: &Self) -> Option<Self> {
477		self.checked_sub(rhs)
478	}
479}
480
481impl core::fmt::Display for Weight {
482	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
483		write!(f, "Weight(ref_time: {}, proof_size: {})", self.ref_time, self.proof_size)
484	}
485}
486
487impl Bounded for Weight {
488	fn min_value() -> Self {
489		Zero::zero()
490	}
491	fn max_value() -> Self {
492		Self::MAX
493	}
494}
495
496impl AddAssign for Weight {
497	fn add_assign(&mut self, other: Self) {
498		*self = Self {
499			ref_time: self.ref_time + other.ref_time,
500			proof_size: self.proof_size + other.proof_size,
501		};
502	}
503}
504
505impl SubAssign for Weight {
506	fn sub_assign(&mut self, other: Self) {
507		*self = Self {
508			ref_time: self.ref_time - other.ref_time,
509			proof_size: self.proof_size - other.proof_size,
510		};
511	}
512}
513
514#[cfg(test)]
515mod tests {
516	use super::*;
517
518	#[test]
519	fn is_zero_works() {
520		assert!(Weight::zero().is_zero());
521		assert!(!Weight::from_parts(1, 0).is_zero());
522		assert!(!Weight::from_parts(0, 1).is_zero());
523		assert!(!Weight::MAX.is_zero());
524	}
525
526	#[test]
527	fn from_parts_works() {
528		assert_eq!(Weight::from_parts(0, 0), Weight { ref_time: 0, proof_size: 0 });
529		assert_eq!(Weight::from_parts(5, 5), Weight { ref_time: 5, proof_size: 5 });
530		assert_eq!(
531			Weight::from_parts(u64::MAX, u64::MAX),
532			Weight { ref_time: u64::MAX, proof_size: u64::MAX }
533		);
534	}
535
536	#[test]
537	fn from_all_works() {
538		assert_eq!(Weight::from_all(0), Weight::from_parts(0, 0));
539		assert_eq!(Weight::from_all(5), Weight::from_parts(5, 5));
540		assert_eq!(Weight::from_all(u64::MAX), Weight::from_parts(u64::MAX, u64::MAX));
541	}
542
543	#[test]
544	fn from_u64_works() {
545		assert_eq!(Weight::from_all(0), 0_u64.into());
546		assert_eq!(Weight::from_all(123), 123_u64.into());
547		assert_eq!(Weight::from_all(u64::MAX), u64::MAX.into());
548	}
549
550	#[test]
551	fn from_u64_pair_works() {
552		assert_eq!(Weight::from_parts(0, 1), (0, 1).into());
553		assert_eq!(Weight::from_parts(123, 321), (123u64, 321u64).into());
554		assert_eq!(Weight::from_parts(u64::MAX, 0), (u64::MAX, 0).into());
555	}
556
557	#[test]
558	fn saturating_reduce_works() {
559		let mut weight = Weight::from_parts(10, 20);
560		weight.saturating_reduce(Weight::from_all(5));
561		assert_eq!(weight, Weight::from_parts(5, 15));
562		weight.saturating_reduce(Weight::from_all(5));
563		assert_eq!(weight, Weight::from_parts(0, 10));
564		weight.saturating_reduce(Weight::from_all(11));
565		assert!(weight.is_zero());
566		weight.saturating_reduce(Weight::from_all(u64::MAX));
567		assert!(weight.is_zero());
568	}
569
570	#[test]
571	fn checked_accrue_works() {
572		let mut weight = Weight::from_parts(10, 20);
573		assert!(weight.checked_accrue(Weight::from_all(2)).is_some());
574		assert_eq!(weight, Weight::from_parts(12, 22));
575		assert!(weight.checked_accrue(Weight::from_parts(u64::MAX, 0)).is_none());
576		assert!(weight.checked_accrue(Weight::from_parts(0, u64::MAX)).is_none());
577		assert_eq!(weight, Weight::from_parts(12, 22));
578		assert!(weight
579			.checked_accrue(Weight::from_parts(u64::MAX - 12, u64::MAX - 22))
580			.is_some());
581		assert_eq!(weight, Weight::MAX);
582		assert!(weight.checked_accrue(Weight::from_parts(1, 0)).is_none());
583		assert!(weight.checked_accrue(Weight::from_parts(0, 1)).is_none());
584		assert_eq!(weight, Weight::MAX);
585	}
586
587	#[test]
588	fn checked_reduce_works() {
589		let mut weight = Weight::from_parts(10, 20);
590		assert!(weight.checked_reduce(Weight::from_all(2)).is_some());
591		assert_eq!(weight, Weight::from_parts(8, 18));
592		assert!(weight.checked_reduce(Weight::from_parts(9, 0)).is_none());
593		assert!(weight.checked_reduce(Weight::from_parts(0, 19)).is_none());
594		assert_eq!(weight, Weight::from_parts(8, 18));
595		assert!(weight.checked_reduce(Weight::from_parts(8, 0)).is_some());
596		assert_eq!(weight, Weight::from_parts(0, 18));
597		assert!(weight.checked_reduce(Weight::from_parts(0, 18)).is_some());
598		assert!(weight.is_zero());
599	}
600
601	#[test]
602	fn checked_div_per_component_works() {
603		assert_eq!(
604			Weight::from_parts(10, 20).checked_div_per_component(&Weight::from_parts(2, 10)),
605			Some(2)
606		);
607		assert_eq!(
608			Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(2, 10)),
609			Some(5)
610		);
611		assert_eq!(
612			Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(1, 10)),
613			Some(10)
614		);
615		assert_eq!(
616			Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(2, 1)),
617			Some(5)
618		);
619		assert_eq!(
620			Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(0, 10)),
621			Some(20)
622		);
623		assert_eq!(
624			Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(1, 0)),
625			Some(10)
626		);
627		assert_eq!(
628			Weight::from_parts(0, 200).checked_div_per_component(&Weight::from_parts(2, 3)),
629			Some(0)
630		);
631		assert_eq!(
632			Weight::from_parts(10, 0).checked_div_per_component(&Weight::from_parts(2, 3)),
633			Some(0)
634		);
635		assert_eq!(
636			Weight::from_parts(10, 200).checked_div_per_component(&Weight::from_parts(0, 0)),
637			None,
638		);
639		assert_eq!(
640			Weight::from_parts(0, 0).checked_div_per_component(&Weight::from_parts(0, 0)),
641			None,
642		);
643	}
644}