mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-19 22:34:46 -07:00
263 lines
8.3 KiB
Rust
263 lines
8.3 KiB
Rust
mod chart;
|
|
mod data;
|
|
mod format;
|
|
|
|
use data::{Cutoffs, DualRun, Result, Run, read_dual_runs, read_runs};
|
|
use std::{
|
|
fs,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
pub struct Visualizer {
|
|
workspace_root: PathBuf,
|
|
}
|
|
|
|
impl Visualizer {
|
|
pub fn new(workspace_root: impl AsRef<Path>) -> Self {
|
|
Self {
|
|
workspace_root: workspace_root.as_ref().to_path_buf(),
|
|
}
|
|
}
|
|
|
|
pub fn from_cargo_env() -> Result<Self> {
|
|
let workspace_root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.and_then(|p| p.parent())
|
|
.ok_or("Failed to find workspace root")?
|
|
.to_path_buf();
|
|
Ok(Self { workspace_root })
|
|
}
|
|
|
|
pub fn generate_all_charts(&self) -> Result<()> {
|
|
let benches_dir = self.workspace_root.join("benches");
|
|
if !benches_dir.exists() {
|
|
return Err("Benches directory does not exist".into());
|
|
}
|
|
|
|
for entry in fs::read_dir(&benches_dir)? {
|
|
let path = entry?.path();
|
|
if path.is_dir() {
|
|
let crate_name = path
|
|
.file_name()
|
|
.and_then(|n| n.to_str())
|
|
.ok_or("Invalid crate name")?;
|
|
|
|
println!("Generating charts for crate: {}", crate_name);
|
|
self.generate_crate_charts(&path, crate_name)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn generate_crate_charts(&self, crate_path: &Path, crate_name: &str) -> Result<()> {
|
|
let disk_runs = read_runs(crate_path, "disk.csv")?;
|
|
let memory_runs = read_dual_runs(crate_path, "memory.csv")?;
|
|
let progress_runs = read_runs(crate_path, "progress.csv")?;
|
|
let io_runs = read_dual_runs(crate_path, "io.csv")?;
|
|
|
|
// Combined charts (all runs)
|
|
self.generate_combined_charts(
|
|
crate_path,
|
|
crate_name,
|
|
&disk_runs,
|
|
&memory_runs,
|
|
&progress_runs,
|
|
&io_runs,
|
|
)?;
|
|
|
|
// Individual charts (one per run)
|
|
self.generate_individual_charts(
|
|
crate_path,
|
|
crate_name,
|
|
&disk_runs,
|
|
&memory_runs,
|
|
&progress_runs,
|
|
&io_runs,
|
|
)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn generate_combined_charts(
|
|
&self,
|
|
crate_path: &Path,
|
|
crate_name: &str,
|
|
disk_runs: &[Run],
|
|
memory_runs: &[DualRun],
|
|
progress_runs: &[Run],
|
|
io_runs: &[DualRun],
|
|
) -> Result<()> {
|
|
let cutoffs = Cutoffs::from_progress(progress_runs);
|
|
|
|
// Trim data to per-run cutoffs for fair comparison
|
|
let disk_trimmed = cutoffs.trim_runs(disk_runs);
|
|
let memory_trimmed = cutoffs.trim_dual_runs(memory_runs);
|
|
let io_trimmed = cutoffs.trim_dual_runs(io_runs);
|
|
|
|
if !disk_trimmed.is_empty() {
|
|
chart::generate(
|
|
chart::ChartConfig {
|
|
output_path: &crate_path.join("disk.svg"),
|
|
title: format!("{} — Disk Usage", crate_name),
|
|
y_label: "Disk Usage".to_string(),
|
|
y_format: chart::YAxisFormat::Bytes,
|
|
},
|
|
&disk_trimmed,
|
|
)?;
|
|
}
|
|
|
|
if !memory_trimmed.is_empty() {
|
|
chart::generate_dual(
|
|
chart::ChartConfig {
|
|
output_path: &crate_path.join("memory.svg"),
|
|
title: format!("{} — Memory", crate_name),
|
|
y_label: "Memory".to_string(),
|
|
y_format: chart::YAxisFormat::Bytes,
|
|
},
|
|
&memory_trimmed,
|
|
"(current)",
|
|
"(peak)",
|
|
)?;
|
|
}
|
|
|
|
if !progress_runs.is_empty() {
|
|
let progress_trimmed = cutoffs.trim_runs(progress_runs);
|
|
chart::generate(
|
|
chart::ChartConfig {
|
|
output_path: &crate_path.join("progress.svg"),
|
|
title: format!("{} — Progress", crate_name),
|
|
y_label: "Progress".to_string(),
|
|
y_format: chart::YAxisFormat::Number,
|
|
},
|
|
&progress_trimmed,
|
|
)?;
|
|
}
|
|
|
|
if !io_trimmed.is_empty() {
|
|
// I/O Read (primary column)
|
|
let io_read: Vec<_> = io_trimmed
|
|
.iter()
|
|
.map(|r| Run {
|
|
id: r.id.clone(),
|
|
data: r.primary.clone(),
|
|
})
|
|
.collect();
|
|
chart::generate(
|
|
chart::ChartConfig {
|
|
output_path: &crate_path.join("io_read.svg"),
|
|
title: format!("{} — I/O Read", crate_name),
|
|
y_label: "Bytes Read".to_string(),
|
|
y_format: chart::YAxisFormat::Bytes,
|
|
},
|
|
&io_read,
|
|
)?;
|
|
|
|
// I/O Write (secondary column)
|
|
let io_write: Vec<_> = io_trimmed
|
|
.iter()
|
|
.map(|r| Run {
|
|
id: r.id.clone(),
|
|
data: r.secondary.clone(),
|
|
})
|
|
.collect();
|
|
chart::generate(
|
|
chart::ChartConfig {
|
|
output_path: &crate_path.join("io_write.svg"),
|
|
title: format!("{} — I/O Write", crate_name),
|
|
y_label: "Bytes Written".to_string(),
|
|
y_format: chart::YAxisFormat::Bytes,
|
|
},
|
|
&io_write,
|
|
)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn generate_individual_charts(
|
|
&self,
|
|
crate_path: &Path,
|
|
crate_name: &str,
|
|
disk_runs: &[Run],
|
|
memory_runs: &[DualRun],
|
|
progress_runs: &[Run],
|
|
io_runs: &[DualRun],
|
|
) -> Result<()> {
|
|
for run in disk_runs {
|
|
let run_path = crate_path.join(&run.id);
|
|
chart::generate(
|
|
chart::ChartConfig {
|
|
output_path: &run_path.join("disk.svg"),
|
|
title: format!("{} — Disk Usage", crate_name),
|
|
y_label: "Disk Usage".to_string(),
|
|
y_format: chart::YAxisFormat::Bytes,
|
|
},
|
|
std::slice::from_ref(run),
|
|
)?;
|
|
}
|
|
|
|
for run in memory_runs {
|
|
let run_path = crate_path.join(&run.id);
|
|
chart::generate_dual(
|
|
chart::ChartConfig {
|
|
output_path: &run_path.join("memory.svg"),
|
|
title: format!("{} — Memory", crate_name),
|
|
y_label: "Memory".to_string(),
|
|
y_format: chart::YAxisFormat::Bytes,
|
|
},
|
|
std::slice::from_ref(run),
|
|
"(current)",
|
|
"(peak)",
|
|
)?;
|
|
}
|
|
|
|
for run in progress_runs {
|
|
let run_path = crate_path.join(&run.id);
|
|
chart::generate(
|
|
chart::ChartConfig {
|
|
output_path: &run_path.join("progress.svg"),
|
|
title: format!("{} — Progress", crate_name),
|
|
y_label: "Progress".to_string(),
|
|
y_format: chart::YAxisFormat::Number,
|
|
},
|
|
std::slice::from_ref(run),
|
|
)?;
|
|
}
|
|
|
|
for run in io_runs {
|
|
let run_path = crate_path.join(&run.id);
|
|
|
|
let read_run = Run {
|
|
id: run.id.clone(),
|
|
data: run.primary.clone(),
|
|
};
|
|
chart::generate(
|
|
chart::ChartConfig {
|
|
output_path: &run_path.join("io_read.svg"),
|
|
title: format!("{} — I/O Read", crate_name),
|
|
y_label: "Bytes Read".to_string(),
|
|
y_format: chart::YAxisFormat::Bytes,
|
|
},
|
|
std::slice::from_ref(&read_run),
|
|
)?;
|
|
|
|
let write_run = Run {
|
|
id: run.id.clone(),
|
|
data: run.secondary.clone(),
|
|
};
|
|
chart::generate(
|
|
chart::ChartConfig {
|
|
output_path: &run_path.join("io_write.svg"),
|
|
title: format!("{} — I/O Write", crate_name),
|
|
y_label: "Bytes Written".to_string(),
|
|
y_format: chart::YAxisFormat::Bytes,
|
|
},
|
|
std::slice::from_ref(&write_run),
|
|
)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|