diff options
author | Aria <me@aria.rip> | 2023-12-14 18:42:24 +0000 |
---|---|---|
committer | Aria <me@aria.rip> | 2023-12-14 18:42:24 +0000 |
commit | 9da1961a33b20cc64d920ae82f6cc49c42d0c728 (patch) | |
tree | 6429afb84eaa09e2029fea1e161e64378cf7db74 | |
parent | 965e64e8436b24c8523b738ceac84c5a5599d100 (diff) |
refactor(candelabra): split cli, reduce duplication
introduce an invalidation function to the cache helper to get rid of
repetitive code
split cli and candelabra out to separate crates
move most top-level operations into the State struct
-rw-r--r-- | src/Cargo.toml | 5 | ||||
-rw-r--r-- | src/crates/candelabra/Cargo.toml | 19 | ||||
-rw-r--r-- | src/crates/candelabra/src/cache.rs (renamed from src/crates/cli/src/cache.rs) | 29 | ||||
-rw-r--r-- | src/crates/candelabra/src/candidates.rs | 116 | ||||
-rw-r--r-- | src/crates/candelabra/src/cost/benchmark.rs (renamed from src/crates/cli/src/cost/benchmark.rs) | 0 | ||||
-rw-r--r-- | src/crates/candelabra/src/cost/fit.rs (renamed from src/crates/cli/src/cost/fit.rs) | 2 | ||||
-rw-r--r-- | src/crates/candelabra/src/cost/mod.rs (renamed from src/crates/cli/src/cost/mod.rs) | 80 | ||||
-rw-r--r-- | src/crates/candelabra/src/lib.rs | 41 | ||||
-rw-r--r-- | src/crates/candelabra/src/paths.rs (renamed from src/crates/cli/src/paths.rs) | 6 | ||||
-rw-r--r-- | src/crates/candelabra/src/profiler.rs (renamed from src/crates/cli/src/profiler/mod.rs) | 25 | ||||
-rw-r--r-- | src/crates/candelabra/src/project.rs (renamed from src/crates/cli/src/project.rs) | 0 | ||||
-rw-r--r-- | src/crates/cli/Cargo.toml | 17 | ||||
-rw-r--r-- | src/crates/cli/src/candidates.rs | 136 | ||||
-rw-r--r-- | src/crates/cli/src/cmd.rs | 39 | ||||
-rw-r--r-- | src/crates/cli/src/main.rs | 143 |
15 files changed, 293 insertions, 365 deletions
diff --git a/src/Cargo.toml b/src/Cargo.toml index d3658ac..3b0a7ba 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -3,8 +3,9 @@ resolver = "2" members = [ "crates/primrose", "crates/library", - "crates/cli", - "crates/benchmarker" + "crates/benchmarker", + "crates/candelabra", + "crates/cli" ] [workspace.dependencies] diff --git a/src/crates/candelabra/Cargo.toml b/src/crates/candelabra/Cargo.toml new file mode 100644 index 0000000..909e577 --- /dev/null +++ b/src/crates/candelabra/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "candelabra" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log = { workspace = true } +primrose = { path = "../primrose" } +anyhow = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +camino = "1.1.6" +cargo_metadata = "0.18.1" +glob = "0.3.1" +tempfile = "3" +nalgebra = "0.32.3" +polars = { version = "0.35.4", features = ["describe"] } diff --git a/src/crates/cli/src/cache.rs b/src/crates/candelabra/src/cache.rs index 4775a0f..424b2e7 100644 --- a/src/crates/cli/src/cache.rs +++ b/src/crates/candelabra/src/cache.rs @@ -1,12 +1,13 @@ //! Common utilities for caching results use std::{ + cell::RefCell, collections::hash_map::DefaultHasher, fs::{create_dir_all, metadata, remove_file, File}, hash::{Hash, Hasher}, marker::PhantomData, }; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use glob::glob; use log::{debug, warn}; @@ -15,23 +16,28 @@ use serde_json::{from_reader, to_writer}; /// A filesystem-based K/V cache /// This doesn't deal with key invalidation or anything, just the filesystem/serialisation stuff -pub struct FileCache<K: 'static + ?Sized, V, VR = V> { +pub struct FileCache<K: 'static + ?Sized, V> { base_dir: Utf8PathBuf, - _data: PhantomData<(&'static K, V, VR)>, + validator: RefCell<Box<dyn FnMut(&K, &V) -> bool>>, + _data: PhantomData<(&'static K, V, V)>, } -impl<K: ?Sized + ToString, V: for<'a> Deserialize<'a>, VR: Serialize> FileCache<K, V, VR> { +impl<K: ?Sized + ToString, V: Serialize + for<'a> Deserialize<'a>> FileCache<K, V> { /// Create a new file store in the given directory. - pub fn new(base_dir: Utf8PathBuf) -> Result<Self> { + pub fn new( + base_dir: Utf8PathBuf, + validator: impl FnMut(&K, &V) -> bool + 'static, + ) -> Result<Self> { create_dir_all(base_dir.as_std_path()).context("Error creating cache directory")?; Ok(Self { base_dir, + validator: RefCell::new(Box::new(validator)), _data: PhantomData, }) } /// Store the given value with the given `key` - pub fn put(&self, key: &K, value: &VR) -> Result<()> { + pub fn put(&self, key: &K, value: &V) -> Result<()> { let path = self.path_for(key); let mut file = File::create(path)?; to_writer(&mut file, value)?; @@ -49,7 +55,7 @@ impl<K: ?Sized + ToString, V: for<'a> Deserialize<'a>, VR: Serialize> FileCache< } let file = File::open(&path).context("Error opening cache entry")?; - let contents: V = match from_reader(file) { + let contents: V = match self.attempt_load(key, &file) { Ok(x) => x, Err(e) => { debug!("Invalid cache entry: {}", e); @@ -64,6 +70,15 @@ impl<K: ?Sized + ToString, V: for<'a> Deserialize<'a>, VR: Serialize> FileCache< Ok(Some(contents)) } + fn attempt_load(&self, key: &K, f: &File) -> Result<V> { + let c = from_reader(f)?; + if (self.validator.borrow_mut())(key, &c) { + Ok(c) + } else { + Err(anyhow!("validation function said no")) + } + } + /// Remove value for the given key pub fn remove(&self, key: &K) -> Result<()> { Ok(remove_file(self.path_for(key))?) diff --git a/src/crates/candelabra/src/candidates.rs b/src/crates/candelabra/src/candidates.rs new file mode 100644 index 0000000..0d76862 --- /dev/null +++ b/src/crates/candelabra/src/candidates.rs @@ -0,0 +1,116 @@ +//! Generating and caching primrose candidate results + +use std::{collections::HashMap, fs::metadata, time::SystemTime}; + +use anyhow::{Context, Result}; +use camino::{Utf8Path, Utf8PathBuf}; +use log::{debug, warn}; +use primrose::ContainerSelector; +use serde::{Deserialize, Serialize}; + +use crate::{ + cache::{gen_tree_hash, FileCache}, + paths::Paths, + project::Project, + State, +}; + +/// Names a container type we want to select. +pub type ConTypeName = String; + +/// Name of a container implementation we are considering +pub type ImplName = String; + +/// A list of candidate container types +pub type Candidates = HashMap<ConTypeName, Vec<ImplName>>; + +/// A list of candidates for each selection site, and each file in a given project +pub type ProjectCandidateList = Vec<(Utf8PathBuf, Vec<(ConTypeName, Vec<ImplName>)>)>; + +/// Info for getting & caching candidate types +pub struct CandidatesStore { + pub store: FileCache<Utf8Path, CacheEntry>, + pub lib_hash: u64, +} + +/// Entry in the benchmark cache +#[derive(Serialize, Deserialize, Debug)] +pub struct CacheEntry { + lib_hash: u64, + mod_time: SystemTime, + value: Candidates, +} + +impl CandidatesStore { + /// Create a new store, using the given paths. + /// Benchmarks are cached in `paths.target_dir / candelabra / primrose_results` + pub fn new(paths: &Paths) -> Result<Self> { + let base_dir = paths.target_dir.join("candelabra").join("primrose_results"); + + let lib_hash = + gen_tree_hash(&paths.library_crate).context("Error generating library hash")?; + debug!("Initialised candidate cacher with hash {}", lib_hash); + + Ok(Self { + store: FileCache::new(base_dir, move |k, v: &CacheEntry| { + let mod_time = metadata(k) + .map(|m| m.modified()) + .unwrap_or(Ok(SystemTime::UNIX_EPOCH)) + .unwrap(); + v.lib_hash == lib_hash && v.mod_time == mod_time + })?, + lib_hash, + }) + } +} + +impl State { + /// Run primrose on all files in the given project. + /// Returns a list of all candidates for each container type in each file. + pub fn project_candidate_list(&self, project: &Project) -> Result<ProjectCandidateList> { + let mut all_candidates = Vec::new(); + for file in project.find_primrose_files()? { + let result = match self.candidates.store.find(&file)? { + Some(x) => x.value, + None => self.calc_candidates(&file)?, + }; + + let mut typs = Vec::new(); + for (con_type_id, candidates) in result { + typs.push((con_type_id.clone(), candidates)); + } + all_candidates.push((file, typs)); + } + + Ok(all_candidates) + } + + /// Find candidate types for every selection site in a given path + fn calc_candidates(&self, path: &Utf8Path) -> Result<Candidates> { + let selector = ContainerSelector::from_path( + path.as_std_path(), + self.paths.library_src.as_std_path(), + self.model_size, + ) + .with_context(|| format!("error getting container selector for {}", path))?; + + let candidates: Candidates = selector + .find_all_candidates()? + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect(); + + let mod_time = metadata(path)?.modified()?; + if let Err(e) = self.candidates.store.put( + path, + &CacheEntry { + lib_hash: self.candidates.lib_hash, + value: candidates.clone(), + mod_time, + }, + ) { + warn!("Error caching candidates for {}: {}", path, e); + } + Ok(candidates) + } +} diff --git a/src/crates/cli/src/cost/benchmark.rs b/src/crates/candelabra/src/cost/benchmark.rs index a1e0e18..a1e0e18 100644 --- a/src/crates/cli/src/cost/benchmark.rs +++ b/src/crates/candelabra/src/cost/benchmark.rs diff --git a/src/crates/cli/src/cost/fit.rs b/src/crates/candelabra/src/cost/fit.rs index f4372f1..ace3634 100644 --- a/src/crates/cli/src/cost/fit.rs +++ b/src/crates/candelabra/src/cost/fit.rs @@ -1,5 +1,5 @@ //! Fitting a 3rd-order polynomial to benchmark results -//! Based on code from al-jshen: https://github.com/al-jshen/compute/tree/master +//! Based on code from al-jshen: <https://github.com/al-jshen/compute/tree/master> use super::benchmark::Observation; use na::{Dyn, MatrixXx4, OVector}; diff --git a/src/crates/cli/src/cost/mod.rs b/src/crates/candelabra/src/cost/mod.rs index f3cad13..18bcb79 100644 --- a/src/crates/cli/src/cost/mod.rs +++ b/src/crates/candelabra/src/cost/mod.rs @@ -1,6 +1,9 @@ //! Generating, caching, and using cost models -pub mod benchmark; -pub mod fit; +mod benchmark; +mod fit; + +pub use benchmark::{BenchmarkResult, Results as BenchmarkResults}; +pub use fit::Estimator; use std::collections::HashMap; @@ -11,11 +14,11 @@ use log::{debug, warn}; use primrose::{LibSpec, LibSpecs}; use serde::{Deserialize, Serialize}; -use self::fit::Estimator; use crate::{ cache::{gen_tree_hash, FileCache}, cost::benchmark::run_benchmarks, paths::Paths, + State, }; /// Cost model for a container, capable of estimating cost of each supported operation. @@ -24,21 +27,24 @@ pub struct CostModel { by_op: HashMap<String, Estimator>, } -/// Entry in the benchmark cache +/// Information for getting & caching cost information for container implementations. +pub struct ResultsStore { + store: FileCache<str, CacheEntry>, + lib_specs: LibSpecs, + lib_hash: u64, +} + +/// Entry in the cost info cache #[derive(Serialize, Deserialize)] struct CacheEntry { + /// Hash of the primrose library at the time measurements were taken lib_hash: u64, + + /// The resulting cost model model: CostModel, - results: Results, -} -/// Gets/retrieves benchmark results for container implementations. -/// This caches results, and invalidates them when the library or parameters change. -pub struct ResultsStore { - paths: Paths, - store: FileCache<str, CacheEntry>, - lib_specs: LibSpecs, - lib_hash: u64, + /// The raw benchmark results + results: Results, } impl ResultsStore { @@ -61,49 +67,39 @@ impl ResultsStore { debug!("Initialised benchmark cacher with hash {}", lib_hash); Ok(Self { - store: FileCache::new(base_dir)?, - paths: paths.clone(), + store: FileCache::new(base_dir, move |_, v: &CacheEntry| v.lib_hash == lib_hash)?, lib_specs, lib_hash, }) } +} - /// Get benchmark results for the given type, using cached results if possible and persisting the results for later. +impl State { + /// Get or calculate the cost model for the given type. /// Will panic if `name` is not in library specs. - pub fn get(&self, name: &str) -> Result<CostModel> { - if let Some(results) = self.find(name)? { - debug!("Cache hit for {} benchmarks", name); - Ok(results) - } else { - debug!("Cache miss for {} benchmarks", name); - let results = run_benchmarks(name, &self.paths, &self.lib_specs)?; - let model = build_cost_model(results.clone())?; - if let Err(e) = self.put(name, &model, &results) { - warn!("Error caching benchmark outputs for {}: {}", name, e); - } - Ok(model) + pub fn cost_model(&self, name: &str) -> Result<CostModel> { + match self.results.store.find(&name)? { + Some(x) => Ok(x.model), + None => self.calc_cost_model(&name), } } - /// Attempt to find an up-to-date set of results with the given key - fn find(&self, name: &str) -> Result<Option<CostModel>> { - Ok(self - .store - .find(name)? - .filter(|e| e.lib_hash == self.lib_hash) - .map(|e| e.model)) - } - - /// Store a new set of results with the given key - fn put(&self, name: &str, model: &CostModel, results: &Results) -> Result<()> { - self.store.put( + /// Calculate cost information for the given type + /// Will panic if `name` is not in library specs. + fn calc_cost_model(&self, name: &str) -> Result<CostModel> { + let results = run_benchmarks(name, &self.paths, &self.results.lib_specs)?; + let model = build_cost_model(results.clone())?; + if let Err(e) = self.results.store.put( name, &CacheEntry { - lib_hash: self.lib_hash, + lib_hash: self.results.lib_hash, model: model.clone(), results: results.clone(), }, - ) + ) { + warn!("Error caching benchmark outputs for {}: {}", name, e); + } + Ok(model) } } diff --git a/src/crates/candelabra/src/lib.rs b/src/crates/candelabra/src/lib.rs new file mode 100644 index 0000000..2836e78 --- /dev/null +++ b/src/crates/candelabra/src/lib.rs @@ -0,0 +1,41 @@ +use anyhow::Result; + +use crate::{candidates::CandidatesStore, cost::ResultsStore}; + +extern crate nalgebra as na; + +mod cache; +pub mod candidates; +pub mod cost; +pub mod profiler; + +mod paths; +mod project; +pub use paths::Paths; +pub use project::Project; + +/// Shared state for program execution +pub struct State { + /// Paths used throughout execution + paths: Paths, + + /// Cache for candidate types for primrose files & annotations + candidates: CandidatesStore, + + /// Results and cost models + results: ResultsStore, + + /// The model size used for primrose operations + model_size: usize, +} + +impl State { + pub fn new(paths: Paths) -> Result<Self> { + Ok(Self { + candidates: CandidatesStore::new(&paths)?, + results: ResultsStore::new(&paths)?, + model_size: 3, // TODO + paths, + }) + } +} diff --git a/src/crates/cli/src/paths.rs b/src/crates/candelabra/src/paths.rs index 2b44400..d25c174 100644 --- a/src/crates/cli/src/paths.rs +++ b/src/crates/candelabra/src/paths.rs @@ -1,7 +1,7 @@ -use std::{env, path::PathBuf}; - use camino::Utf8PathBuf; +use std::{env, path::PathBuf}; +/// Paths used throughout execution #[derive(Debug, Clone)] pub struct Paths { pub base: Utf8PathBuf, @@ -12,7 +12,7 @@ pub struct Paths { } impl Paths { - fn from_base(base: Utf8PathBuf) -> Self { + pub fn from_base(base: Utf8PathBuf) -> Self { Paths { library_crate: base.join("crates").join("library"), library_src: base.join("crates").join("library").join("src"), diff --git a/src/crates/cli/src/profiler/mod.rs b/src/crates/candelabra/src/profiler.rs index 24ae544..b240258 100644 --- a/src/crates/cli/src/profiler/mod.rs +++ b/src/crates/candelabra/src/profiler.rs @@ -1,3 +1,5 @@ +//! Profiling applications for info about container usage + use anyhow::{anyhow, bail, Context, Result}; use camino::Utf8Path; use log::{debug, trace}; @@ -15,12 +17,13 @@ use tempfile::tempdir; use crate::project::Project; use crate::State; +/// The information we get from profiling. pub type ProfilerInfo = DataFrame; impl State { - /// Profile all benchmarks for the given project - pub fn profile_all(&self, project: &Project) -> Result<ProfilerInfo> { - self.prepare_for_profiling(project)?; + /// Get/calculate profiler info for the given project. + pub fn calc_profiler_info(&self, project: &Project) -> Result<ProfilerInfo> { + self.project_profiling_prep(project)?; project .benchmarks .iter() @@ -32,16 +35,22 @@ impl State { .ok_or(anyhow!("nothing to run or types are not used"))? } - fn prepare_for_profiling(&self, project: &Project) -> Result<()> { - for (file, candidates) in self.get_all_candidates(project)? { - self.prepare_file(&file, &candidates) + /// Prepare the given project to be profiled, by replacing all candidate types with the profiler wrapper. + fn project_profiling_prep(&self, project: &Project) -> Result<()> { + for (file, candidates) in self.project_candidate_list(project)? { + self.file_profiling_prep(&file, &candidates) .with_context(|| format!("error preparing {} for profiling", file))?; } Ok(()) } - fn prepare_file(&self, file: &Utf8Path, candidates: &[(String, Vec<String>)]) -> Result<()> { + /// Prepare the given file to be profiled, by replacing all candidate types with the profiler wrapper. + fn file_profiling_prep( + &self, + file: &Utf8Path, + candidates: &[(String, Vec<String>)], + ) -> Result<()> { debug!("Setting up {} for profiling", file); let selector = ContainerSelector::from_path( @@ -70,6 +79,7 @@ impl State { Ok(()) } + /// Run the given benchmark on the project, and parse the resulting profiling information. fn profile_benchmark(&self, project: &Project, name: &str) -> Result<DataFrame> { let profiler_out_dir = tempdir()?; debug!( @@ -102,6 +112,7 @@ impl State { } } +/// Parse the output of the profiler fn parse_output(contents: &str) -> Result<DataFrame> { let mut lines = contents.lines().map(i32::from_str); let missing_line_err = || anyhow!("wrong number of lines in "); diff --git a/src/crates/cli/src/project.rs b/src/crates/candelabra/src/project.rs index cc8b4a2..cc8b4a2 100644 --- a/src/crates/cli/src/project.rs +++ b/src/crates/candelabra/src/project.rs diff --git a/src/crates/cli/Cargo.toml b/src/crates/cli/Cargo.toml index 0a3a643..c0180ee 100644 --- a/src/crates/cli/Cargo.toml +++ b/src/crates/cli/Cargo.toml @@ -1,22 +1,9 @@ [package] -name = "candelabra-cli" +name = "cli" version = "0.1.0" edition = "2021" -default-run = "candelabra-cli" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = { workspace = true } -env_logger = { workspace = true } -primrose = { path = "../primrose" } -anyhow = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -camino = "1.1.6" -cargo_metadata = "0.18.1" -argh = "0.1.12" -glob = "0.3.1" -tempfile = "3" -nalgebra = "0.32.3" -polars = { version = "0.35.4", features = ["describe"] } +candelabra = { path = "../candelabra" }
\ No newline at end of file diff --git a/src/crates/cli/src/candidates.rs b/src/crates/cli/src/candidates.rs deleted file mode 100644 index ab713d7..0000000 --- a/src/crates/cli/src/candidates.rs +++ /dev/null @@ -1,136 +0,0 @@ -//! Generating and caching primrose candidate results - -use std::{collections::HashMap, fs::metadata, time::SystemTime}; - -use anyhow::{Context, Result}; -use camino::{Utf8Path, Utf8PathBuf}; -use log::{debug, warn}; -use primrose::ContainerSelector; -use serde::{Deserialize, Serialize}; - -use crate::{ - cache::{gen_tree_hash, FileCache}, - paths::Paths, - project::Project, - State, -}; - -// TODO: Make this adjustable -/// The size of the model used by primrose -const MODEL_SIZE: usize = 3; - -/// Names a container type we want to select. -pub type ConTypeName = String; - -/// Name of a container implementation we are considering -pub type ImplName = String; - -/// A list of candidate container types -pub type Candidates = HashMap<ConTypeName, Vec<ImplName>>; - -/// Entry in the benchmark cache -#[derive(Serialize, Deserialize, Debug)] -struct CacheEntry { - lib_hash: u64, - mod_time: SystemTime, - value: Candidates, -} - -/// Gets/retrieves candidate container types for primrose files. -/// This caches results, and invalidates them when the file changes. -pub struct CandidatesStore { - paths: Paths, - store: FileCache<Utf8Path, CacheEntry>, - lib_hash: u64, -} - -impl CandidatesStore { - /// Create a new store, using the given paths. - /// Benchmarks are cached in `paths.target_dir / candelabra / primrose_results` - pub fn new(paths: &Paths) -> Result<Self> { - let base_dir = paths.target_dir.join("candelabra").join("primrose_results"); - - let lib_hash = - gen_tree_hash(&paths.library_crate).context("Error generating library hash")?; - - debug!("Initialised candidate cacher with hash {}", lib_hash); - - Ok(Self { - store: FileCache::new(base_dir)?, - paths: paths.clone(), - lib_hash, - }) - } - - /// Get benchmark results for the given type, using cached results if possible and persisting the results for later. - /// Will panic if `name` is not in library specs. - pub fn get(&self, src: &Utf8Path) -> Result<Candidates> { - if let Some(results) = self.find(src)? { - debug!("Cache hit for {} candidates", src); - Ok(results) - } else { - debug!("Cache miss for {} candidates", src); - let selector = ContainerSelector::from_path( - src.as_std_path(), - self.paths.library_src.as_std_path(), - MODEL_SIZE, - ) - .with_context(|| format!("error getting container selector for {}", src))?; - - let candidates = selector - .find_all_candidates()? - .into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect(); - - if let Err(e) = self.put(src, &candidates) { - warn!("Error caching candidates for {}: {}", src, e); - } - Ok(candidates) - } - } - - /// Attempt to find an up-to-date set of results with the given key - fn find(&self, src: &Utf8Path) -> Result<Option<Candidates>> { - let mod_time = metadata(src)?.modified()?; - Ok(self - .store - .find(src)? - .filter(|e| e.lib_hash == self.lib_hash && e.mod_time == mod_time) - .map(|e| e.value)) - } - - /// Store a new set of results with the given key - fn put(&self, src: &Utf8Path, results: &Candidates) -> Result<()> { - let mod_time = metadata(src)?.modified()?; - self.store.put( - src, - &CacheEntry { - lib_hash: self.lib_hash, - value: results.clone(), - mod_time, - }, - ) - } -} - -pub type ProjectCandidateList = Vec<(Utf8PathBuf, Vec<(ConTypeName, Vec<ImplName>)>)>; - -impl State { - /// Run primrose on all files in the given project. - /// Returns a list of all candidates for each container type in each file. - pub fn get_all_candidates(&self, project: &Project) -> Result<ProjectCandidateList> { - let mut all_candidates = Vec::new(); - for file in project.find_primrose_files()? { - let result = self.candidates.get(&file)?; - - let mut typs = Vec::new(); - for (con_type_id, candidates) in result { - typs.push((con_type_id.clone(), candidates)); - } - all_candidates.push((file, typs)); - } - - Ok(all_candidates) - } -} diff --git a/src/crates/cli/src/cmd.rs b/src/crates/cli/src/cmd.rs deleted file mode 100644 index 7f9857d..0000000 --- a/src/crates/cli/src/cmd.rs +++ /dev/null @@ -1,39 +0,0 @@ -use argh::FromArgs; - -#[derive(FromArgs)] -/// Find the best performing container type using primrose -pub struct Args { - /// path to Cargo.toml - #[argh(option)] - pub manifest_path: Option<String>, - - /// project to run on, if in a workspace - #[argh(option, short = 'p')] - pub project: Option<String>, - - #[argh(subcommand)] - pub cmd: Subcommand, -} - -#[derive(FromArgs)] -#[argh(subcommand)] -pub enum Subcommand { - Model(ModelSubcommand), - Candidates(CandidatesSubcommand), - Profile(ProfileSubcommand), -} - -#[derive(FromArgs)] -/// Show the cost model for the given implementation -#[argh(subcommand, name = "cost-model")] -pub struct ModelSubcommand {} - -#[derive(FromArgs)] -/// Show the candidate types selected by primrose -#[argh(subcommand, name = "candidates")] -pub struct CandidatesSubcommand {} - -#[derive(FromArgs)] -/// Show the profiling information generated from benchmarks -#[argh(subcommand, name = "profile")] -pub struct ProfileSubcommand {} diff --git a/src/crates/cli/src/main.rs b/src/crates/cli/src/main.rs index 8827084..5bd0f34 100644 --- a/src/crates/cli/src/main.rs +++ b/src/crates/cli/src/main.rs @@ -1,115 +1,32 @@ -use anyhow::{anyhow, Context, Result}; -use candidates::CandidatesStore; -use cmd::{CandidatesSubcommand, ModelSubcommand, ProfileSubcommand}; -use cost::ResultsStore; -use log::info; -use polars::prelude::*; -use project::Project; - -use crate::{ - cmd::{Args, Subcommand}, - paths::Paths, -}; - -extern crate nalgebra as na; - -mod cache; -mod candidates; -mod cmd; -mod cost; -mod paths; -mod profiler; -mod project; - -fn main() -> Result<()> { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); - - let args: Args = argh::from_env(); - - // Build shared state - let paths = Paths::default(); - info!("Using source dir: {:?}", &paths.base); - let state = State { - candidates: CandidatesStore::new(&paths).context("error creating candidate store")?, - results: ResultsStore::new(&paths).context("error creating result store")?, - paths, - model_size: 3, // TODO - }; - - let projects = get_projects(&args).context("failed to find project paths")?; - match args.cmd { - Subcommand::Model(c) => state.cmd_model(projects, c), - Subcommand::Candidates(c) => state.cmd_candidates(projects, c), - Subcommand::Profile(c) => state.cmd_profile(projects, c), - } +fn main() { + println!("Hello, world!"); } -/// Shared state for program execution -pub struct State { - /// Paths used throughout execution - paths: Paths, - - /// Candidate types for primrose files & annotations - candidates: CandidatesStore, - - /// Results and cost models - results: ResultsStore, - - /// The model size used for primrose operations - model_size: usize, -} - -impl State { - pub fn cmd_model(&self, projects: Vec<Project>, c: ModelSubcommand) -> Result<()> { - todo!() - } - - pub fn cmd_candidates(&self, projects: Vec<Project>, c: CandidatesSubcommand) -> Result<()> { - todo!() - } - - pub fn cmd_profile(&self, projects: Vec<Project>, c: ProfileSubcommand) -> Result<()> { - for project in projects { - info!("Profiling project {}", project.name); - let inf = self - .profile_all(&project) - .with_context(|| format!("Error profiling project {}", project.name))?; - - // TODO: More useful output - info!("{:?}", inf); - info!("{} samples", inf.shape().0); - info!("{:?}", inf.describe(None)); - } - - Ok(()) - } -} - -fn get_projects(args: &Args) -> Result<Vec<Project>> { - let mut cmd = cargo_metadata::MetadataCommand::new(); - if let Some(p) = &args.manifest_path { - cmd.manifest_path(p); - } - - let metadata = cmd.exec().context("failed to get manifest metadata")?; - - if let Some(p) = &args.project { - // Select a specific project - Ok(vec![metadata - .packages - .iter() - .find(|pkg| pkg.name == *p) - .map(|pkg| Project::new(pkg.clone())) - .ok_or_else(|| { - anyhow!("specified project does not exist") - })?]) - } else { - // Default to all workspace members - Ok(metadata - .workspace_members - .iter() - .flat_map(|member| metadata.packages.iter().find(|pkg| pkg.id == *member)) - .map(|pkg| Project::new(pkg.clone())) - .collect()) - } -} +// fn get_projects(args: &Args) -> Result<Vec<Project>> { +// let mut cmd = cargo_metadata::MetadataCommand::new(); +// if let Some(p) = &args.manifest_path { +// cmd.manifest_path(p); +// } + +// let metadata = cmd.exec().context("failed to get manifest metadata")?; + +// if let Some(p) = &args.project { +// // Select a specific project +// Ok(vec![metadata +// .packages +// .iter() +// .find(|pkg| pkg.name == *p) +// .map(|pkg| Project::new(pkg.clone())) +// .ok_or_else(|| { +// anyhow!("specified project does not exist") +// })?]) +// } else { +// // Default to all workspace members +// Ok(metadata +// .workspace_members +// .iter() +// .flat_map(|member| metadata.packages.iter().find(|pkg| pkg.id == *member)) +// .map(|pkg| Project::new(pkg.clone())) +// .collect()) +// } +// } |