device-driver-core 2.2.0

Internal compiler crate for the device-driver toolkit
Documentation
use std::time::Duration;

use annotate_snippets::{Group, Level};
use device_driver_common::{instant::Instant, span::Span};
use device_driver_diagnostics::{Diagnostic, Severity};
use device_driver_mir::PassTiming;

use crate::TimingsMode;

#[derive(Debug)]
pub struct Timings {
    mode: TimingsMode,
    source_bytes: usize,
    lexer: Duration,
    tokens: usize,
    parser: Duration,
    mir: Duration,
    mir_passes: Vec<PassTiming>,
    lir: Duration,
    codegen: Duration,
    formatting: Duration,
}

impl Timings {
    pub const fn new(mode: TimingsMode) -> Self {
        Self {
            mode,
            source_bytes: 0,
            lexer: Duration::ZERO,
            tokens: 0,
            parser: Duration::ZERO,
            mir: Duration::ZERO,
            mir_passes: Vec::new(),
            lir: Duration::ZERO,
            codegen: Duration::ZERO,
            formatting: Duration::ZERO,
        }
    }

    pub fn start_lexer(&mut self, source_bytes: usize) -> Timer<'_> {
        self.source_bytes = source_bytes;
        Timer::new(&mut self.lexer)
    }
    pub fn start_parser(&mut self, tokens: usize) -> Timer<'_> {
        self.tokens = tokens;
        Timer::new(&mut self.parser)
    }
    pub fn start_mir(&mut self) -> Timer<'_> {
        Timer::new(&mut self.mir)
    }
    pub fn start_lir(&mut self) -> Timer<'_> {
        Timer::new(&mut self.lir)
    }
    pub fn start_codegen(&mut self) -> Timer<'_> {
        Timer::new(&mut self.codegen)
    }
    pub fn start_formatting(&mut self) -> Timer<'_> {
        Timer::new(&mut self.formatting)
    }
    fn total(&self) -> Duration {
        self.lexer + self.parser + self.mir + self.lir + self.codegen + self.formatting
    }

    pub fn set_mir_timings(&mut self, mir_timings: Vec<PassTiming>) {
        if matches!(self.mode, TimingsMode::Verbose) {
            self.mir_passes = mir_timings;
        }
    }
}

impl Diagnostic for Timings {
    fn severity(&self) -> Severity {
        Severity::Info
    }

    fn as_report<'a>(&'a self, _source: &'a str, _path: &'a str) -> Vec<Group<'a>> {
        let total = self.total();
        [self
            .title_snippet()
            .element(Level::INFO.with_name(Some("lexer")).message(format!(
                "  {:>8.3}ms ({:4.1}%) {:7.1} MB/s   ({:.1} KB)",
                self.lexer.as_secs_f64() * 1000.0,
                self.lexer.as_secs_f64() / total.as_secs_f64() * 100.0,
                self.source_bytes as f64 / self.lexer.as_secs_f64() / 1024.0 / 1024.0,
                self.source_bytes as f64 / 1024.0,
            )))
            .element(Level::INFO.with_name(Some("parser")).message(format!(
                " {:>8.3}ms ({:4.1}%) {:7.1} ktok/s ({} tokens)",
                self.parser.as_secs_f64() * 1000.0,
                self.parser.as_secs_f64() / total.as_secs_f64() * 100.0,
                self.tokens as f64 / self.parser.as_secs_f64() / 1000.0,
                self.tokens
            )))
            .element(Level::INFO.with_name(Some("mir")).message(format!(
                "    {:>8.3}ms ({:4.1}%)",
                self.mir.as_secs_f64() * 1000.0,
                self.mir.as_secs_f64() / total.as_secs_f64() * 100.0,
            )))
            .elements(self.mir_passes.iter().map(|pass| {
                Level::INFO.no_name().message(format!(
                    "- {}: {:0.3}ms ({:4.1}%)",
                    pass.name.split("::").last().unwrap(),
                    pass.duration.as_secs_f64() * 1000.0,
                    pass.duration.as_secs_f64() / total.as_secs_f64() * 100.0,
                ))
            }))
            .element(Level::INFO.with_name(Some("lir")).message(format!(
                "    {:>8.3}ms ({:4.1}%)",
                self.lir.as_secs_f64() * 1000.0,
                self.lir.as_secs_f64() / total.as_secs_f64() * 100.0,
            )))
            .element(Level::INFO.with_name(Some("codegen")).message(format!(
                "{:>8.3}ms ({:4.1}%)",
                self.codegen.as_secs_f64() * 1000.0,
                self.codegen.as_secs_f64() / total.as_secs_f64() * 100.0,
            )))
            .element(Level::INFO.with_name(Some("format")).message(format!(
                " {:>8.3}ms ({:4.1}%)",
                self.formatting.as_secs_f64() * 1000.0,
                self.formatting.as_secs_f64() / total.as_secs_f64() * 100.0,
            )))
            .element(
                Level::INFO
                    .with_name(Some("total"))
                    .message(format!("  {:>8.3}ms", (total.as_secs_f64()) * 1000.0)),
            )]
        .to_vec()
    }

    fn primary_span(&self) -> Span {
        Span::empty()
    }

    fn title(&self) -> std::borrow::Cow<'static, str> {
        "timings".into()
    }
}

pub struct Timer<'a> {
    start: Instant,
    result: &'a mut Duration,
}

impl<'a> Timer<'a> {
    pub fn new(result: &'a mut Duration) -> Self {
        Self {
            start: Instant::now(),
            result,
        }
    }
}

impl Drop for Timer<'_> {
    fn drop(&mut self) {
        *self.result = self.start.elapsed();
    }
}