rival/eval/profile.rs
1//! Per-instruction profiling for the rival machine.
2//!
3//! Rival's evaluator exposes profiling data via [`Execution`] records,
4//! which can be accessed via [`Machine::execution_records`](crate::Machine::execution_records)
5//! or [`Machine::take_executions`](crate::Machine::take_executions).
6
7/// A single recorded execution of a Rival interval operator.
8///
9/// Each execution corresponds to a single interval operator being executed.
10/// The [`name`](Execution::name) names the operator, except the special
11/// name `"adjust"`, in which case it refers to an internal precision-tuning
12/// pass.
13/// The [`number`](Execution::number) gives its position in the compiled
14/// instruction sequence; this allows disambiguating if an expression contains,
15/// say, multiple addition operations.
16/// The [`precision`](Execution::precision) is the working precision in bits
17/// that the operator was executed at,
18/// the [`time_ms`](Execution::time_ms) is the time, in milliseconds, that
19/// the execution took,
20/// and the [`iteration`](Execution::iteration) records which sampling
21/// iteration triggered the execution.
22///
23/// Note that, because Rival executes the register machine multiple times,
24/// the same operator (with the same name and number) can appear multiple
25/// times for a single point. On the other hand, in some iterations Rival
26/// might skip some operators, if the precision is unchanged from previous
27/// iterations, so not every operator may show up in the executions list
28/// the same number of times.
29#[derive(Clone, Copy, Debug)]
30pub struct Execution {
31 /// The name of the operator, or `"adjust"` for precision-tuning passes.
32 pub name: &'static str,
33 /// Position in the compiled instruction sequence (-1 for adjust passes).
34 pub number: i32,
35 /// Working precision in bits for this execution.
36 pub precision: u32,
37 /// Wall-clock time in milliseconds.
38 pub time_ms: f64,
39 /// The sampling iteration that triggered this execution.
40 pub iteration: usize,
41}
42
43impl Default for Execution {
44 fn default() -> Self {
45 Execution {
46 name: "",
47 number: 0,
48 precision: 0,
49 time_ms: 0.0,
50 iteration: 0,
51 }
52 }
53}
54
55#[derive(Debug)]
56pub(crate) struct Profiler {
57 records: Vec<Execution>,
58 ptr: usize,
59}
60
61impl Profiler {
62 /// Create a profiler with a fixed capacity (number of records)
63 pub(crate) fn with_capacity(capacity: usize) -> Self {
64 Self {
65 records: vec![Execution::default(); capacity],
66 ptr: 0,
67 }
68 }
69
70 /// Reset the profiler write pointer without clearing memory
71 #[inline]
72 pub(crate) fn reset(&mut self) {
73 self.ptr = 0;
74 }
75
76 /// Slice of current execution records
77 #[inline]
78 pub(crate) fn records(&self) -> &[Execution] {
79 &self.records[..self.ptr]
80 }
81
82 /// Record an execution if capacity allows
83 #[inline]
84 pub(crate) fn record(&mut self, exec: Execution) {
85 if self.ptr < self.records.len() {
86 self.records[self.ptr] = exec;
87 self.ptr += 1;
88 }
89 }
90}