diff options
Diffstat (limited to 'src/crates')
-rw-r--r-- | src/crates/cli/src/estimate.rs | 127 | ||||
-rw-r--r-- | src/crates/cli/src/main.rs | 6 | ||||
-rw-r--r-- | src/crates/cli/src/place.rs | 53 | ||||
-rw-r--r-- | src/crates/primrose/src/codegen.rs | 34 |
4 files changed, 86 insertions, 134 deletions
diff --git a/src/crates/cli/src/estimate.rs b/src/crates/cli/src/estimate.rs deleted file mode 100644 index 13af9a7..0000000 --- a/src/crates/cli/src/estimate.rs +++ /dev/null @@ -1,127 +0,0 @@ -use std::iter::once; - -use anyhow::{anyhow, bail, Result}; -use argh::FromArgs; -use candelabra::types::Mapping2D; -use cargo_metadata::camino::Utf8PathBuf; -use log::info; -use primrose::ContainerSelection; - -use crate::State; - -/// Estimate the cost of a given set of assignments for a specific project, and detail how that was reached. -#[derive(FromArgs)] -#[argh(subcommand, name = "estimate")] -pub struct Args { - /// the assignments, written <file path> <type name> <implementation> ... - #[argh(positional, greedy)] - assignments: Vec<String>, - - /// also run the benchmarks with this set of assignments - #[argh(switch)] - run: bool, -} - -impl State { - pub fn cmd_estimate(&self, args: Args) -> Result<()> { - if self.projects.len() > 1 { - bail!("estimate must target only one project!") - } - let proj = &self.projects[0]; - - // parse assignment list - if args.assignments.len() % 3 != 0 { - bail!("assignments list must have length divisible by 3"); - } - let assignments = args - .assignments - .iter() - .step_by(3) - .map(Utf8PathBuf::from) - .zip(args.assignments.iter().skip(1).step_by(3)) - .zip(args.assignments.iter().skip(2).step_by(3)) - .collect::<Mapping2D<_, _, _>>(); - - info!("Using assignments: {:?}", &assignments); - - // get breakdown by operation for each assignment - info!("Summary breakdown:"); - - let profile_info = self.inner.usage_profile(proj)?; - let cost_models = profile_info - .keys() - .map(|(file, ctn)| { - ( - file, - ctn, - assignments - .get(file, &ctn) - .ok_or_else(|| anyhow!("missing assignment for {}", ctn)), - ) - }) - .map(|(f, ctn, impl_name)| Ok((f, ctn, self.inner.cost_model(impl_name?)?))) - .collect::<Result<Mapping2D<_, _, _>>>()?; - - let mut acc = Mapping2D::default(); - for (f, ctn) in assignments.keys() { - let ctn_alias = format!("{} / {}", f, ctn); - - let cost_model = cost_models.get(&f, ctn).unwrap(); - let profiler = profile_info - .get(f, *ctn) - .ok_or_else(|| anyhow!("no profiling info for {} / {} - wrong name?", f, ctn))?; - - let breakdown = profiler.cost_breakdown(cost_model); - for (op, i) in breakdown { - acc.insert(&ctn_alias, op, i); - } - } - - self.print_table(acc.into_iter_2d()); - - info!("Breakdown by split"); - profile_info - .iter() - .map(|(f, ctn, p)| (f, ctn, cost_models.get(&f, &ctn).unwrap(), p)) - .map(|(f, ctn, cost_model, p)| { - ( - f, - ctn, - p.0.iter().enumerate().map(|(i, partition)| { - (i, partition.cost_breakdown(cost_model).into_iter()) - }), - ) - }) - .for_each(|(f, ctn, table)| { - info!("{} / {}:", f, ctn); - self.print_table(table); - }); - - if !args.run { - return Ok(()); - } - - info!("Running benchmarks for comparison"); - - self.print_table( - self.inner - .run_benchmarks_with( - proj, - &assignments - .into_iter() - .map(|(f, ctn, sel)| { - ( - f, - ctn.to_string(), - ContainerSelection::Singular(sel.to_string()), - ) - }) - .collect(), - )? - .into_iter() - .map(|(name, results)| (name, once(("avg", results)))), - ); - - Ok(()) - } -} diff --git a/src/crates/cli/src/main.rs b/src/crates/cli/src/main.rs index 552e5a9..9195117 100644 --- a/src/crates/cli/src/main.rs +++ b/src/crates/cli/src/main.rs @@ -5,9 +5,9 @@ use log::info; mod candidates; mod display; -mod estimate; mod library; mod model; +mod place; mod profile; mod select; @@ -38,7 +38,7 @@ pub enum Subcommand { Candidates(candidates::Args), Profile(profile::Args), Select(select::Args), - Estimate(estimate::Args), + Place(place::Args), } fn main() -> Result<()> { @@ -60,7 +60,7 @@ fn main() -> Result<()> { Subcommand::Candidates(a) => state.cmd_candidates(a), Subcommand::Profile(a) => state.cmd_profile(a), Subcommand::Select(a) => state.cmd_select(a), - Subcommand::Estimate(a) => state.cmd_estimate(a), + Subcommand::Place(a) => state.cmd_place(a), } } diff --git a/src/crates/cli/src/place.rs b/src/crates/cli/src/place.rs new file mode 100644 index 0000000..6bfff48 --- /dev/null +++ b/src/crates/cli/src/place.rs @@ -0,0 +1,53 @@ +use anyhow::{bail, Result}; +use argh::FromArgs; +use candelabra::types::Mapping2D; +use cargo_metadata::camino::Utf8PathBuf; +use log::info; +use primrose::ContainerSelection; + +use crate::State; + +/// Generate code for the project that uses the given assignments +#[derive(FromArgs)] +#[argh(subcommand, name = "place")] +pub struct Args { + /// the assignments, written <file path> <type name> <implementation> ... + #[argh(positional, greedy)] + assignments: Vec<String>, +} + +impl State { + pub fn cmd_place(&self, args: Args) -> Result<()> { + if self.projects.len() > 1 { + bail!("estimate must target only one project!") + } + let proj = &self.projects[0]; + + // parse assignment list + if args.assignments.len() % 3 != 0 { + bail!("assignments list must have length divisible by 3"); + } + let assignments = + args.assignments + .iter() + .step_by(3) + .map(Utf8PathBuf::from) + .zip( + args.assignments + .iter() + .skip(1) + .step_by(3) + .map(String::to_string), + ) + .zip(args.assignments.iter().skip(2).step_by(3).map(|s| { + ContainerSelection::parse(s).expect("invalid container selection format") + })) + .collect::<Mapping2D<_, _, _>>(); + + info!("Using assignments: {:?}", &assignments); + + self.inner.save_choices(proj, &assignments)?; + + Ok(()) + } +} diff --git a/src/crates/primrose/src/codegen.rs b/src/crates/primrose/src/codegen.rs index 72c3b41..bf26c8f 100644 --- a/src/crates/primrose/src/codegen.rs +++ b/src/crates/primrose/src/codegen.rs @@ -25,6 +25,25 @@ pub enum ContainerSelection { }, } +impl ContainerSelection { + /// Parse a container selection from a string. + /// The string should either be the name of a single implementation, or in the format + /// `{before}-UNTIL-{threshold}-THEN-{after}` + pub fn parse(inp: &str) -> Option<Self> { + if inp.contains("-UNTIL-") { + let (before, inp) = inp.split_once("-UNTIL-")?; + let (threshold, after) = inp.split_once("-THEN-")?; + Some(Self::Split { + before: before.to_string(), + threshold: threshold.parse().ok()?, + after: after.to_string(), + }) + } else { + Some(Self::Singular(inp.to_string())) + } + } +} + impl std::fmt::Debug for ContainerSelection { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) @@ -117,14 +136,21 @@ fn _{tag_id}<{bounds}>() -> {tag_id}<{vars}> {{ before, threshold, after, - } => format!( - r#" + } => { + let adaptive_container_type = if vars.contains(",") { + "AdaptiveMappingContainer" + } else { + "AdaptiveContainer" + }; + format!( + r#" #[allow(non_snake_case)] fn _{tag_id}<{bounds}>() -> {tag_id}<{vars}> {{ - ::primrose_library::AdaptiveContainer::<{threshold}, {before}<{vars}>, {after}<{vars}>>::default() + ::primrose_library::{adaptive_container_type}::<{threshold}, {before}<{vars}>, {after}<{vars}>, {vars}>::default() }} "# - ), + ) + } }; codegen_block(&inner) |