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 43a146170..0d174c597 100644 --- a/bencher/src/handler.rs +++ b/bencher/src/handler.rs @@ -1,9 +1,10 @@ use crate::BenchResult; +use serde::{Serialize, Deserialize}; use codec::Decode; use linregress::{FormulaRegressionBuilder, RegressionDataBuilder}; -use serde::Serialize; +use std::io::Write; -#[derive(Serialize, Default, Debug, Clone)] +#[derive(Serialize, Deserialize, Default, Debug, Clone)] struct BenchData { pub name: String, pub base_weight: u64, @@ -11,113 +12,10 @@ struct BenchData { 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) { - // 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() }); - - let hbs_data = TemplateData { - header: "".to_string(), // TODO: header from provided file path - benchmarks, - }; - - const TEMPLATE: &str = include_str!("./template.hbs"); - // TODO: add option to provide custom template - - // TODO: add option to change output file path - let mut output_path = ::std::env::current_dir().unwrap(); - output_path.push("src/module_weights.rs"); - - let mut output_file = ::std::fs::File::create(output_path).unwrap(); - - handlebars - .render_template_to_write(&TEMPLATE, &hbs_data, &mut output_file) - .unwrap(); -} - /// 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(); @@ -145,6 +43,12 @@ pub fn handle(output: Vec) { }) .collect(); - // TODO: check if should write weights file - 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/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}} +}