diff options
author | Aria <me@aria.rip> | 2023-11-30 17:49:53 +0000 |
---|---|---|
committer | Aria <me@aria.rip> | 2023-11-30 17:49:53 +0000 |
commit | 30fd91647bb901350df8b5f72e05968e3a698412 (patch) | |
tree | 67915de6d7dd032943b58663186734fe7b16d692 /src/crates/cli | |
parent | 329d9ec18957525514dd9bf1d69deaf410e19326 (diff) |
feat(cli): WIP profiling
Diffstat (limited to 'src/crates/cli')
-rw-r--r-- | src/crates/cli/src/main.rs | 15 | ||||
-rw-r--r-- | src/crates/cli/src/profiler/mod.rs | 117 | ||||
-rw-r--r-- | src/crates/cli/src/project.rs | 19 |
3 files changed, 145 insertions, 6 deletions
diff --git a/src/crates/cli/src/main.rs b/src/crates/cli/src/main.rs index 92941b2..dd0a537 100644 --- a/src/crates/cli/src/main.rs +++ b/src/crates/cli/src/main.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, Context, Result}; +use candidates::CandidatesStore; use cmd::{CandidatesSubcommand, ModelSubcommand, ProfileSubcommand}; use log::info; use project::Project; @@ -15,6 +16,7 @@ mod candidates; mod cmd; mod cost; mod paths; +mod profiler; mod project; fn main() -> Result<()> { @@ -43,7 +45,18 @@ fn cmd_candidates(paths: Paths, projects: Vec<Project>, c: CandidatesSubcommand) } fn cmd_profile(paths: Paths, projects: Vec<Project>, c: ProfileSubcommand) -> Result<()> { - todo!() + let candidates = CandidatesStore::new(&paths)?; + for project in projects { + info!("Profiling project {}", project.name); + let inf = project + .profile_all(&candidates) + .with_context(|| format!("Error profiling project {}", project.name))?; + + // TODO: More useful output + info!("{:?}", inf); + } + + Ok(()) } fn get_projects(args: &Args) -> Result<Vec<Project>> { diff --git a/src/crates/cli/src/profiler/mod.rs b/src/crates/cli/src/profiler/mod.rs new file mode 100644 index 0000000..b76138a --- /dev/null +++ b/src/crates/cli/src/profiler/mod.rs @@ -0,0 +1,117 @@ +use anyhow::{anyhow, bail, Context, Result}; +use primrose::ContainerSelector; +use std::str::FromStr; +use std::{ + collections::HashMap, + fs::{read_dir, File}, + io::Read, + process::{Command, Stdio}, +}; +use tempfile::tempdir; + +use crate::candidates::CandidatesStore; +use crate::project::Project; + +pub type ProfilerInfo = Vec<HashMap<String, usize>>; + +impl Project { + pub fn profile_all(&self, candidates: &CandidatesStore) -> Result<ProfilerInfo> { + self.prepare_for_profiling(candidates)?; + + // Run all benchmarks + let mut info = ProfilerInfo::default(); + for name in &self.benchmarks { + info.extend( + self.profile_benchmark(name) + .with_context(|| format!("Error profiling benchmark {}", name))?, + ); + } + + Ok(info) + } + + pub fn prepare_for_profiling(&self, candidates: &CandidatesStore) -> Result<()> { + for (file, candidates) in self.get_all_candidates(&candidates)? { + let selector = todo!(); + selector.gen_replacement_file( + candidates + .iter() + .map(|(dest_name, impls)| (dest_name, &impls[0])), + )?; + } + + Ok(()) + } + + fn profile_benchmark(&self, name: &str) -> Result<ProfilerInfo> { + let profiler_out_dir = tempdir()?; + let output = Command::new("cargo") + .current_dir(&self.source_dir) + .args(["bench", "--bench", name]) + .env("PROFILER_OUT_DIR", dbg!(profiler_out_dir.as_ref())) // Where profiler info gets outputted + .stderr(Stdio::inherit()) + .stdout(Stdio::inherit()) + .output()?; + + if !output.status.success() { + bail!("Error running benchmark"); + } + + let mut info = ProfilerInfo::default(); + for file in read_dir(profiler_out_dir)? { + let file = file?; + let mut contents = String::new(); + File::open(file.path())?.read_to_string(&mut contents)?; + + info.push(parse_output(&contents)?); + } + + Ok(info) + } +} + +fn parse_output(contents: &str) -> Result<HashMap<String, usize>> { + let mut lines = contents.lines().map(|l| usize::from_str(l)); + let mut map = HashMap::new(); + + let missing_line_err = || anyhow!("wrong number of lines in "); + map.insert("n".to_string(), lines.next().ok_or_else(missing_line_err)??); + map.insert( + "contains".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + map.insert( + "insert".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + map.insert( + "clear".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + map.insert( + "remove".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + map.insert( + "first".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + map.insert( + "last".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + map.insert( + "nth".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + map.insert( + "push".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + map.insert( + "pop".to_string(), + lines.next().ok_or_else(missing_line_err)??, + ); + + Ok(map) +} diff --git a/src/crates/cli/src/project.rs b/src/crates/cli/src/project.rs index 8d04c94..4f29082 100644 --- a/src/crates/cli/src/project.rs +++ b/src/crates/cli/src/project.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use anyhow::{Context, Result}; -use cargo_metadata::{camino::Utf8PathBuf, Package}; +use cargo_metadata::{camino::Utf8PathBuf, Package, Target}; use glob::glob; use crate::candidates::{CandidatesStore, ConTypeName, ImplName}; @@ -10,7 +10,8 @@ use crate::candidates::{CandidatesStore, ConTypeName, ImplName}; #[derive(Debug, Clone)] pub struct Project { pub name: String, - source_dir: Utf8PathBuf, + pub benchmarks: Vec<String>, + pub source_dir: Utf8PathBuf, } impl Project { @@ -18,6 +19,12 @@ impl Project { Project { name: package.name.clone(), source_dir: package.manifest_path.parent().unwrap().to_path_buf(), + benchmarks: package + .targets + .into_iter() + .filter(Target::is_bench) + .map(|t| t.name) + .collect(), } } @@ -33,17 +40,19 @@ impl Project { /// Run primrose on all files in this project. /// Returns a list of all candidates for each container type in each file. pub fn get_all_candidates(&self, store: &CandidatesStore) -> Result<ProjectCandidateList> { - let mut all_candidates = HashMap::new(); + let mut all_candidates = Vec::new(); for file in self.find_primrose_files()? { let result = store.get(&file)?; + let mut typs = Vec::new(); for (con_type_id, candidates) in result { - all_candidates.insert((file.clone(), con_type_id.clone()), candidates); + typs.push((con_type_id.clone(), candidates)); } + all_candidates.push((file, typs)); } Ok(all_candidates) } } -pub type ProjectCandidateList = HashMap<(Utf8PathBuf, ConTypeName), Vec<ImplName>>; +pub type ProjectCandidateList = Vec<(Utf8PathBuf, Vec<(ConTypeName, Vec<ImplName>)>)>; |