aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/crates/cli/src/estimate.rs127
-rw-r--r--src/crates/cli/src/main.rs6
-rw-r--r--src/crates/cli/src/place.rs53
-rw-r--r--src/crates/primrose/src/codegen.rs34
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)