Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
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
22 changes: 22 additions & 0 deletions prdoc/pr_7785.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
title: 'pallet scheduler: fix weight and add safety checks'
doc:
- audience: Runtime Dev
description: |-
Changes:
- Add runtime integrity test for scheduler pallet to ensure that lookups use sensible weights
- Check all passed storage names in the omni bencher to be known by FRAME metadata
- Trim storage names in omni bencher to fix V1 bench syntax bug
- Fix V1 bench syntax storage name sanitization for specific Rust versions

I re-ran the benchmarks with the omni-bencher modifications and it did not change the [proof size](https://weights.tasty.limo/compare?repo=polkadot-sdk&threshold=1&path_pattern=substrate%2Fframe%2F**%2Fsrc%2Fweights.rs%2Cpolkadot%2Fruntime%2F*%2Fsrc%2Fweights%2F**%2F*.rs%2Cpolkadot%2Fbridges%2Fmodules%2F*%2Fsrc%2Fweights.rs%2Ccumulus%2F**%2Fweights%2F*.rs%2Ccumulus%2F**%2Fweights%2Fxcm%2F*.rs%2Ccumulus%2F**%2Fsrc%2Fweights.rs&method=asymptotic&ignore_errors=true&unit=proof&old=cc0142510b81dcf1c1a22f7dc164c453c25287e6&new=bb19d78821eaeaf2262f6a23ee45f83dd4f94d29). I reverted [the commit](https://github.com/paritytech/polkadot-sdk/pull/7785/commits/bb19d78821eaeaf2262f6a23ee45f83dd4f94d29) afterwards to reduce the noise for reviewers.
crates:
- name: frame-benchmarking-cli
bump: minor
- name: frame-benchmarking
bump: minor
- name: pallet-scheduler
bump: minor
- name: asset-hub-westend-runtime
bump: minor
- name: westend-runtime
bump: minor
3 changes: 2 additions & 1 deletion substrate/frame/benchmarking/src/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,8 @@ macro_rules! impl_benchmark {
$(
(stringify!($pov_name).as_bytes().to_vec(),
$crate::__private::vec![
$( ( stringify!($storage).as_bytes().to_vec(),
// Stringify sometimes includes spaces, depending on the Rust version.
$( ( stringify!($storage).replace(" ", "").as_bytes().to_vec(),
stringify!($pov_mode).as_bytes().to_vec() ), )*
]),
)*
Expand Down
23 changes: 23 additions & 0 deletions substrate/frame/scheduler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,29 @@ pub mod pallet {
Self::service_agendas(&mut weight_counter, now, u32::MAX);
weight_counter.consumed()
}

#[cfg(feature = "std")]
fn integrity_test() {
fn lookup_weight<T: Config>(s: u32) -> Weight {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe add some comment why is this important and/or what does it ensure exactly and/or why do we need to calculate all the weights bellow.

Isn't this related somehow to MarginalWeightInfo? Can we reuse MarginalWeightInfo here?
Looks like it does something like:

T::WeightInfo::service_agendas_base() +
   T::WeightInfo::service_agenda_base(T::MaxScheduledPerBlock::get())
   <T::WeightInfo as MarginalWeightInfo>::service_task(...);

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes thanks, using that seems easier.

T::WeightInfo::service_agendas_base() +
T::WeightInfo::service_agenda_base(T::MaxScheduledPerBlock::get()) +
T::WeightInfo::service_task_base() +
T::WeightInfo::service_task_fetched(s) +
T::WeightInfo::service_task_named() +
T::WeightInfo::service_task_periodic()
}

let limit = sp_runtime::Perbill::from_percent(90) * T::MaximumWeight::get();

let small_lookup = lookup_weight::<T>(128);
assert!(small_lookup.all_lte(limit), "Must be possible to submit a small lookup");

let medium_lookup = lookup_weight::<T>(1024);
assert!(medium_lookup.all_lte(limit), "Must be possible to submit a medium lookup");

let large_lookup = lookup_weight::<T>(1024 * 1024);
assert!(large_lookup.all_lte(limit), "Must be possible to submit a large lookup");
}
}

#[pallet::call]
Expand Down
48 changes: 44 additions & 4 deletions substrate/utils/frame/benchmarking-cli/src/pallet/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@
let mut timer = time::SystemTime::now();
// Maps (pallet, extrinsic) to its component ranges.
let mut component_ranges = HashMap::<(String, String), Vec<ComponentRange>>::new();
let pov_modes = Self::parse_pov_modes(&benchmarks_to_run)?;
let pov_modes =
Self::parse_pov_modes(&benchmarks_to_run, &storage_info, self.ignore_unknown_pov_mode)?;
let mut failed = Vec::<(String, String)>::new();

'outer: for (i, SelectedBenchmark { pallet, instance, extrinsic, components, .. }) in
Expand Down Expand Up @@ -912,22 +913,27 @@
}

/// Parses the PoV modes per benchmark that were specified by the `#[pov_mode]` attribute.
fn parse_pov_modes(benchmarks: &Vec<SelectedBenchmark>) -> Result<PovModesMap> {
fn parse_pov_modes(
Copy link
Contributor

Choose a reason for hiding this comment

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

@ggwpez just out of curiosity, for example where is the #[pov_mode] used?

Copy link
Member Author

Choose a reason for hiding this comment

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

It is used extensively in the contracts pallet and a few other spots where there are no MaxEncodedLen bounds possible.

#[benchmark(skip_meta, pov_mode = Measured)]

Copy link
Contributor

Choose a reason for hiding this comment

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

ahaaa, I see, #[benchmark(pov_mode = Measured)], I was searching for #[pov_mode],
maybe we could change docs here from #[pov_mode] -> #[benchmark(.., pov_mode = <> or something

benchmarks: &Vec<SelectedBenchmark>,
storage_info: &[StorageInfo],
ignore_unknown_pov_mode: bool,
) -> Result<PovModesMap> {
use std::collections::hash_map::Entry;
let mut parsed = PovModesMap::new();

for SelectedBenchmark { pallet, extrinsic, pov_modes, .. } in benchmarks {
for (pallet_storage, mode) in pov_modes {
let mode = PovEstimationMode::from_str(&mode)?;
let splits = pallet_storage.split("::").collect::<Vec<_>>();
let splits = pallet_storage.replace(" ", "").split("::").collect::<Vec<_>>();

Check failure on line 927 in substrate/utils/frame/benchmarking-cli/src/pallet/command.rs

View workflow job for this annotation

GitHub Actions / cargo-check-all-crate-macos

temporary value dropped while borrowed
if splits.is_empty() || splits.len() > 2 {
return Err(format!(
"Expected 'Pallet::Storage' as storage name but got: {}",
pallet_storage
)
.into())
}
let (pov_pallet, pov_storage) = (splits[0], splits.get(1).unwrap_or(&"ALL"));
let (pov_pallet, pov_storage) =
(splits[0].trim(), splits.get(1).unwrap_or(&"ALL").trim());

match parsed
.entry((pallet.clone(), extrinsic.clone()))
Expand All @@ -946,9 +952,43 @@
}
}
}
log::debug!("Parsed PoV modes: {:?}", parsed);
Self::check_pov_modes(&parsed, storage_info, ignore_unknown_pov_mode)?;

Ok(parsed)
}

fn check_pov_modes(
pov_modes: &PovModesMap,
storage_info: &[StorageInfo],
ignore_unknown_pov_mode: bool,
) -> Result<()> {
// Check that all PoV modes are valid pallet storage keys
for (pallet, storage) in pov_modes.values().map(|i| i.keys()).flatten() {
let (mut found_pallet, mut found_storage) = (false, false);

for info in storage_info {
if pallet == "ALL" || info.pallet_name == pallet.as_bytes() {
found_pallet = true;
}
if storage == "ALL" || info.storage_name == storage.as_bytes() {
found_storage = true;
}
}
if !found_pallet || !found_storage {
let err = format!("The PoV mode references an unknown storage item or pallet: `{}::{}`. You can ignore this warning by specifying `--ignore-unknown-pov-mode`", pallet, storage);

if ignore_unknown_pov_mode {
log::warn!(target: LOG_TARGET, "Error demoted to warning due to `--ignore-unknown-pov-mode`: {}", err);
} else {
return Err(err.into());
}
}
}

Ok(())
}

/// Sanity check the CLI arguments.
fn check_args(
&self,
Expand Down
4 changes: 4 additions & 0 deletions substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ pub struct PalletCmd {
#[arg(long, default_value("max-encoded-len"), value_enum)]
pub default_pov_mode: command::PovEstimationMode,

/// Ignore the error when PoV modes reference unknown storage items or pallets.
#[arg(long)]
pub ignore_unknown_pov_mode: bool,

/// Set the heap pages while running benchmarks. If not set, the default value from the client
/// is used.
#[arg(long)]
Expand Down
Loading