use std::fmt;
#[cfg(feature = "perf_test")]
use opentelemetry::{
global,
trace::{get_active_span, Tracer},
KeyValue,
};
use tracing::info;
use diskann_providers::utils::Timer;
pub const LATENCY_LOG_TARGET: &str = "latency_event";
mod scenario {
pub const DISK_INDEX_BUILD_SCENARIO: &str = "DiskIndexBuild";
}
#[derive(Debug)]
pub enum DiskIndexBuildCheckpoint {
PqConstruction,
InmemIndexBuild,
DiskLayout,
}
impl fmt::Display for DiskIndexBuildCheckpoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug)]
pub enum BuildMergedVamanaIndexCheckpoint {
PartitionData,
BuildIndicesOnShards,
MergeIndices,
}
impl fmt::Display for BuildMergedVamanaIndexCheckpoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
pub struct PerfLogger {
inner: Option<PerfLoggerInner>,
}
impl PerfLogger {
pub fn new<T: std::fmt::Display>(scenario: T, enable_logging: bool) -> Self {
if enable_logging {
let inner = PerfLoggerInner {
scenario: scenario.to_string(),
timer: Timer::new(),
};
Self { inner: Some(inner) }
} else {
Self { inner: None }
}
}
pub fn new_disk_index_build_logger() -> Self {
Self::new(scenario::DISK_INDEX_BUILD_SCENARIO, true)
}
pub fn start(&mut self) {
if let Some(inner) = &mut self.inner {
inner.start();
}
}
pub fn log_checkpoint<T: fmt::Display>(&mut self, checkpoint: T) {
if let Some(inner) = &mut self.inner {
inner.log_checkpoint(checkpoint);
}
}
pub fn log_enabled(&self) -> bool {
self.inner.is_some()
}
}
struct PerfLoggerInner {
scenario: String,
timer: Timer,
}
impl PerfLoggerInner {
fn start(&mut self) {
self.timer.reset();
}
fn log_checkpoint<T: fmt::Display>(&mut self, checkpoint: T) {
info!( target: LATENCY_LOG_TARGET,
"Time for {} [Checkpoint: {}] completed: {:.3} seconds, {:.3}B cycles, {:.3}% CPU time, peak memory {:.3} GBs",
self.scenario,
checkpoint,
self.timer.elapsed().as_secs_f32(),
self.timer.elapsed_gcycles(),
self.timer.get_average_cpu_time_in_percents(),
self.timer.get_peak_memory_usage()
);
#[cfg(feature = "perf_test")]
{
let tracer = global::tracer("");
tracer.in_span(format!("{}-{}", self.scenario, checkpoint), |_context| {
get_active_span(|span| {
span.set_attribute(KeyValue::new(
"duration_seconds",
self.timer.elapsed().as_secs_f64(),
));
span.set_attribute(KeyValue::new(
"elapsed_cycles",
self.timer.elapsed_gcycles() as f64,
));
span.set_attribute(KeyValue::new(
"cpu_time",
self.timer.get_average_cpu_time_in_percents(),
));
span.set_attribute(KeyValue::new(
"peak_memory_usage",
self.timer.get_peak_memory_usage() as f64,
));
});
});
}
self.timer.reset();
}
}
#[cfg(test)]
mod perf_logger_tests {
use super::*;
#[test]
fn test_log() {
let scenario = "test";
let mut logger = PerfLogger::new(scenario, true);
assert!(logger.log_enabled());
logger.log_checkpoint(DiskIndexBuildCheckpoint::PqConstruction);
logger.start();
logger.log_checkpoint(DiskIndexBuildCheckpoint::InmemIndexBuild);
}
#[test]
fn test_log_disabled() {
let scenario = "test";
let mut logger = PerfLogger::new(scenario, false);
assert!(!logger.log_enabled());
logger.log_checkpoint(DiskIndexBuildCheckpoint::PqConstruction);
logger.start();
logger.log_checkpoint(DiskIndexBuildCheckpoint::InmemIndexBuild);
}
}