Skip to main content

llama_cpp_4/context/
perf.rs

1//! Safe wrapper around `llama_perf_context_data`.
2use std::fmt::{Debug, Display, Formatter};
3
4use llama_cpp_sys_4::{ggml_time_us, llama_perf_context_print};
5
6use crate::context::LlamaContext;
7
8/// A wrapper around `llama_perf_context_data`.
9#[derive(Clone, Copy, Debug)]
10pub struct PerfContextData {
11    pub(crate) perf_context_data: llama_cpp_sys_4::llama_perf_context_data,
12}
13
14impl PerfContextData {
15    /// Create a new `PerfContextData`.
16    /// ```
17    /// # use llama_cpp_4::context::perf::PerfContextData;
18    /// let timings = PerfContextData::new(1.0, 2.0, 3.0, 4.0, 5, 6);
19    /// assert_eq!(timings.t_load_ms(), 2.0);
20    /// assert_eq!(timings.t_p_eval_ms(), 3.0);
21    /// assert_eq!(timings.t_eval_ms(), 4.0);
22    /// assert_eq!(timings.n_p_eval(), 5);
23    /// assert_eq!(timings.n_eval(), 6);
24    /// ```
25    #[allow(clippy::too_many_arguments)]
26    #[must_use]
27    pub fn new(
28        t_start_ms: f64,
29        // t_end_ms: f64,
30        t_load_ms: f64,
31        // t_sample_ms: f64,
32        t_p_eval_ms: f64,
33        t_eval_ms: f64,
34        // n_sample: i32,
35        n_p_eval: i32,
36        n_eval: i32,
37    ) -> Self {
38        Self {
39            perf_context_data: llama_cpp_sys_4::llama_perf_context_data {
40                t_start_ms,
41                // t_end_ms,
42                t_load_ms,
43                // t_sample_ms,
44                t_p_eval_ms,
45                t_eval_ms,
46                // n_sample,
47                n_p_eval,
48                n_eval,
49                n_reused: 0,
50                // Upstream counters, added after this constructor's signature
51                // was fixed (llama.cpp b11829). They are only meaningful on a
52                // value read back from a context, so a hand-built one reports
53                // zero; see the accessors below.
54                n_reallocs_same: 0,
55                n_reallocs_diff: 0,
56            },
57        }
58    }
59
60    /// print llama context performance data
61    /// load time
62    /// prompt eval time
63    /// eval time
64    /// total time
65    pub fn print(ctx: &LlamaContext<'_>) {
66        // SAFETY: a live, non-null context; this only reads the counters and
67        // writes to llama.cpp's log.
68        unsafe {
69            llama_perf_context_print(ctx.context.as_ptr());
70        };
71    }
72
73    /// Get the start time in milliseconds.
74    #[must_use]
75    pub fn t_start_ms(&self) -> f64 {
76        self.perf_context_data.t_start_ms
77    }
78
79    /// Get the end time in milliseconds.
80    #[must_use]
81    pub fn t_end_ms(&self) -> f64 {
82        // self.perf_context_data.t_end_ms
83
84        #[allow(clippy::cast_precision_loss)]
85        {
86            // SAFETY: no pointer is involved; this reads ggml's monotonic clock.
87            1e-3 * (unsafe { ggml_time_us() }) as f64
88        }
89    }
90
91    /// Get the load time in milliseconds.
92    #[must_use]
93    pub fn t_load_ms(&self) -> f64 {
94        self.perf_context_data.t_load_ms
95    }
96
97    // /// Get the sample time in milliseconds.
98    // #[must_use]
99    // pub fn t_sample_ms(&self) -> f64 {
100    //     self.perf_context_data.t_sample_ms
101    // }
102
103    /// Get the prompt evaluation time in milliseconds.
104    #[must_use]
105    pub fn t_p_eval_ms(&self) -> f64 {
106        self.perf_context_data.t_p_eval_ms
107    }
108
109    /// Get the evaluation time in milliseconds.
110    #[must_use]
111    pub fn t_eval_ms(&self) -> f64 {
112        self.perf_context_data.t_eval_ms
113    }
114
115    // /// Get the number of samples.
116    // #[must_use]
117    // pub fn n_sample(&self) -> i32 {
118    //     self.perf_context_data.n_sample
119    // }
120
121    /// Get the number of prompt evaluations.
122    #[must_use]
123    pub fn n_p_eval(&self) -> i32 {
124        self.perf_context_data.n_p_eval
125    }
126
127    /// Get the number of evaluations.
128    #[must_use]
129    pub fn n_eval(&self) -> i32 {
130        self.perf_context_data.n_eval
131    }
132
133    /// How many times llama.cpp reused a cached compute graph instead of
134    /// rebuilding it.
135    ///
136    /// Graph reuse is the fast path. A low count against a high
137    /// [`Self::n_eval`] means something is invalidating the graph every step —
138    /// a changing batch shape, or toggling a context flag mid-generation.
139    #[must_use]
140    pub fn n_reused(&self) -> i32 {
141        self.perf_context_data.n_reused
142    }
143
144    /// Graph reallocations where the graph size was unchanged.
145    ///
146    /// These should be rare: the shape did not change, so the buffers should
147    /// have been reusable. A climbing count points at allocator churn rather
148    /// than at your batching.
149    #[must_use]
150    pub fn n_reallocs_same(&self) -> i32 {
151        self.perf_context_data.n_reallocs_same
152    }
153
154    /// Graph reallocations where the graph size changed.
155    ///
156    /// Expected whenever the batch shape moves — a prefill of a different
157    /// length, or switching between prompt and single-token decode.
158    #[must_use]
159    pub fn n_reallocs_diff(&self) -> i32 {
160        self.perf_context_data.n_reallocs_diff
161    }
162
163    /// Set the start time in milliseconds.
164    pub fn set_t_start_ms(&mut self, t_start_ms: f64) {
165        self.perf_context_data.t_start_ms = t_start_ms;
166    }
167
168    // /// Set the end time in milliseconds.
169    // pub fn set_t_end_ms(&mut self, t_end_ms: f64) {
170    //     self.perf_context_data.t_end_ms = t_end_ms;
171    // }
172
173    /// Set the load time in milliseconds.
174    pub fn set_t_load_ms(&mut self, t_load_ms: f64) {
175        self.perf_context_data.t_load_ms = t_load_ms;
176    }
177
178    // /// Set the sample time in milliseconds.
179    // pub fn set_t_sample_ms(&mut self, t_sample_ms: f64) {
180    //     self.perf_context_data.t_sample_ms = t_sample_ms;
181    // }
182
183    /// Set the prompt evaluation time in milliseconds.
184    pub fn set_t_p_eval_ms(&mut self, t_p_eval_ms: f64) {
185        self.perf_context_data.t_p_eval_ms = t_p_eval_ms;
186    }
187
188    /// Set the evaluation time in milliseconds.
189    pub fn set_t_eval_ms(&mut self, t_eval_ms: f64) {
190        self.perf_context_data.t_eval_ms = t_eval_ms;
191    }
192
193    // /// Set the number of samples.
194    // pub fn set_n_sample(&mut self, n_sample: i32) {
195    //     self.perf_context_data.n_sample = n_sample;
196    // }
197
198    /// Set the number of prompt evaluations.
199    pub fn set_n_p_eval(&mut self, n_p_eval: i32) {
200        self.perf_context_data.n_p_eval = n_p_eval;
201    }
202
203    /// Set the number of evaluations.
204    pub fn set_n_eval(&mut self, n_eval: i32) {
205        self.perf_context_data.n_eval = n_eval;
206    }
207}
208
209impl Display for PerfContextData {
210    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
211        writeln!(f, "load time = {:.2} ms", self.t_load_ms())?;
212        // writeln!(
213        //     f,
214        //     "sample time = {:.2} ms / {} runs ({:.2} ms per token, {:.2} tokens per second)",
215        //     self.t_sample_ms(),
216        //     self.n_sample(),
217        //     self.t_sample_ms() / f64::from(self.n_sample()),
218        //     1e3 / self.t_sample_ms() * f64::from(self.n_sample())
219        // )?;
220        writeln!(
221            f,
222            "prompt eval time = {:.2} ms / {} tokens ({:.2} ms per token, {:.2} tokens per second)",
223            self.t_p_eval_ms(),
224            self.n_p_eval(),
225            self.t_p_eval_ms() / f64::from(self.n_p_eval()),
226            1e3 / self.t_p_eval_ms() * f64::from(self.n_p_eval())
227        )?;
228        writeln!(
229            f,
230            "eval time = {:.2} ms / {} runs ({:.2} ms per token, {:.2} tokens per second)",
231            self.t_eval_ms(),
232            self.n_eval(),
233            self.t_eval_ms() / f64::from(self.n_eval()),
234            1e3 / self.t_eval_ms() * f64::from(self.n_eval())
235        )?;
236        write!(
237            f,
238            "total time = {:.2} ms",
239            self.t_end_ms() - self.t_start_ms()
240        )
241    }
242}