From efaf20c01b5275c16420304430bc384288df98d8 Mon Sep 17 00:00:00 2001 From: data-pup <16364986+data-pup@users.noreply.github.com> Date: Wed, 25 Dec 2019 14:26:49 -0500 Subject: [PATCH 1/8] move monos.rs into submodule --- analyze/analyses/{monos.rs => monos/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename analyze/analyses/{monos.rs => monos/mod.rs} (100%) diff --git a/analyze/analyses/monos.rs b/analyze/analyses/monos/mod.rs similarity index 100% rename from analyze/analyses/monos.rs rename to analyze/analyses/monos/mod.rs From 680622d31ee15dfeadef30e0b32e9a81fd28f2cf Mon Sep 17 00:00:00 2001 From: data-pup <16364986+data-pup@users.noreply.github.com> Date: Wed, 25 Dec 2019 14:47:03 -0500 Subject: [PATCH 2/8] divide monos into separate submodules --- analyze/analyses/monos/emit.rs | 182 ++++++++++++++++++++++++++++ analyze/analyses/monos/entry.rs | 23 ++++ analyze/analyses/monos/mod.rs | 205 +------------------------------- 3 files changed, 210 insertions(+), 200 deletions(-) create mode 100644 analyze/analyses/monos/emit.rs create mode 100644 analyze/analyses/monos/entry.rs diff --git a/analyze/analyses/monos/emit.rs b/analyze/analyses/monos/emit.rs new file mode 100644 index 00000000..0a3779c3 --- /dev/null +++ b/analyze/analyses/monos/emit.rs @@ -0,0 +1,182 @@ +use std::io; +use std::iter; + +use csv; +use serde_derive::Serialize; + +use crate::formats::json; +use crate::formats::table::{Align, Table}; +use twiggy_ir as ir; +use twiggy_traits as traits; + +use super::entry::MonosEntry; +use super::Monos; + +impl traits::Emit for Monos { + #[cfg(feature = "emit_text")] + fn emit_text(&self, items: &ir::Items, dest: &mut dyn io::Write) -> Result<(), traits::Error> { + struct TableRow { + bloat: Option, + bloat_percent: Option, + size: u32, + size_percent: f64, + name: String, + }; + + // Given an entry representing a generic function and its various + // monomorphizations, return a vector of table rows. + fn process_entry<'a>( + entry: &'a MonosEntry, + total_size: f64, + ) -> impl Iterator + 'a { + let MonosEntry { + name, + insts, + size, + bloat, + } = entry; + + let get_size_percent = move |x: u32| f64::from(x) / total_size * 100.0; + + iter::once(TableRow { + bloat: Some(*bloat), + bloat_percent: Some(get_size_percent(*bloat)), + size: *size, + size_percent: get_size_percent(*size), + name: name.to_string(), + }) + .chain(insts.iter().map(move |(name, size)| TableRow { + bloat: None, + bloat_percent: None, + size: *size, + size_percent: get_size_percent(*size), + name: format!(" {}", name), + })) + } + + let mut table = Table::with_header(vec![ + (Align::Right, "Apprx. Bloat Bytes".into()), + (Align::Right, "Apprx. Bloat %".into()), + (Align::Right, "Bytes".into()), + (Align::Right, "%".into()), + (Align::Left, "Monomorphizations".to_string()), + ]); + + for TableRow { + bloat, + bloat_percent, + size, + size_percent, + name, + } in self + .monos + .iter() + .flat_map(|mono| process_entry(mono, f64::from(items.size()))) + { + table.add_row(vec![ + bloat.map(|b| b.to_string()).unwrap_or_default(), + bloat_percent + .map(|b| format!("{:.2}%", b)) + .unwrap_or_default(), + size.to_string(), + format!("{:.2}%", size_percent), + name.clone(), + ]); + } + write!(dest, "{}", &table)?; + Ok(()) + } + + #[cfg(feature = "emit_json")] + fn emit_json(&self, items: &ir::Items, dest: &mut dyn io::Write) -> Result<(), traits::Error> { + // Given an entry representing a generic function and its various + // monomorphizations, add its information to the given JSON object. + fn process_entry( + entry: &MonosEntry, + obj: &mut json::Object, + total_size: f64, + ) -> Result<(), traits::Error> { + let get_size_percent = |size: u32| (f64::from(size)) / total_size * 100.0; + let MonosEntry { + name, + insts, + size, + bloat, + } = entry; + obj.field("generic", name.as_str())?; + obj.field("approximate_monomorphization_bloat_bytes", *bloat)?; + obj.field( + "approximate_monomorphization_bloat_percent", + get_size_percent(*bloat), + )?; + obj.field("total_size", *size)?; + obj.field("total_size_percent", get_size_percent(*size))?; + let mut monos = obj.array("monomorphizations")?; + for (name, size, size_percent) in insts + .iter() + .map(|(name, size)| (name, size, get_size_percent(*size))) + { + let mut obj = monos.object()?; + obj.field("name", name.as_str())?; + obj.field("shallow_size", *size)?; + obj.field("shallow_size_percent", size_percent)?; + } + Ok(()) + }; + + let items_size = f64::from(items.size()); + let mut arr = json::array(dest)?; + for entry in &self.monos { + let mut obj = arr.object()?; + process_entry(entry, &mut obj, items_size)?; + } + + Ok(()) + } + + #[cfg(feature = "emit_csv")] + fn emit_csv(&self, items: &ir::Items, dest: &mut dyn io::Write) -> Result<(), traits::Error> { + // Calculate the total size of the collection of items, and define a + // helper closure to calculate a percent value for a given u32 size. + let items_size = f64::from(items.size()); + let get_size_percent = |size: u32| (f64::from(size)) / items_size * 100.0; + + #[derive(Debug, Default, Serialize)] + #[serde(rename_all = "PascalCase")] + struct Record { + generic: Option, + approximate_monomorphization_bloat_bytes: Option, + approximate_monomorphization_bloat_percent: Option, + total_size: Option, + total_size_percent: Option, + monomorphizations: Option, + } + + // Given a single `MonosEntry` object, create a record object. + let process_entry = |entry: &MonosEntry| -> Record { + let monos = entry + .insts + .iter() + .map(|(name, _)| name.as_str()) + .collect::>(); + Record { + generic: Some(entry.name.clone()), + approximate_monomorphization_bloat_bytes: Some(entry.bloat), + approximate_monomorphization_bloat_percent: Some(get_size_percent(entry.bloat)), + total_size: Some(entry.size), + total_size_percent: Some(get_size_percent(entry.size)), + monomorphizations: Some(monos.join(", ")), + } + }; + + // Create a CSV writer and iterate through the monomorphization entries. + // Process each record and pass it to the destination to be serialized. + let mut wtr = csv::Writer::from_writer(dest); + for entry in &self.monos { + let record = process_entry(entry); + wtr.serialize(record)?; + wtr.flush()?; + } + Ok(()) + } +} diff --git a/analyze/analyses/monos/entry.rs b/analyze/analyses/monos/entry.rs new file mode 100644 index 00000000..d19cd477 --- /dev/null +++ b/analyze/analyses/monos/entry.rs @@ -0,0 +1,23 @@ +#[derive(Debug, PartialEq, Eq)] +pub(super) struct MonosEntry { + pub name: String, + pub insts: Vec<(String, u32)>, + pub size: u32, + pub bloat: u32, +} + +impl PartialOrd for MonosEntry { + fn partial_cmp(&self, rhs: &MonosEntry) -> Option { + Some(self.cmp(rhs)) + } +} + +impl Ord for MonosEntry { + fn cmp(&self, rhs: &MonosEntry) -> std::cmp::Ordering { + rhs.bloat + .cmp(&self.bloat) + .then(rhs.size.cmp(&self.size)) + .then(self.insts.cmp(&rhs.insts)) + .then(self.name.cmp(&rhs.name)) + } +} diff --git a/analyze/analyses/monos/mod.rs b/analyze/analyses/monos/mod.rs index d0073473..0002e235 100644 --- a/analyze/analyses/monos/mod.rs +++ b/analyze/analyses/monos/mod.rs @@ -1,215 +1,20 @@ use std::cmp; use std::collections::{BTreeMap, BTreeSet}; -use std::io; -use std::iter; -use csv; -use serde_derive::Serialize; - -use crate::formats::json; -use crate::formats::table::{Align, Table}; use twiggy_ir as ir; use twiggy_opt as opt; use twiggy_traits as traits; +mod emit; +mod entry; + +use self::entry::MonosEntry; + #[derive(Debug)] struct Monos { monos: Vec, } -#[derive(Debug, PartialEq, Eq)] -struct MonosEntry { - name: String, - insts: Vec<(String, u32)>, - size: u32, - bloat: u32, -} - -impl PartialOrd for MonosEntry { - fn partial_cmp(&self, rhs: &MonosEntry) -> Option { - Some(self.cmp(rhs)) - } -} - -impl Ord for MonosEntry { - fn cmp(&self, rhs: &MonosEntry) -> cmp::Ordering { - rhs.bloat - .cmp(&self.bloat) - .then(rhs.size.cmp(&self.size)) - .then(self.insts.cmp(&rhs.insts)) - .then(self.name.cmp(&rhs.name)) - } -} - -impl traits::Emit for Monos { - #[cfg(feature = "emit_text")] - fn emit_text(&self, items: &ir::Items, dest: &mut dyn io::Write) -> Result<(), traits::Error> { - struct TableRow { - bloat: Option, - bloat_percent: Option, - size: u32, - size_percent: f64, - name: String, - }; - - // Given an entry representing a generic function and its various - // monomorphizations, return a vector of table rows. - fn process_entry<'a>( - entry: &'a MonosEntry, - total_size: f64, - ) -> impl Iterator + 'a { - let MonosEntry { - name, - insts, - size, - bloat, - } = entry; - - let get_size_percent = move |x: u32| f64::from(x) / total_size * 100.0; - - iter::once(TableRow { - bloat: Some(*bloat), - bloat_percent: Some(get_size_percent(*bloat)), - size: *size, - size_percent: get_size_percent(*size), - name: name.to_string(), - }) - .chain(insts.iter().map(move |(name, size)| TableRow { - bloat: None, - bloat_percent: None, - size: *size, - size_percent: get_size_percent(*size), - name: format!(" {}", name), - })) - } - - let mut table = Table::with_header(vec![ - (Align::Right, "Apprx. Bloat Bytes".into()), - (Align::Right, "Apprx. Bloat %".into()), - (Align::Right, "Bytes".into()), - (Align::Right, "%".into()), - (Align::Left, "Monomorphizations".to_string()), - ]); - - for TableRow { - bloat, - bloat_percent, - size, - size_percent, - name, - } in self - .monos - .iter() - .flat_map(|mono| process_entry(mono, f64::from(items.size()))) - { - table.add_row(vec![ - bloat.map(|b| b.to_string()).unwrap_or_default(), - bloat_percent - .map(|b| format!("{:.2}%", b)) - .unwrap_or_default(), - size.to_string(), - format!("{:.2}%", size_percent), - name.clone(), - ]); - } - write!(dest, "{}", &table)?; - Ok(()) - } - - #[cfg(feature = "emit_json")] - fn emit_json(&self, items: &ir::Items, dest: &mut dyn io::Write) -> Result<(), traits::Error> { - // Given an entry representing a generic function and its various - // monomorphizations, add its information to the given JSON object. - fn process_entry( - entry: &MonosEntry, - obj: &mut json::Object, - total_size: f64, - ) -> Result<(), traits::Error> { - let get_size_percent = |size: u32| (f64::from(size)) / total_size * 100.0; - let MonosEntry { - name, - insts, - size, - bloat, - } = entry; - obj.field("generic", name.as_str())?; - obj.field("approximate_monomorphization_bloat_bytes", *bloat)?; - obj.field( - "approximate_monomorphization_bloat_percent", - get_size_percent(*bloat), - )?; - obj.field("total_size", *size)?; - obj.field("total_size_percent", get_size_percent(*size))?; - let mut monos = obj.array("monomorphizations")?; - for (name, size, size_percent) in insts - .iter() - .map(|(name, size)| (name, size, get_size_percent(*size))) - { - let mut obj = monos.object()?; - obj.field("name", name.as_str())?; - obj.field("shallow_size", *size)?; - obj.field("shallow_size_percent", size_percent)?; - } - Ok(()) - }; - - let items_size = f64::from(items.size()); - let mut arr = json::array(dest)?; - for entry in &self.monos { - let mut obj = arr.object()?; - process_entry(entry, &mut obj, items_size)?; - } - - Ok(()) - } - - #[cfg(feature = "emit_csv")] - fn emit_csv(&self, items: &ir::Items, dest: &mut dyn io::Write) -> Result<(), traits::Error> { - // Calculate the total size of the collection of items, and define a - // helper closure to calculate a percent value for a given u32 size. - let items_size = f64::from(items.size()); - let get_size_percent = |size: u32| (f64::from(size)) / items_size * 100.0; - - #[derive(Debug, Default, Serialize)] - #[serde(rename_all = "PascalCase")] - struct Record { - generic: Option, - approximate_monomorphization_bloat_bytes: Option, - approximate_monomorphization_bloat_percent: Option, - total_size: Option, - total_size_percent: Option, - monomorphizations: Option, - } - - // Given a single `MonosEntry` object, create a record object. - let process_entry = |entry: &MonosEntry| -> Record { - let monos = entry - .insts - .iter() - .map(|(name, _)| name.as_str()) - .collect::>(); - Record { - generic: Some(entry.name.clone()), - approximate_monomorphization_bloat_bytes: Some(entry.bloat), - approximate_monomorphization_bloat_percent: Some(get_size_percent(entry.bloat)), - total_size: Some(entry.size), - total_size_percent: Some(get_size_percent(entry.size)), - monomorphizations: Some(monos.join(", ")), - } - }; - - // Create a CSV writer and iterate through the monomorphization entries. - // Process each record and pass it to the destination to be serialized. - let mut wtr = csv::Writer::from_writer(dest); - for entry in &self.monos { - let record = process_entry(entry); - wtr.serialize(record)?; - wtr.flush()?; - } - Ok(()) - } -} - /// Find bloaty monomorphizations of generic functions. pub fn monos( items: &mut ir::Items, From 4a870358eaa26447f089eabb7a59e32e0414f4df Mon Sep 17 00:00:00 2001 From: data-pup <16364986+data-pup@users.noreply.github.com> Date: Wed, 25 Dec 2019 14:56:53 -0500 Subject: [PATCH 3/8] outline nested functions --- analyze/analyses/monos/mod.rs | 220 +++++++++++++++++----------------- 1 file changed, 111 insertions(+), 109 deletions(-) diff --git a/analyze/analyses/monos/mod.rs b/analyze/analyses/monos/mod.rs index 0002e235..9f734aba 100644 --- a/analyze/analyses/monos/mod.rs +++ b/analyze/analyses/monos/mod.rs @@ -15,122 +15,124 @@ struct Monos { monos: Vec, } -/// Find bloaty monomorphizations of generic functions. -pub fn monos( - items: &mut ir::Items, - opts: &opt::Monos, -) -> Result, traits::Error> { - // Type alias used to represent a map of generic function names and instantiations. - type MonosMap<'a> = BTreeMap<&'a str, Vec<(String, u32)>>; - - fn collect_monomorphizations<'a>(items: &'a ir::Items) -> MonosMap { - let unsorted_monos: BTreeMap<&'a str, BTreeSet<(String, u32)>> = items - .iter() - .filter_map(|item| { - if let Some(generic) = item.monomorphization_of() { - Some((generic, item)) - } else { - None - } - }) - .fold(BTreeMap::new(), |mut monos, (generic, inst)| { - monos - .entry(generic) - .or_insert_with(BTreeSet::new) - .insert((inst.name().to_string(), inst.size())); - monos +/// Type alias used to represent a map of generic function names and instantiations. +type MonosMap<'a> = BTreeMap<&'a str, Vec<(String, u32)>>; + +/// Collect the monomorphizations of generic functions into a map, then +/// process the entries and sort the resulting vector. +fn collect_monomorphizations<'a>(items: &'a ir::Items) -> MonosMap { + let unsorted_monos: BTreeMap<&'a str, BTreeSet<(String, u32)>> = items + .iter() + .filter_map(|item| { + if let Some(generic) = item.monomorphization_of() { + Some((generic, item)) + } else { + None + } + }) + .fold(BTreeMap::new(), |mut monos, (generic, inst)| { + monos + .entry(generic) + .or_insert_with(BTreeSet::new) + .insert((inst.name().to_string(), inst.size())); + monos + }); + + unsorted_monos + .into_iter() + .map(|(generic, inst_set)| { + let mut insts = inst_set.into_iter().collect::>(); + insts.sort_by(|(a_name, a_size), (b_name, b_size)| { + b_size.cmp(a_size).then(a_name.cmp(b_name)) }); + (generic, insts) + }) + .collect() +} - unsorted_monos - .into_iter() - .map(|(generic, inst_set)| { - let mut insts = inst_set.into_iter().collect::>(); - insts.sort_by(|(a_name, a_size), (b_name, b_size)| { - b_size.cmp(a_size).then(a_name.cmp(b_name)) - }); - (generic, insts) - }) - .collect() - } - - // Helper function usedd to summarize a sequence of `MonosEntry` objects. - // Returns a tuple representing the number of items summarized, the total - // size of the items, and the total approximate potential savings. - fn summarize_entries<'a>(entries: impl Iterator) -> (usize, u32, u32) { - entries.fold( - (0, 0, 0), - |(total_cnt, total_size, total_savings), - MonosEntry { - insts, size, bloat, .. - }| { - ( - total_cnt + 1 + insts.len(), - total_size + size, - total_savings + bloat, - ) - }, - ) - } +/// Helper function usedd to summarize a sequence of `MonosEntry` objects. +/// Returns a tuple representing the number of items summarized, the total +/// size of the items, and the total approximate potential savings. +fn summarize_entries<'a>(entries: impl Iterator) -> (usize, u32, u32) { + entries.fold( + (0, 0, 0), + |(total_cnt, total_size, total_savings), + MonosEntry { + insts, size, bloat, .. + }| { + ( + total_cnt + 1 + insts.len(), + total_size + size, + total_savings + bloat, + ) + }, + ) +} - // Helper function used to summarize a sequence of tuples representing - // instantiations of a generic function. Returns a tuple representing the - // number of instantiations found, and the total size. - fn summarize_insts<'a>(entries: impl Iterator) -> (u32, u32) { - entries.fold((0, 0), |(total_cnt, total_size), (_, size)| { - (total_cnt + 1, total_size + size) - }) - } +/// Helper function used to summarize a sequence of tuples representing +/// instantiations of a generic function. Returns a tuple representing the +/// number of instantiations found, and the total size. +fn summarize_insts<'a>(entries: impl Iterator) -> (u32, u32) { + entries.fold((0, 0), |(total_cnt, total_size), (_, size)| { + (total_cnt + 1, total_size + size) + }) +} - // Find the approximate potential savings by calculating the benefits of - // removing the largest instantiation, and the benefits of removing an - // average instantiation. Returns a tuple containing total size, and bloat. - fn calculate_total_and_bloat(insts: &[(String, u32)]) -> Option<(u32, u32)> { - if let Some(max) = insts.iter().map(|(_, size)| size).max() { - let total_size = insts.iter().map(|(_, size)| size).sum::(); - let inst_cnt = insts.len() as u32; - let size_per_inst = total_size / inst_cnt; - let avg_savings = size_per_inst * (inst_cnt - 1); - let removing_largest_savings = total_size - max; - let approx_potential_savings = cmp::min(avg_savings, removing_largest_savings); - Some((total_size, approx_potential_savings)) - } else { - None - } +/// Find the approximate potential savings by calculating the benefits of +/// removing the largest instantiation, and the benefits of removing an +/// average instantiation. Returns a tuple containing total size, and bloat. +fn calculate_total_and_bloat(insts: &[(String, u32)]) -> Option<(u32, u32)> { + if let Some(max) = insts.iter().map(|(_, size)| size).max() { + let total_size = insts.iter().map(|(_, size)| size).sum::(); + let inst_cnt = insts.len() as u32; + let size_per_inst = total_size / inst_cnt; + let avg_savings = size_per_inst * (inst_cnt - 1); + let removing_largest_savings = total_size - max; + let approx_potential_savings = cmp::min(avg_savings, removing_largest_savings); + Some((total_size, approx_potential_savings)) + } else { + None } +} - // Process all of the monorphizations, into a vector of `MonosEntry` objects. - fn process_monomorphizations(monos_map: MonosMap, opts: &opt::Monos) -> Vec { - let mut monos = monos_map - .into_iter() - .filter_map(|(g, insts)| { - calculate_total_and_bloat(&insts).map(|(total, bloat)| (g, insts, total, bloat)) - }) - .map(|(g, mut insts, t, b)| { - // Truncate `insts` according to the relevant options before - // we map these values into `MonosEntry` objects. - if opts.only_generics() { - insts.truncate(0); - } else { - let max_monos = opts.max_monos() as usize; - let (rem_cnt, rem_size) = summarize_insts(insts.iter().skip(max_monos)); - insts.truncate(max_monos); - if rem_cnt > 0 { - insts.push((format!("... and {} more.", rem_cnt), rem_size)); - } - }; - (g, insts, t, b) - }) - .map(|(name, insts, size, bloat)| MonosEntry { - name: name.to_string(), - insts, - size, - bloat, - }) - .collect::>(); - monos.sort(); - monos - } +/// Process all of the monorphizations, into a vector of `MonosEntry` objects. +fn process_monomorphizations(monos_map: MonosMap, opts: &opt::Monos) -> Vec { + let mut monos = monos_map + .into_iter() + .filter_map(|(g, insts)| { + calculate_total_and_bloat(&insts).map(|(total, bloat)| (g, insts, total, bloat)) + }) + .map(|(g, mut insts, t, b)| { + // Truncate `insts` according to the relevant options before + // we map these values into `MonosEntry` objects. + if opts.only_generics() { + insts.truncate(0); + } else { + let max_monos = opts.max_monos() as usize; + let (rem_cnt, rem_size) = summarize_insts(insts.iter().skip(max_monos)); + insts.truncate(max_monos); + if rem_cnt > 0 { + insts.push((format!("... and {} more.", rem_cnt), rem_size)); + } + }; + (g, insts, t, b) + }) + .map(|(name, insts, size, bloat)| MonosEntry { + name: name.to_string(), + insts, + size, + bloat, + }) + .collect::>(); + monos.sort(); + monos +} +/// Find bloaty monomorphizations of generic functions. +pub fn monos( + items: &mut ir::Items, + opts: &opt::Monos, +) -> Result, traits::Error> { // Collect the options that will be needed. let max_generics = opts.max_generics() as usize; From da5b805f070ff9d1ebca24a06fa0bb57be30dabc Mon Sep 17 00:00:00 2001 From: data-pup <16364986+data-pup@users.noreply.github.com> Date: Wed, 25 Dec 2019 15:57:13 -0500 Subject: [PATCH 4/8] outline summary code --- analyze/analyses/monos/mod.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/analyze/analyses/monos/mod.rs b/analyze/analyses/monos/mod.rs index 9f734aba..1c292292 100644 --- a/analyze/analyses/monos/mod.rs +++ b/analyze/analyses/monos/mod.rs @@ -128,19 +128,11 @@ fn process_monomorphizations(monos_map: MonosMap, opts: &opt::Monos) -> Vec Result, traits::Error> { - // Collect the options that will be needed. +/// Adds entries to summarize remaining rows that will be truncated, and +/// totals for the entire set of monomorphizations. +fn add_stats(mut monos: Vec, opts: &opt::Monos) -> Vec { let max_generics = opts.max_generics() as usize; - // Collect the monomorphizations of generic functions into a map, then - // process the entries and sort the resulting vector. - let monos_map = collect_monomorphizations(&items); - let mut monos = process_monomorphizations(monos_map, &opts); - // Create an entry to represent the remaining rows that will be truncated. let (rem_cnt, rem_size, rem_savings) = summarize_entries(monos.iter().skip(max_generics)); let remaining = MonosEntry { @@ -165,5 +157,16 @@ pub fn monos( monos.push(remaining); } monos.push(total); + monos +} + +/// Find bloaty monomorphizations of generic functions. +pub fn monos( + items: &mut ir::Items, + opts: &opt::Monos, +) -> Result, traits::Error> { + let monos_map = collect_monomorphizations(&items); + let mut monos = process_monomorphizations(monos_map, &opts); + monos = add_stats(monos, &opts); Ok(Box::new(Monos { monos }) as Box<_>) } From c291b8a0b97e1354876f18e8d40820a50f32ad5d Mon Sep 17 00:00:00 2001 From: data-pup <16364986+data-pup@users.noreply.github.com> Date: Wed, 25 Dec 2019 16:13:09 -0500 Subject: [PATCH 5/8] clean up add_stats function --- analyze/analyses/monos/mod.rs | 38 ++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/analyze/analyses/monos/mod.rs b/analyze/analyses/monos/mod.rs index 1c292292..aef214b0 100644 --- a/analyze/analyses/monos/mod.rs +++ b/analyze/analyses/monos/mod.rs @@ -133,27 +133,37 @@ fn process_monomorphizations(monos_map: MonosMap, opts: &opt::Monos) -> Vec, opts: &opt::Monos) -> Vec { let max_generics = opts.max_generics() as usize; - // Create an entry to represent the remaining rows that will be truncated. - let (rem_cnt, rem_size, rem_savings) = summarize_entries(monos.iter().skip(max_generics)); - let remaining = MonosEntry { - name: format!("... and {} more.", rem_cnt), - size: rem_size, - insts: vec![], - bloat: rem_savings, + // Create an entry to represent the remaining rows that will be truncated, + // only if there are more generics than we will display. + let remaining: Option = { + if monos.len() > max_generics { + let (rem_cnt, rem_size, rem_savings) = + summarize_entries(monos.iter().skip(max_generics)); + Some(MonosEntry { + name: format!("... and {} more.", rem_cnt), + size: rem_size, + insts: vec![], + bloat: rem_savings, + }) + } else { + None + } }; // Create an entry to represent the 'total' summary. - let (total_cnt, total_size, total_savings) = summarize_entries(monos.iter()); - let total = MonosEntry { - name: format!("Σ [{} Total Rows]", total_cnt), - size: total_size, - insts: vec![], - bloat: total_savings, + let total = { + let (total_cnt, total_size, total_savings) = summarize_entries(monos.iter()); + MonosEntry { + name: format!("Σ [{} Total Rows]", total_cnt), + size: total_size, + insts: vec![], + bloat: total_savings, + } }; // Truncate the vector, and add the 'remaining' and 'total' summary entries. monos.truncate(max_generics); - if rem_cnt > 0 { + if let Some(remaining) = remaining { monos.push(remaining); } monos.push(total); From 2c9a72a0f6c7f500b95ad107fba3d67fd240d3a0 Mon Sep 17 00:00:00 2001 From: data-pup <16364986+data-pup@users.noreply.github.com> Date: Wed, 25 Dec 2019 17:26:24 -0500 Subject: [PATCH 6/8] filtering monomorphizations --- analyze/analyses/monos/mod.rs | 21 ++++++++++--- opt/definitions.rs | 31 +++++++++++++++++-- twiggy/tests/all/expectations/monos_functions | 11 +++++++ twiggy/tests/all/expectations/monos_regex | 27 ++++++++++++++++ twiggy/tests/all/monos_tests.rs | 18 +++++++++++ 5 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 twiggy/tests/all/expectations/monos_functions create mode 100644 twiggy/tests/all/expectations/monos_regex diff --git a/analyze/analyses/monos/mod.rs b/analyze/analyses/monos/mod.rs index aef214b0..12835926 100644 --- a/analyze/analyses/monos/mod.rs +++ b/analyze/analyses/monos/mod.rs @@ -1,6 +1,7 @@ use std::cmp; use std::collections::{BTreeMap, BTreeSet}; +use regex; use twiggy_ir as ir; use twiggy_opt as opt; use twiggy_traits as traits; @@ -20,7 +21,14 @@ type MonosMap<'a> = BTreeMap<&'a str, Vec<(String, u32)>>; /// Collect the monomorphizations of generic functions into a map, then /// process the entries and sort the resulting vector. -fn collect_monomorphizations<'a>(items: &'a ir::Items) -> MonosMap { +fn collect_monomorphizations<'a>( + items: &'a ir::Items, + opts: &opt::Monos, +) -> Result, traits::Error> { + let args_given = !opts.functions().is_empty(); + let using_regexps = opts.using_regexps(); + let regexps = regex::RegexSet::new(opts.functions())?; + let unsorted_monos: BTreeMap<&'a str, BTreeSet<(String, u32)>> = items .iter() .filter_map(|item| { @@ -30,6 +38,11 @@ fn collect_monomorphizations<'a>(items: &'a ir::Items) -> MonosMap { None } }) + .filter(|(generic, inst)| match (args_given, using_regexps) { + (true, true) => regexps.is_match(generic), + (true, false) => opts.functions().iter().any(|name| name == generic), + (false, _) => true, + }) .fold(BTreeMap::new(), |mut monos, (generic, inst)| { monos .entry(generic) @@ -38,7 +51,7 @@ fn collect_monomorphizations<'a>(items: &'a ir::Items) -> MonosMap { monos }); - unsorted_monos + Ok(unsorted_monos .into_iter() .map(|(generic, inst_set)| { let mut insts = inst_set.into_iter().collect::>(); @@ -47,7 +60,7 @@ fn collect_monomorphizations<'a>(items: &'a ir::Items) -> MonosMap { }); (generic, insts) }) - .collect() + .collect()) } /// Helper function usedd to summarize a sequence of `MonosEntry` objects. @@ -175,7 +188,7 @@ pub fn monos( items: &mut ir::Items, opts: &opt::Monos, ) -> Result, traits::Error> { - let monos_map = collect_monomorphizations(&items); + let monos_map = collect_monomorphizations(&items, &opts)?; let mut monos = process_monomorphizations(monos_map, &opts); monos = add_stats(monos, &opts); Ok(Box::new(Monos { monos }) as Box<_>) diff --git a/opt/definitions.rs b/opt/definitions.rs index fc503c6d..dbdb619e 100644 --- a/opt/definitions.rs +++ b/opt/definitions.rs @@ -361,14 +361,18 @@ impl Paths { /// List the generic function monomorphizations that are contributing to /// code bloat. #[wasm_bindgen] -#[derive(Clone, Debug)] -#[derive(StructOpt)] +#[derive(Clone, Debug, StructOpt)] pub struct Monos { /// The path to the input binary to size profile. #[cfg(feature = "cli")] #[structopt(parse(from_os_str))] input: path::PathBuf, + /// The names of the generic functions whose monomorphizations + /// should be printed. + #[cfg(feature = "cli")] + functions: Vec, + /// The parse mode for the input binary data. #[cfg(feature = "cli")] #[structopt(short = "d", long = "mode", default_value = "auto")] @@ -411,6 +415,10 @@ pub struct Monos { /// function. Overrides -n #[structopt(long = "all-monos")] all_monos: bool, + + /// Whether or not `names` should be treated as regular expressions. + #[structopt(long = "regex")] + using_regexps: bool, } impl Default for Monos { @@ -425,6 +433,8 @@ impl Default for Monos { #[cfg(feature = "cli")] output_format: Default::default(), + functions: Default::default(), + only_generics: false, max_generics: 10, max_monos: 10, @@ -432,10 +442,22 @@ impl Default for Monos { all_generics_and_monos: false, all_generics: false, all_monos: false, + + using_regexps: false, } } } +impl Monos { + // TODO: wasm-bindgen doesn't support sending Vec across the wasm + // ABI boundary yet. + + /// The functions to find call paths to. + pub fn functions(&self) -> &[String] { + &self.functions + } +} + #[wasm_bindgen] impl Monos { /// Construct a new, default `Monos`. @@ -467,6 +489,11 @@ impl Monos { } } + /// Whether or not `functions` should be treated as regular expressions. + pub fn using_regexps(&self) -> bool { + self.using_regexps + } + /// Set whether to hide individual monomorphizations and only show the /// generic functions. pub fn set_only_generics(&mut self, do_it: bool) { diff --git a/twiggy/tests/all/expectations/monos_functions b/twiggy/tests/all/expectations/monos_functions new file mode 100644 index 00000000..5bbfc09f --- /dev/null +++ b/twiggy/tests/all/expectations/monos_functions @@ -0,0 +1,11 @@ + Apprx. Bloat Bytes │ Apprx. Bloat % │ Bytes │ % │ Monomorphizations +────────────────────┼────────────────┼───────┼───────┼───────────────────────────────────────────────── + 2141 ┊ 3.68% ┊ 3249 ┊ 5.58% ┊ alloc::slice::merge_sort + ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hb3d195f9800bdad6 + ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hfcf2318d7dc71d03 + ┊ ┊ 1033 ┊ 1.77% ┊ alloc::slice::merge_sort::hcfca67f5c75a52ef + 236 ┊ 0.41% ┊ 357 ┊ 0.61% ┊ alloc::slice::insert_head + ┊ ┊ 121 ┊ 0.21% ┊ alloc::slice::insert_head::h2cdb84a455761146 + ┊ ┊ 121 ┊ 0.21% ┊ alloc::slice::insert_head::haf6e08236bab8bde + ┊ ┊ 115 ┊ 0.20% ┊ alloc::slice::insert_head::hed0e79da03eeec8b + 2377 ┊ 4.08% ┊ 3606 ┊ 6.20% ┊ Σ [8 Total Rows] diff --git a/twiggy/tests/all/expectations/monos_regex b/twiggy/tests/all/expectations/monos_regex new file mode 100644 index 00000000..8872711b --- /dev/null +++ b/twiggy/tests/all/expectations/monos_regex @@ -0,0 +1,27 @@ + Apprx. Bloat Bytes │ Apprx. Bloat % │ Bytes │ % │ Monomorphizations +────────────────────┼────────────────┼───────┼───────┼───────────────────────────────────────────────────────────────────────────────────── + 2141 ┊ 3.68% ┊ 3249 ┊ 5.58% ┊ alloc::slice::merge_sort + ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hb3d195f9800bdad6 + ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hfcf2318d7dc71d03 + ┊ ┊ 1033 ┊ 1.77% ┊ alloc::slice::merge_sort::hcfca67f5c75a52ef + 658 ┊ 1.13% ┊ 843 ┊ 1.45% ┊ >::double + ┊ ┊ 185 ┊ 0.32% ┊ >::double::h28f86621ee2a10aa + ┊ ┊ 185 ┊ 0.32% ┊ >::double::h956450b93bdc9e1e + ┊ ┊ 185 ┊ 0.32% ┊ >::double::hcb2fb5861b96a3b0 + ┊ ┊ 176 ┊ 0.30% ┊ >::double::ha715b4e5cc3c60ae + ┊ ┊ 112 ┊ 0.19% ┊ >::double::h77ff8547127c5db2 + 236 ┊ 0.41% ┊ 357 ┊ 0.61% ┊ alloc::slice::insert_head + ┊ ┊ 121 ┊ 0.21% ┊ alloc::slice::insert_head::h2cdb84a455761146 + ┊ ┊ 121 ┊ 0.21% ┊ alloc::slice::insert_head::haf6e08236bab8bde + ┊ ┊ 115 ┊ 0.20% ┊ alloc::slice::insert_head::hed0e79da03eeec8b + 210 ┊ 0.36% ┊ 290 ┊ 0.50% ┊ >::push + ┊ ┊ 80 ┊ 0.14% ┊ >::push::h98b02eda22d1ca25 + ┊ ┊ 71 ┊ 0.12% ┊ >::push::h5729b9e7651ef67b + ┊ ┊ 71 ┊ 0.12% ┊ >::push::hc927b4bedb35b00d + ┊ ┊ 68 ┊ 0.12% ┊ >::push::h9415ef699ccc65d8 + 40 ┊ 0.07% ┊ 60 ┊ 0.10% ┊ as core::ops::drop::Drop>::drop + ┊ ┊ 20 ┊ 0.03% ┊ as core::ops::drop::Drop>::drop::h420ff33e8bc0de30 + ┊ ┊ 20 ┊ 0.03% ┊ as core::ops::drop::Drop>::drop::hab66cea5bda1ed02 + ┊ ┊ 20 ┊ 0.03% ┊ as core::ops::drop::Drop>::drop::hbe243f4c44295f3d + 0 ┊ 0.00% ┊ 871 ┊ 1.50% ┊ ... and 16 more. + 3285 ┊ 5.64% ┊ 5670 ┊ 9.74% ┊ Σ [39 Total Rows] diff --git a/twiggy/tests/all/monos_tests.rs b/twiggy/tests/all/monos_tests.rs index 42f6638b..94a5be1a 100644 --- a/twiggy/tests/all/monos_tests.rs +++ b/twiggy/tests/all/monos_tests.rs @@ -57,3 +57,21 @@ test!( "-f", "json" ); + +test!( + monos_regex, + "monos", + "./fixtures/monos.wasm", + "--regex", + "-m", + "5", + "^ Date: Thu, 26 Dec 2019 23:53:38 -0500 Subject: [PATCH 7/8] fix warning --- analyze/analyses/monos/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyze/analyses/monos/mod.rs b/analyze/analyses/monos/mod.rs index 12835926..636b06aa 100644 --- a/analyze/analyses/monos/mod.rs +++ b/analyze/analyses/monos/mod.rs @@ -38,7 +38,7 @@ fn collect_monomorphizations<'a>( None } }) - .filter(|(generic, inst)| match (args_given, using_regexps) { + .filter(|(generic, _)| match (args_given, using_regexps) { (true, true) => regexps.is_match(generic), (true, false) => opts.functions().iter().any(|name| name == generic), (false, _) => true, From 25806e60083481a5d69a8b343dbdb3741951a862 Mon Sep 17 00:00:00 2001 From: data-pup <16364986+data-pup@users.noreply.github.com> Date: Mon, 6 Jan 2020 17:45:04 -0500 Subject: [PATCH 8/8] fix wasm api build error --- .rustfmt.toml | 3 +++ opt/definitions.rs | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 .rustfmt.toml diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 00000000..f9a31f09 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,3 @@ +# We do not merge derives because of `twiggy-opt` and its build script. +# Merging derives in `opt/definitions` will break the wasm api. +merge_derives = false diff --git a/opt/definitions.rs b/opt/definitions.rs index dbdb619e..5a3bbe32 100644 --- a/opt/definitions.rs +++ b/opt/definitions.rs @@ -1,4 +1,3 @@ - // Fun times ahead! // // Apparently, proc-macros don't play well with `cfg_attr` yet, and their @@ -41,7 +40,7 @@ pub enum Options { /// Find and display code and data that is not transitively referenced by /// any exports or public functions. #[structopt(name = "garbage")] - Garbage(Garbage) + Garbage(Garbage), } /// List the top code size offenders in a binary. @@ -361,7 +360,8 @@ impl Paths { /// List the generic function monomorphizations that are contributing to /// code bloat. #[wasm_bindgen] -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug)] +#[derive(StructOpt)] pub struct Monos { /// The path to the input binary to size profile. #[cfg(feature = "cli")] @@ -370,7 +370,6 @@ pub struct Monos { /// The names of the generic functions whose monomorphizations /// should be printed. - #[cfg(feature = "cli")] functions: Vec, /// The parse mode for the input binary data. @@ -604,7 +603,11 @@ impl Diff { impl Diff { /// The maximum number of items to display. pub fn max_items(&self) -> u32 { - if self.all_items { u32::MAX } else { self.max_items } + if self.all_items { + u32::MAX + } else { + self.max_items + } } /// Whether or not `items` should be treated as regular expressions. @@ -691,7 +694,11 @@ impl Garbage { /// The maximum number of items to display. pub fn max_items(&self) -> u32 { - if self.all_items { u32::MAX } else { self.max_items } + if self.all_items { + u32::MAX + } else { + self.max_items + } } /// Set the maximum number of items to display.