pub mod activity;
pub mod callback;
pub mod error;
pub mod metrics;
mod sys;
pub use activity::{ActivityKind, ActivityRecord, KernelRecord, MemcpyRecord};
pub use callback::{CallbackDomain, CallbackId};
pub use error::{CuptiError, CuptiResult};
pub use metrics::{MetricId, MetricValue, SmMetrics, WarpMetrics};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
pub struct Profiler {
active: Arc<AtomicBool>,
enabled_activities: Vec<ActivityKind>,
records: Vec<ActivityRecord>,
available: bool,
}
impl Profiler {
pub fn new() -> CuptiResult<Self> {
let available = Self::check_cupti_available();
Ok(Self {
active: Arc::new(AtomicBool::new(false)),
enabled_activities: Vec::new(),
records: Vec::new(),
available,
})
}
pub fn is_available(&self) -> bool {
self.available
}
pub fn enable(&mut self, kind: ActivityKind) -> CuptiResult<()> {
if !self.available {
return Err(CuptiError::NotAvailable);
}
if !self.enabled_activities.contains(&kind) {
self.enabled_activities.push(kind);
}
Ok(())
}
pub fn disable(&mut self, kind: ActivityKind) -> CuptiResult<()> {
self.enabled_activities.retain(|k| *k != kind);
Ok(())
}
pub fn start(&self) -> CuptiResult<()> {
if !self.available {
return Err(CuptiError::NotAvailable);
}
self.active.store(true, Ordering::SeqCst);
Ok(())
}
pub fn stop(&self) -> CuptiResult<()> {
self.active.store(false, Ordering::SeqCst);
Ok(())
}
pub fn is_active(&self) -> bool {
self.active.load(Ordering::SeqCst)
}
pub fn flush(&mut self) -> CuptiResult<Vec<ActivityRecord>> {
Ok(std::mem::take(&mut self.records))
}
pub fn enabled_activities(&self) -> &[ActivityKind] {
&self.enabled_activities
}
fn check_cupti_available() -> bool {
#[cfg(target_os = "linux")]
{
let paths = [
"/usr/local/cuda/lib64/libcupti.so",
"/usr/lib/x86_64-linux-gnu/libcupti.so",
"/opt/cuda/lib64/libcupti.so",
];
for path in &paths {
if std::path::Path::new(path).exists() {
return true;
}
}
if let Ok(ld_path) = std::env::var("LD_LIBRARY_PATH") {
for dir in ld_path.split(':') {
let cupti_path = std::path::Path::new(dir).join("libcupti.so");
if cupti_path.exists() {
return true;
}
}
}
}
false
}
}
impl Default for Profiler {
fn default() -> Self {
Self::new().unwrap_or(Self {
active: Arc::new(AtomicBool::new(false)),
enabled_activities: Vec::new(),
records: Vec::new(),
available: false,
})
}
}
#[derive(Debug, Default)]
pub struct ProfilerBuilder {
activities: Vec<ActivityKind>,
buffer_size: Option<usize>,
flush_period_ms: Option<u64>,
}
impl ProfilerBuilder {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn activity(mut self, kind: ActivityKind) -> Self {
self.activities.push(kind);
self
}
#[must_use]
pub fn kernels(self) -> Self {
self.activity(ActivityKind::Kernel)
}
#[must_use]
pub fn memcpy(self) -> Self {
self.activity(ActivityKind::MemoryCopy)
}
#[must_use]
pub fn sync(self) -> Self {
self.activity(ActivityKind::Synchronization)
}
#[must_use]
pub fn buffer_size(mut self, size: usize) -> Self {
self.buffer_size = Some(size);
self
}
#[must_use]
pub fn flush_period_ms(mut self, ms: u64) -> Self {
self.flush_period_ms = Some(ms);
self
}
pub fn build(self) -> CuptiResult<Profiler> {
let mut profiler = Profiler::new()?;
for activity in self.activities {
profiler.enable(activity)?;
}
Ok(profiler)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_profiler_creation() {
let profiler = Profiler::new();
assert!(profiler.is_ok());
}
#[test]
fn test_profiler_default() {
let profiler = Profiler::default();
assert!(!profiler.is_active());
}
#[test]
fn test_profiler_builder() {
let builder = ProfilerBuilder::new()
.kernels()
.memcpy()
.buffer_size(1024 * 1024);
let profiler = builder.build();
if let Ok(p) = profiler {
assert!(p.enabled_activities().contains(&ActivityKind::Kernel));
assert!(p.enabled_activities().contains(&ActivityKind::MemoryCopy));
}
}
#[test]
fn test_activity_enable_disable() {
let mut profiler = Profiler::default();
if profiler.is_available() {
assert!(profiler.enable(ActivityKind::Kernel).is_ok());
assert!(profiler
.enabled_activities()
.contains(&ActivityKind::Kernel));
assert!(profiler.disable(ActivityKind::Kernel).is_ok());
assert!(!profiler
.enabled_activities()
.contains(&ActivityKind::Kernel));
}
}
#[test]
fn test_profiler_start_stop() {
let profiler = Profiler::default();
if profiler.is_available() {
assert!(profiler.start().is_ok());
assert!(profiler.is_active());
assert!(profiler.stop().is_ok());
assert!(!profiler.is_active());
}
}
}