aboutsummaryrefslogtreecommitdiff
path: root/src/crates/cli
diff options
context:
space:
mode:
authorAria <me@aria.rip>2023-11-30 17:49:53 +0000
committerAria <me@aria.rip>2023-11-30 17:49:53 +0000
commit30fd91647bb901350df8b5f72e05968e3a698412 (patch)
tree67915de6d7dd032943b58663186734fe7b16d692 /src/crates/cli
parent329d9ec18957525514dd9bf1d69deaf410e19326 (diff)
feat(cli): WIP profiling
Diffstat (limited to 'src/crates/cli')
-rw-r--r--src/crates/cli/src/main.rs15
-rw-r--r--src/crates/cli/src/profiler/mod.rs117
-rw-r--r--src/crates/cli/src/project.rs19
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>)>)>;