mirror of
https://github.com/qdrant/qdrant.git
synced 2026-07-30 22:51:00 -05:00
90 lines
3.0 KiB
Rust
90 lines
3.0 KiB
Rust
use std::io::Write;
|
|
use std::os::raw::c_int;
|
|
use std::path::Path;
|
|
|
|
use criterion::profiler::Profiler;
|
|
use fs_err as fs;
|
|
use fs_err::File;
|
|
use pprof::ProfilerGuard;
|
|
use pprof::flamegraph::TextTruncateDirection;
|
|
use pprof::protos::Message;
|
|
|
|
/// Small custom profiler that can be used with Criterion to create a flamegraph for benchmarks.
|
|
/// Also see [the Criterion documentation on this][custom-profiler].
|
|
///
|
|
/// ## Example on how to enable the custom profiler:
|
|
///
|
|
/// ```
|
|
/// mod perf;
|
|
/// use perf::FlamegraphProfiler;
|
|
///
|
|
/// fn fibonacci_profiled(criterion: &mut Criterion) {
|
|
/// // Use the criterion struct as normal here.
|
|
/// }
|
|
///
|
|
/// fn custom() -> Criterion {
|
|
/// Criterion::default().with_profiler(FlamegraphProfiler::new())
|
|
/// }
|
|
///
|
|
/// criterion_group! {
|
|
/// name = benches;
|
|
/// config = custom();
|
|
/// targets = fibonacci_profiled
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// The neat thing about this is that it will sample _only_ the benchmark, and not other stuff like
|
|
/// the setup process.
|
|
///
|
|
/// Further, it will only kick in if `--profile-time <time>` is passed to the benchmark binary.
|
|
/// A flamegraph will be created for each individual benchmark in its report directory under
|
|
/// `profile/flamegraph.svg`.
|
|
///
|
|
/// [custom-profiler]: https://bheisler.github.io/criterion.rs/book/user_guide/profiling.html#implementing-in-process-profiling-hooks
|
|
pub struct FlamegraphProfiler<'a> {
|
|
frequency: c_int,
|
|
active_profiler: Option<ProfilerGuard<'a>>,
|
|
}
|
|
|
|
impl FlamegraphProfiler<'_> {
|
|
pub fn new(frequency: c_int) -> Self {
|
|
FlamegraphProfiler {
|
|
frequency,
|
|
active_profiler: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Profiler for FlamegraphProfiler<'_> {
|
|
fn start_profiling(&mut self, _benchmark_id: &str, _benchmark_dir: &Path) {
|
|
self.active_profiler = Some(ProfilerGuard::new(self.frequency).unwrap());
|
|
}
|
|
|
|
fn stop_profiling(&mut self, _benchmark_id: &str, benchmark_dir: &Path) {
|
|
fs::create_dir_all(benchmark_dir).unwrap();
|
|
let pprof_path = benchmark_dir.join("profile.pb");
|
|
let flamegraph_path = benchmark_dir.join("flamegraph.svg");
|
|
eprintln!("\nflamegraph_path = {flamegraph_path:#?}");
|
|
let flamegraph_file = File::create(&flamegraph_path)
|
|
.expect("File system error while creating flamegraph.svg");
|
|
let mut options = pprof::flamegraph::Options::default();
|
|
options.hash = true;
|
|
options.image_width = Some(2500);
|
|
options.text_truncate_direction = TextTruncateDirection::Left;
|
|
options.font_size /= 3;
|
|
if let Some(profiler) = self.active_profiler.take() {
|
|
let report = profiler.report().build().unwrap();
|
|
|
|
let mut file = File::create(pprof_path).unwrap();
|
|
let profile = report.pprof().unwrap();
|
|
let mut content = Vec::new();
|
|
profile.encode(&mut content).unwrap();
|
|
file.write_all(&content).unwrap();
|
|
|
|
report
|
|
.flamegraph_with_options(flamegraph_file, &mut options)
|
|
.expect("Error writing flamegraph");
|
|
}
|
|
}
|
|
}
|