Files
pezkuwi-subxt/substrate/utils/frame/benchmarking-cli/src/writer.rs
T
Shawn Tabrizi 74a583d147 WeightInfo for System, Timestamp, and Utility (#6868)
* initial updates to system

* fix compile

* Update writer.rs

* update weights

* finish system weights

* timestamp weights

* utility weight

* Fix overflow in weight calculations

* add back weight notes

* Update for whitelisted benchmarks

* add trait bounds

* Revert "add trait bounds"

This reverts commit 12b08b7189aa3969f96fa19b211a370860fdb240.

* Update weights for unaccounted for read
2020-08-17 20:59:23 +00:00

226 lines
7.1 KiB
Rust

// This file is part of Substrate.
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Outputs benchmark results to Rust files that can be ingested by the runtime.
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use frame_benchmarking::{BenchmarkBatch, BenchmarkSelector, Analysis};
use sp_runtime::traits::Zero;
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn open_file(path: &str) -> Result<File, std::io::Error> {
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
}
pub fn write_trait(file: &mut File, batches: Vec<BenchmarkBatch>) -> Result<(), std::io::Error> {
let mut current_pallet = Vec::<u8>::new();
// Skip writing if there are no batches
if batches.is_empty() { return Ok(()) }
for batch in &batches {
// Skip writing if there are no results
if batch.results.is_empty() { continue }
let pallet_string = String::from_utf8(batch.pallet.clone()).unwrap();
let benchmark_string = String::from_utf8(batch.benchmark.clone()).unwrap();
// only create new trait definitions when we go to a new pallet
if batch.pallet != current_pallet {
if !current_pallet.is_empty() {
// close trait
write!(file, "}}\n")?;
}
// trait wrapper
write!(file, "// {}\n", pallet_string)?;
write!(file, "pub trait WeightInfo {{\n")?;
current_pallet = batch.pallet.clone()
}
// function name
write!(file, "\tfn {}(", benchmark_string)?;
// params
let components = &batch.results[0].components;
for component in components {
write!(file, "{:?}: u32, ", component.0)?;
}
// return value
write!(file, ") -> Weight;\n")?;
}
// final close trait
write!(file, "}}\n")?;
Ok(())
}
pub fn write_results(batches: &[BenchmarkBatch]) -> Result<(), std::io::Error> {
let mut current_pallet = Vec::<u8>::new();
// Skip writing if there are no batches
if batches.is_empty() { return Ok(()) }
let mut batches_iter = batches.iter().peekable();
let first_pallet = String::from_utf8(
batches_iter.peek().expect("we checked that batches is not empty").pallet.clone()
).unwrap();
let mut file = open_file(&(first_pallet + ".rs"))?;
while let Some(batch) = batches_iter.next() {
// Skip writing if there are no results
if batch.results.is_empty() { continue }
let pallet_string = String::from_utf8(batch.pallet.clone()).unwrap();
let benchmark_string = String::from_utf8(batch.benchmark.clone()).unwrap();
// only create new trait definitions when we go to a new pallet
if batch.pallet != current_pallet {
// auto-generation note
write!(
file,
"//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {}\n\n",
VERSION,
)?;
// allow statements
write!(
file,
"#![allow(unused_parens)]\n#![allow(unused_imports)]\n\n",
)?;
// general imports
write!(
file,
"use frame_support::weights::{{Weight, constants::RocksDbWeight as DbWeight}};\n\n"
)?;
// struct for weights
write!(file, "pub struct WeightInfo;\n")?;
// trait wrapper
write!(file, "impl {}::WeightInfo for WeightInfo {{\n", pallet_string)?;
current_pallet = batch.pallet.clone()
}
// Analysis results
let extrinsic_time = Analysis::min_squares_iqr(&batch.results, BenchmarkSelector::ExtrinsicTime).unwrap();
let reads = Analysis::min_squares_iqr(&batch.results, BenchmarkSelector::Reads).unwrap();
let writes = Analysis::min_squares_iqr(&batch.results, BenchmarkSelector::Writes).unwrap();
// Analysis data may include components that are not used, this filters out anything whose value is zero.
let mut used_components = Vec::new();
let mut used_extrinsic_time = Vec::new();
let mut used_reads = Vec::new();
let mut used_writes = Vec::new();
extrinsic_time.slopes.iter().zip(extrinsic_time.names.iter()).for_each(|(slope, name)| {
if !slope.is_zero() {
if !used_components.contains(&name) { used_components.push(name); }
used_extrinsic_time.push((slope, name));
}
});
reads.slopes.iter().zip(reads.names.iter()).for_each(|(slope, name)| {
if !slope.is_zero() {
if !used_components.contains(&name) { used_components.push(name); }
used_reads.push((slope, name));
}
});
writes.slopes.iter().zip(writes.names.iter()).for_each(|(slope, name)| {
if !slope.is_zero() {
if !used_components.contains(&name) { used_components.push(name); }
used_writes.push((slope, name));
}
});
let all_components = batch.results[0].components
.iter()
.map(|(name, _)| -> String { return name.to_string() })
.collect::<Vec<String>>();
if all_components.len() != used_components.len() {
let mut unused_components = all_components;
unused_components.retain(|x| !used_components.contains(&x));
write!(file, "\t// WARNING! Some components were not used: {:?}\n", unused_components)?;
}
// function name
write!(file, "\tfn {}(", benchmark_string)?;
// params
for component in used_components {
write!(file, "{}: u32, ", component)?;
}
// return value
write!(file, ") -> Weight {{\n")?;
write!(file, "\t\t({} as Weight)\n", extrinsic_time.base.saturating_mul(1000))?;
used_extrinsic_time.iter().try_for_each(|(slope, name)| -> Result<(), std::io::Error> {
write!(file, "\t\t\t.saturating_add(({} as Weight).saturating_mul({} as Weight))\n",
slope.saturating_mul(1000),
name,
)
})?;
if !reads.base.is_zero() {
write!(file, "\t\t\t.saturating_add(DbWeight::get().reads({} as Weight))\n", reads.base)?;
}
used_reads.iter().try_for_each(|(slope, name)| -> Result<(), std::io::Error> {
write!(file, "\t\t\t.saturating_add(DbWeight::get().reads(({} as Weight).saturating_mul({} as Weight)))\n",
slope,
name,
)
})?;
if !writes.base.is_zero() {
write!(file, "\t\t\t.saturating_add(DbWeight::get().writes({} as Weight))\n", writes.base)?;
}
used_writes.iter().try_for_each(|(slope, name)| -> Result<(), std::io::Error> {
write!(file, "\t\t\t.saturating_add(DbWeight::get().writes(({} as Weight).saturating_mul({} as Weight)))\n",
slope,
name,
)
})?;
// close function
write!(file, "\t}}\n")?;
// Check if this is the end of the iterator
if let Some(next) = batches_iter.peek() {
// Next pallet is different than current pallet, so we close up the file and open a new one.
if next.pallet != current_pallet {
write!(file, "}}\n")?;
let next_pallet = String::from_utf8(next.pallet.clone()).unwrap();
file = open_file(&(next_pallet + ".rs"))?;
}
} else {
// This is the end of the iterator, so we close up the final file.
write!(file, "}}\n")?;
}
}
Ok(())
}