From e9bbb6e1686cb5d215c06d818cbf864185744119 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Mon, 12 Apr 2021 18:11:27 -0400 Subject: [PATCH 1/4] Added command line options for output, headers, and templates --- bencher/src/handler.rs | 84 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/bencher/src/handler.rs b/bencher/src/handler.rs index 43a146170..6edeee05e 100644 --- a/bencher/src/handler.rs +++ b/bencher/src/handler.rs @@ -88,6 +88,40 @@ impl handlebars::HelperDef for JoinHelper { } fn write(benchmarks: Vec) { + let mut template_path: Option<&str> = None; + let mut output_path: Option<&str> = None; + let mut header_path: Option<&str> = None; + + let args: Vec = ::std::env::args().collect(); + + let mut options: Vec<(&str, &str)> = Vec::new(); + + let mut prev: Option<&str> = None; + + // Iterate over args excluding the binary path and features arg + // Parse the args into (command, argument) pairs + for arg in &args[1..(args.len() - 1)] { + if let Some(p) = prev { + options.push((p, arg)); + prev = None; + } else { + prev = Some(arg); + } + } + + if let Some(opt) = prev { + panic!("No argument passed to option: {}", opt); + } + + for pair in options { + match pair { + ("out", path) => output_path = Some(path), + ("template", path) => template_path = Some(path), + ("header", path) => header_path = Some(path), + (opt, _) => panic!("Option not supported: {}", opt), + } + } + // New Handlebars instance with helpers. let mut handlebars = handlebars::Handlebars::new(); handlebars.register_helper("underscore", Box::new(UnderscoreHelper)); @@ -95,23 +129,52 @@ fn write(benchmarks: Vec) { // Don't HTML escape any characters. handlebars.register_escape_fn(|s| -> String { s.to_string() }); + // Use empty header if a header path is not given. + let header = { + if let Some(path) = header_path { + let header_string = ::std::fs::read_to_string(path) + .expect(&format!("Header file not found at: {}", path)); + + header_string + } else { + String::from("") + } + }; + let hbs_data = TemplateData { - header: "".to_string(), // TODO: header from provided file path + header, benchmarks, }; - const TEMPLATE: &str = include_str!("./template.hbs"); - // TODO: add option to provide custom template + const DEFAULT_TEMPLATE: &str = include_str!("./template.hbs"); - // TODO: add option to change output file path - let mut output_path = ::std::env::current_dir().unwrap(); - output_path.push("src/module_weights.rs"); + // Use the default template if a template is not given. + let template = { + if let Some(path) = template_path { + let template_string = ::std::fs::read_to_string(path) + .expect(&format!("Template file not found at: {}", path)); - let mut output_file = ::std::fs::File::create(output_path).unwrap(); + template_string + } else { + String::from(DEFAULT_TEMPLATE) + } + }; - handlebars - .render_template_to_write(&TEMPLATE, &hbs_data, &mut output_file) - .unwrap(); + // Write weight file to given output path or print to the screen if none is given. + if let Some(path) = output_path { + let mut output_file = ::std::fs::File::create(path) + .expect(&format!("Could not create output file: {}", path)); + + handlebars + .render_template_to_write(&template, &hbs_data, &mut output_file) + .unwrap(); + } else { + let template_string = handlebars + .render_template(&template, &hbs_data) + .unwrap(); + + println!("{}", template_string); + } } /// Handle bench results @@ -145,6 +208,5 @@ pub fn handle(output: Vec) { }) .collect(); - // TODO: check if should write weights file write(data); } From 37269daef19dbff757674d3183b8a97531f546c9 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Mon, 12 Apr 2021 18:31:47 -0400 Subject: [PATCH 2/4] Fixed options to conform to standard cli --- bencher/src/handler.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bencher/src/handler.rs b/bencher/src/handler.rs index 6edeee05e..7f1b24dc5 100644 --- a/bencher/src/handler.rs +++ b/bencher/src/handler.rs @@ -100,7 +100,7 @@ fn write(benchmarks: Vec) { // Iterate over args excluding the binary path and features arg // Parse the args into (command, argument) pairs - for arg in &args[1..(args.len() - 1)] { + for arg in &args[2..(args.len() - 1)] { if let Some(p) = prev { options.push((p, arg)); prev = None; @@ -115,9 +115,9 @@ fn write(benchmarks: Vec) { for pair in options { match pair { - ("out", path) => output_path = Some(path), - ("template", path) => template_path = Some(path), - ("header", path) => header_path = Some(path), + ("--out", path) => output_path = Some(path), + ("--template", path) => template_path = Some(path), + ("--header", path) => header_path = Some(path), (opt, _) => panic!("Option not supported: {}", opt), } } From e436828b7393aaa70a8385dc78e5500b142f9ce4 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Wed, 14 Apr 2021 20:19:51 -0400 Subject: [PATCH 3/4] Added weight-gen cli --- bencher/Cargo.toml | 5 + bencher/src/handler.rs | 189 +++--------------------------------- bencher/src/lib.rs | 11 +++ weight-gen/Cargo.toml | 20 ++++ weight-gen/src/main.rs | 185 +++++++++++++++++++++++++++++++++++ weight-gen/src/template.hbs | 23 +++++ 6 files changed, 255 insertions(+), 178 deletions(-) create mode 100644 weight-gen/Cargo.toml create mode 100644 weight-gen/src/main.rs create mode 100644 weight-gen/src/template.hbs diff --git a/bencher/Cargo.toml b/bencher/Cargo.toml index 0e7cae6bf..cbe74919c 100644 --- a/bencher/Cargo.toml +++ b/bencher/Cargo.toml @@ -7,10 +7,15 @@ version = "0.1.0" authors = ["Laminar Developers "] edition = "2018" +[[bin]] +path = "src/bin.rs" +name = "codegen" + [dependencies] linregress = { version = "0.4.0", optional = true } handlebars = {version = "3.5.2", optional = true } serde = { features = ['derive'], optional = true, version = "1.0.119" } +serde_json = "1.0" codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"], default-features = false } sp-core = { version = "3.0.0", default-features = false } sp-std = { version = "3.0.0", default-features = false } diff --git a/bencher/src/handler.rs b/bencher/src/handler.rs index 7f1b24dc5..313435ddf 100644 --- a/bencher/src/handler.rs +++ b/bencher/src/handler.rs @@ -1,186 +1,12 @@ -use crate::BenchResult; +use crate::{BenchResult, BenchData}; use codec::Decode; use linregress::{FormulaRegressionBuilder, RegressionDataBuilder}; -use serde::Serialize; - -#[derive(Serialize, Default, Debug, Clone)] -struct BenchData { - pub name: String, - pub base_weight: u64, - pub base_reads: u32, - pub base_writes: u32, -} - -#[derive(Serialize, Default, Debug, Clone)] -struct TemplateData { - pub header: String, - pub benchmarks: Vec, -} - -// A Handlebars helper to add an underscore after every 3rd character, -// i.e. a separator for large numbers. -#[derive(Clone, Copy)] -struct UnderscoreHelper; -impl handlebars::HelperDef for UnderscoreHelper { - fn call<'reg: 'rc, 'rc>( - &self, - h: &handlebars::Helper, - _: &handlebars::Handlebars, - _: &handlebars::Context, - _rc: &mut handlebars::RenderContext, - out: &mut dyn handlebars::Output, - ) -> handlebars::HelperResult { - use handlebars::JsonRender; - let param = h.param(0).unwrap(); - let underscore_param = underscore(param.value().render()); - out.write(&underscore_param)?; - Ok(()) - } -} - -// Add an underscore after every 3rd character, i.e. a separator for large -// numbers. -fn underscore(i: Number) -> String -where - Number: std::string::ToString, -{ - let mut s = String::new(); - let i_str = i.to_string(); - let a = i_str.chars().rev().enumerate(); - for (idx, val) in a { - if idx != 0 && idx % 3 == 0 { - s.insert(0, '_'); - } - s.insert(0, val); - } - s -} - -// A helper to join a string of vectors. -#[derive(Clone, Copy)] -struct JoinHelper; -impl handlebars::HelperDef for JoinHelper { - fn call<'reg: 'rc, 'rc>( - &self, - h: &handlebars::Helper, - _: &handlebars::Handlebars, - _: &handlebars::Context, - _rc: &mut handlebars::RenderContext, - out: &mut dyn handlebars::Output, - ) -> handlebars::HelperResult { - use handlebars::JsonRender; - let param = h.param(0).unwrap(); - let value = param.value(); - let joined = if value.is_array() { - value - .as_array() - .unwrap() - .iter() - .map(|v| v.render()) - .collect::>() - .join(" ") - } else { - value.render() - }; - out.write(&joined)?; - Ok(()) - } -} - -fn write(benchmarks: Vec) { - let mut template_path: Option<&str> = None; - let mut output_path: Option<&str> = None; - let mut header_path: Option<&str> = None; - - let args: Vec = ::std::env::args().collect(); - - let mut options: Vec<(&str, &str)> = Vec::new(); - - let mut prev: Option<&str> = None; - - // Iterate over args excluding the binary path and features arg - // Parse the args into (command, argument) pairs - for arg in &args[2..(args.len() - 1)] { - if let Some(p) = prev { - options.push((p, arg)); - prev = None; - } else { - prev = Some(arg); - } - } - - if let Some(opt) = prev { - panic!("No argument passed to option: {}", opt); - } - - for pair in options { - match pair { - ("--out", path) => output_path = Some(path), - ("--template", path) => template_path = Some(path), - ("--header", path) => header_path = Some(path), - (opt, _) => panic!("Option not supported: {}", opt), - } - } - - // New Handlebars instance with helpers. - let mut handlebars = handlebars::Handlebars::new(); - handlebars.register_helper("underscore", Box::new(UnderscoreHelper)); - handlebars.register_helper("join", Box::new(JoinHelper)); - // Don't HTML escape any characters. - handlebars.register_escape_fn(|s| -> String { s.to_string() }); - - // Use empty header if a header path is not given. - let header = { - if let Some(path) = header_path { - let header_string = ::std::fs::read_to_string(path) - .expect(&format!("Header file not found at: {}", path)); - - header_string - } else { - String::from("") - } - }; - - let hbs_data = TemplateData { - header, - benchmarks, - }; - - const DEFAULT_TEMPLATE: &str = include_str!("./template.hbs"); - - // Use the default template if a template is not given. - let template = { - if let Some(path) = template_path { - let template_string = ::std::fs::read_to_string(path) - .expect(&format!("Template file not found at: {}", path)); - - template_string - } else { - String::from(DEFAULT_TEMPLATE) - } - }; - - // Write weight file to given output path or print to the screen if none is given. - if let Some(path) = output_path { - let mut output_file = ::std::fs::File::create(path) - .expect(&format!("Could not create output file: {}", path)); - - handlebars - .render_template_to_write(&template, &hbs_data, &mut output_file) - .unwrap(); - } else { - let template_string = handlebars - .render_template(&template, &hbs_data) - .unwrap(); - - println!("{}", template_string); - } -} +use std::io::Write; /// Handle bench results pub fn handle(output: Vec) { let results = as Decode>::decode(&mut &output[..]).unwrap(); - let data = results + let data: Vec = results .into_iter() .map(|result| { let name = String::from_utf8_lossy(&result.method).to_string(); @@ -208,5 +34,12 @@ pub fn handle(output: Vec) { }) .collect(); - write(data); + if let Ok(json) = serde_json::to_string(&data) { + let stdout = ::std::io::stdout(); + let mut handle = stdout.lock(); + + handle.write_all(&json.as_bytes()).unwrap(); + } else { + eprintln!("Could not write benchdata to JSON"); + } } diff --git a/bencher/src/lib.rs b/bencher/src/lib.rs index 2ef25f776..bd6c51499 100644 --- a/bencher/src/lib.rs +++ b/bencher/src/lib.rs @@ -9,6 +9,8 @@ pub extern crate sp_std; use codec::{Decode, Encode}; use sp_std::prelude::Vec; +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; #[derive(Encode, Decode, Default, Clone, PartialEq, Debug)] pub struct BenchResult { @@ -20,6 +22,15 @@ pub struct BenchResult { pub repeat_writes: u32, } +#[cfg(feature = "std")] +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct BenchData { + pub name: String, + pub base_weight: u64, + pub base_reads: u32, + pub base_writes: u32, +} + mod macros; #[cfg(feature = "std")] diff --git a/weight-gen/Cargo.toml b/weight-gen/Cargo.toml new file mode 100644 index 000000000..81f68bd1a --- /dev/null +++ b/weight-gen/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "weight-gen" +description = "CLI for generating weight from bencher output" +license = "Apache-2.0" +version = "0.1.0" +authors = ["Laminar Developers "] +edition = "2018" + +[dependencies] +serde = { features = ['derive'], optional = true, version = "1.0.119" } +serde_json = "1.0" +clap = "3.0.0-beta.2" +handlebars = {version = "3.5.2", optional = true } + +[features] +default = ["std"] +std = [ + "handlebars", + "serde/std", +] \ No newline at end of file diff --git a/weight-gen/src/main.rs b/weight-gen/src/main.rs new file mode 100644 index 000000000..301f2514e --- /dev/null +++ b/weight-gen/src/main.rs @@ -0,0 +1,185 @@ +use serde::{Serialize, Deserialize}; +use clap::{AppSettings, Clap}; +use std::io::Read; + +#[derive(Clap)] +#[clap(version = "01.0", author = "Laminar Developers ")] +#[clap(setting = AppSettings::ColoredHelp)] +struct Opts { + input: Option, + #[clap(short, long)] + template: Option, + #[clap(short, long)] + header: Option, + #[clap(short, long)] + out: Option, +} + +#[cfg(feature = "std")] +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct BenchData { + pub name: String, + pub base_weight: u64, + pub base_reads: u32, + pub base_writes: u32, +} + +#[derive(Serialize, Default, Debug, Clone)] +struct TemplateData { + pub header: String, + pub benchmarks: Vec, +} + +// A Handlebars helper to add an underscore after every 3rd character, +// i.e. a separator for large numbers. +#[derive(Clone, Copy)] +struct UnderscoreHelper; +impl handlebars::HelperDef for UnderscoreHelper { + fn call<'reg: 'rc, 'rc>( + &self, + h: &handlebars::Helper, + _: &handlebars::Handlebars, + _: &handlebars::Context, + _rc: &mut handlebars::RenderContext, + out: &mut dyn handlebars::Output, + ) -> handlebars::HelperResult { + use handlebars::JsonRender; + let param = h.param(0).unwrap(); + let underscore_param = underscore(param.value().render()); + out.write(&underscore_param)?; + Ok(()) + } +} + +// Add an underscore after every 3rd character, i.e. a separator for large +// numbers. +fn underscore(i: Number) -> String +where + Number: std::string::ToString, +{ + let mut s = String::new(); + let i_str = i.to_string(); + let a = i_str.chars().rev().enumerate(); + for (idx, val) in a { + if idx != 0 && idx % 3 == 0 { + s.insert(0, '_'); + } + s.insert(0, val); + } + s +} + +// A helper to join a string of vectors. +#[derive(Clone, Copy)] +struct JoinHelper; +impl handlebars::HelperDef for JoinHelper { + fn call<'reg: 'rc, 'rc>( + &self, + h: &handlebars::Helper, + _: &handlebars::Handlebars, + _: &handlebars::Context, + _rc: &mut handlebars::RenderContext, + out: &mut dyn handlebars::Output, + ) -> handlebars::HelperResult { + use handlebars::JsonRender; + let param = h.param(0).unwrap(); + let value = param.value(); + let joined = if value.is_array() { + value + .as_array() + .unwrap() + .iter() + .map(|v| v.render()) + .collect::>() + .join(" ") + } else { + value.render() + }; + out.write(&joined)?; + Ok(()) + } +} + +fn parse_stdio() -> Option> { + let mut buffer = String::new(); + let stdin = std::io::stdin(); + let mut handle = stdin.lock(); + + handle.read_to_string(&mut buffer).unwrap(); + + let lines: Vec<&str> = buffer.split("\n").collect(); + for line in lines { + let json = serde_json::from_str(line); + + if let Ok(data) = json { + return Some(data); + } + } + + None +} + +fn main() { + let opts: Opts = Opts::parse(); + + let benchmarks: Vec = { + if let Some(data) = opts.input { + serde_json::from_str(&data).expect("Could not parse JSON data") + } else { + parse_stdio().expect("Could not parse JSON data") + } + }; + + let mut handlebars = handlebars::Handlebars::new(); + handlebars.register_helper("underscore", Box::new(UnderscoreHelper)); + handlebars.register_helper("join", Box::new(JoinHelper)); + // Don't HTML escape any characters. + handlebars.register_escape_fn(|s| -> String { s.to_string() }); + + // Use empty header if a header path is not given. + let header = { + if let Some(path) = opts.header { + let header_string = ::std::fs::read_to_string(&path) + .expect(&format!("Header file not found at {}", &path)); + + header_string + } else { + String::from("") + } + }; + + let hbs_data = TemplateData { + header, + benchmarks, + }; + + const DEFAULT_TEMPLATE: &str = include_str!("./template.hbs"); + + // Use default template if template path is not given. + let template = { + if let Some(path) = opts.template { + let template_string = ::std::fs::read_to_string(&path) + .expect(&format!("Template file not found at {}", &path)); + + template_string + } else { + String::from(DEFAULT_TEMPLATE) + } + }; + + // Write benchmark to file or print to terminal if output path is not given. + if let Some(path) = opts.out { + let mut output_file = ::std::fs::File::create(&path) + .expect(&format!("Could not create output file at {}", &path)); + + handlebars + .render_template_to_write(&template, &hbs_data, &mut output_file) + .unwrap(); + } else { + let template_string = handlebars + .render_template(&template, &hbs_data) + .unwrap(); + + println!("{}", template_string); + } +} \ No newline at end of file diff --git a/weight-gen/src/template.hbs b/weight-gen/src/template.hbs new file mode 100644 index 000000000..470b00c25 --- /dev/null +++ b/weight-gen/src/template.hbs @@ -0,0 +1,23 @@ +{{header}} + +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(dead_code)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct ModuleWeights(PhantomData); +impl ModuleWeights { + {{~#each benchmarks as |benchmark|}} + pub fn {{benchmark.name~}} () -> Weight { + ({{underscore benchmark.base_weight}} as Weight) + {{~#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as Weight)) + {{~/if}} + {{~#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as Weight)) + {{~/if}} + } + {{~/each}} +} From 01b16d383d83194a83e7b399f244714bc72ab921 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Wed, 14 Apr 2021 20:38:30 -0400 Subject: [PATCH 4/4] fixed dependencies --- bencher/src/handler.rs | 11 ++++++++++- bencher/src/lib.rs | 11 ----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/bencher/src/handler.rs b/bencher/src/handler.rs index 313435ddf..0d174c597 100644 --- a/bencher/src/handler.rs +++ b/bencher/src/handler.rs @@ -1,8 +1,17 @@ -use crate::{BenchResult, BenchData}; +use crate::BenchResult; +use serde::{Serialize, Deserialize}; use codec::Decode; use linregress::{FormulaRegressionBuilder, RegressionDataBuilder}; use std::io::Write; +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +struct BenchData { + pub name: String, + pub base_weight: u64, + pub base_reads: u32, + pub base_writes: u32, +} + /// Handle bench results pub fn handle(output: Vec) { let results = as Decode>::decode(&mut &output[..]).unwrap(); diff --git a/bencher/src/lib.rs b/bencher/src/lib.rs index bd6c51499..2ef25f776 100644 --- a/bencher/src/lib.rs +++ b/bencher/src/lib.rs @@ -9,8 +9,6 @@ pub extern crate sp_std; use codec::{Decode, Encode}; use sp_std::prelude::Vec; -#[cfg(feature = "std")] -use serde::{Serialize, Deserialize}; #[derive(Encode, Decode, Default, Clone, PartialEq, Debug)] pub struct BenchResult { @@ -22,15 +20,6 @@ pub struct BenchResult { pub repeat_writes: u32, } -#[cfg(feature = "std")] -#[derive(Serialize, Deserialize, Default, Debug, Clone)] -pub struct BenchData { - pub name: String, - pub base_weight: u64, - pub base_reads: u32, - pub base_writes: u32, -} - mod macros; #[cfg(feature = "std")]