llama_cpp_4/ggml.rs
1//! Safe wrappers around core ggml graph computation APIs.
2//!
3//! This module provides the building blocks for creating and executing tensor computation
4//! graphs using ggml backends. It's used for operations like `LoRA` merging, importance
5//! matrix computation, and control vector generation.
6//!
7//! # Example
8//!
9//! ```rust,ignore
10//! use llama_cpp_4::ggml::*;
11//!
12//! // Create a backend and context
13//! let backend = GgmlBackend::cpu()?;
14//! let mut ctx = GgmlContext::new(1024 * 1024, true)?;
15//!
16//! // Create tensors
17//! let a = ctx.new_tensor_2d(GgmlType::F32, 4, 4);
18//! let b = ctx.new_tensor_2d(GgmlType::F32, 4, 4);
19//!
20//! // Build computation graph
21//! let sum = ctx.add(&a, &b);
22//! let mut graph = ctx.new_graph();
23//! graph.build_forward(&sum);
24//!
25//! // Allocate and compute
26//! let mut alloc = GgmlAllocr::new(&backend);
27//! alloc.alloc_graph(&mut graph);
28//! // ... set tensor data ...
29//! backend.graph_compute(&mut graph);
30//! // ... get results ...
31//! ```
32
33use std::ffi::CStr;
34use std::ptr::NonNull;
35
36/// Re-export the raw ggml types for advanced usage.
37pub use llama_cpp_sys_4::ggml_type;
38
39/// A safe wrapper around `ggml_context`.
40#[derive(Debug)]
41pub struct GgmlContext {
42 ctx: NonNull<llama_cpp_sys_4::ggml_context>,
43}
44
45impl GgmlContext {
46 /// Create a new ggml context.
47 ///
48 /// # Parameters
49 /// - `mem_size`: Memory pool size in bytes for tensor metadata.
50 /// - `no_alloc`: If true, tensor data is not allocated (use with backend allocation).
51 ///
52 /// # Sizing the pool
53 ///
54 /// Every tensor and graph created from this context is carved out of
55 /// `mem_size`, and **running out is not a recoverable error**:
56 ///
57 /// - a *release* build of ggml returns null, which the constructors here
58 /// turn into a panic;
59 /// - a *debug* build of ggml calls `GGML_ABORT` and kills the process
60 /// before returning, so no amount of Rust-side care helps.
61 ///
62 /// So size it up front rather than guessing. [`Self::sized_for`] does the
63 /// arithmetic; [`Self::used_mem`] and [`Self::mem_size`] let you check
64 /// headroom while building a graph.
65 ///
66 /// # Panics
67 ///
68 /// Panics if ggml returns a null pointer.
69 #[must_use]
70 pub fn new(mem_size: usize, no_alloc: bool) -> Self {
71 let params = llama_cpp_sys_4::ggml_init_params {
72 mem_size,
73 mem_buffer: std::ptr::null_mut(),
74 no_alloc,
75 };
76 let ctx = unsafe { llama_cpp_sys_4::ggml_init(params) };
77 Self {
78 ctx: NonNull::new(ctx).expect("ggml_init returned null"),
79 }
80 }
81
82 /// Create a context sized for `n_tensors` tensors and `n_graphs` graphs.
83 ///
84 /// Turns "pick a number and hope" into arithmetic: the pool holds metadata,
85 /// whose per-item cost ggml reports through [`tensor_overhead`] and
86 /// [`graph_overhead`]. A little slack is added for ggml's own bookkeeping.
87 ///
88 /// Views, reshapes and every intermediate an operation produces are tensors
89 /// too — count graph *nodes*, not just the tensors you name.
90 ///
91 /// ```
92 /// # use llama_cpp_4::ggml::GgmlContext;
93 /// // Room for 64 tensors and one graph.
94 /// let ctx = GgmlContext::sized_for(64, 1, true);
95 /// assert!(ctx.mem_size() >= 64 * llama_cpp_4::ggml::tensor_overhead());
96 /// ```
97 ///
98 /// # Panics
99 ///
100 /// Panics if ggml returns a null pointer.
101 #[must_use]
102 pub fn sized_for(n_tensors: usize, n_graphs: usize, no_alloc: bool) -> Self {
103 // The +1 tensor and the 1 KiB tail cover ggml's per-object headers and
104 // alignment padding, which are not part of the published overheads.
105 let mem_size = (n_tensors + 1) * tensor_overhead() + n_graphs * graph_overhead() + 1024;
106 Self::new(mem_size, no_alloc)
107 }
108
109 /// Bytes of the pool used so far.
110 ///
111 /// Compare with [`Self::mem_size`] while building a graph to see whether it
112 /// will fit, rather than finding out by aborting.
113 #[must_use]
114 pub fn used_mem(&self) -> usize {
115 unsafe { llama_cpp_sys_4::ggml_used_mem(self.ctx.as_ptr()) }
116 }
117
118 /// Total size of the pool, as passed to [`Self::new`].
119 #[must_use]
120 pub fn mem_size(&self) -> usize {
121 unsafe { llama_cpp_sys_4::ggml_get_mem_size(self.ctx.as_ptr()) }
122 }
123
124 /// Bytes still available in the pool.
125 #[must_use]
126 pub fn free_mem(&self) -> usize {
127 self.mem_size().saturating_sub(self.used_mem())
128 }
129
130 /// Get the raw context pointer.
131 #[must_use]
132 pub fn as_ptr(&self) -> *mut llama_cpp_sys_4::ggml_context {
133 self.ctx.as_ptr()
134 }
135
136 // ── Tensor creation ──────────────────────────────────────
137
138 /// Create a 1D tensor.
139 ///
140 /// # Panics
141 ///
142 /// Panics if ggml returns null, which in a release build means the
143 /// context's memory pool is exhausted. A debug build of ggml aborts
144 /// before returning — see [`GgmlContext::new`] for how to size the pool.
145 #[must_use]
146 pub fn new_tensor_1d(&self, typ: ggml_type, ne0: i64) -> GgmlTensor {
147 let t = unsafe { llama_cpp_sys_4::ggml_new_tensor_1d(self.ctx.as_ptr(), typ, ne0) };
148 GgmlTensor(NonNull::new(t).expect("ggml_new_tensor_1d returned null"))
149 }
150
151 /// Create a 2D tensor.
152 ///
153 /// # Panics
154 ///
155 /// Panics if ggml returns null, which in a release build means the
156 /// context's memory pool is exhausted. A debug build of ggml aborts
157 /// before returning — see [`GgmlContext::new`] for how to size the pool.
158 #[must_use]
159 pub fn new_tensor_2d(&self, typ: ggml_type, ne0: i64, ne1: i64) -> GgmlTensor {
160 let t = unsafe { llama_cpp_sys_4::ggml_new_tensor_2d(self.ctx.as_ptr(), typ, ne0, ne1) };
161 GgmlTensor(NonNull::new(t).expect("ggml_new_tensor_2d returned null"))
162 }
163
164 /// Create a 3D tensor.
165 ///
166 /// # Panics
167 ///
168 /// Panics if ggml returns null, which in a release build means the
169 /// context's memory pool is exhausted. A debug build of ggml aborts
170 /// before returning — see [`GgmlContext::new`] for how to size the pool.
171 #[must_use]
172 pub fn new_tensor_3d(&self, typ: ggml_type, ne0: i64, ne1: i64, ne2: i64) -> GgmlTensor {
173 let t =
174 unsafe { llama_cpp_sys_4::ggml_new_tensor_3d(self.ctx.as_ptr(), typ, ne0, ne1, ne2) };
175 GgmlTensor(NonNull::new(t).expect("ggml_new_tensor_3d returned null"))
176 }
177
178 /// Create a 4D tensor.
179 ///
180 /// # Panics
181 ///
182 /// Panics if ggml returns null, which in a release build means the
183 /// context's memory pool is exhausted. A debug build of ggml aborts
184 /// before returning — see [`GgmlContext::new`] for how to size the pool.
185 #[must_use]
186 pub fn new_tensor_4d(
187 &self,
188 typ: ggml_type,
189 ne0: i64,
190 ne1: i64,
191 ne2: i64,
192 ne3: i64,
193 ) -> GgmlTensor {
194 let t = unsafe {
195 llama_cpp_sys_4::ggml_new_tensor_4d(self.ctx.as_ptr(), typ, ne0, ne1, ne2, ne3)
196 };
197 GgmlTensor(NonNull::new(t).expect("ggml_new_tensor_4d returned null"))
198 }
199
200 /// Create a tensor with the same shape and type as another.
201 ///
202 /// # Panics
203 ///
204 /// Panics if ggml returns null, which in a release build means the
205 /// context's memory pool is exhausted. A debug build of ggml aborts
206 /// before returning — see [`GgmlContext::new`] for how to size the pool.
207 #[must_use]
208 pub fn dup_tensor(&self, src: &GgmlTensor) -> GgmlTensor {
209 let t = unsafe { llama_cpp_sys_4::ggml_dup_tensor(self.ctx.as_ptr(), src.0.as_ptr()) };
210 GgmlTensor(NonNull::new(t).expect("ggml_dup_tensor returned null"))
211 }
212
213 /// Create a new tensor with arbitrary dimensions.
214 ///
215 /// # Panics
216 ///
217 /// Panics if ggml returns null, which in a release build means the
218 /// context's memory pool is exhausted. A debug build of ggml aborts
219 /// before returning — see [`GgmlContext::new`] for how to size the pool.
220 #[must_use]
221 pub fn new_tensor(&self, typ: ggml_type, ne: &[i64]) -> GgmlTensor {
222 let t = unsafe {
223 llama_cpp_sys_4::ggml_new_tensor(
224 self.ctx.as_ptr(),
225 typ,
226 // ggml caps dimensions at GGML_MAX_DIMS (4); anything longer is
227 // rejected by ggml itself, so this cannot truncate meaningfully.
228 i32::try_from(ne.len()).unwrap_or(i32::MAX),
229 ne.as_ptr(),
230 )
231 };
232 GgmlTensor(NonNull::new(t).expect("ggml_new_tensor returned null"))
233 }
234
235 // ── Tensor operations (build graph nodes) ────────────────
236
237 /// Element-wise addition: `a + b`
238 ///
239 /// # Panics
240 ///
241 /// Panics if ggml returns null, which in a release build means the
242 /// context's memory pool is exhausted. A debug build of ggml aborts
243 /// before returning — see [`GgmlContext::new`] for how to size the pool.
244 #[must_use]
245 pub fn add(&self, a: &GgmlTensor, b: &GgmlTensor) -> GgmlTensor {
246 let t = unsafe { llama_cpp_sys_4::ggml_add(self.ctx.as_ptr(), a.0.as_ptr(), b.0.as_ptr()) };
247 GgmlTensor(NonNull::new(t).expect("ggml_add returned null"))
248 }
249
250 /// Matrix multiplication: `a @ b`
251 ///
252 /// # Panics
253 ///
254 /// Panics if ggml returns null, which in a release build means the
255 /// context's memory pool is exhausted. A debug build of ggml aborts
256 /// before returning — see [`GgmlContext::new`] for how to size the pool.
257 #[must_use]
258 pub fn mul_mat(&self, a: &GgmlTensor, b: &GgmlTensor) -> GgmlTensor {
259 let t =
260 unsafe { llama_cpp_sys_4::ggml_mul_mat(self.ctx.as_ptr(), a.0.as_ptr(), b.0.as_ptr()) };
261 GgmlTensor(NonNull::new(t).expect("ggml_mul_mat returned null"))
262 }
263
264 /// Scale tensor: `a * s`
265 ///
266 /// # Panics
267 ///
268 /// Panics if ggml returns null, which in a release build means the
269 /// context's memory pool is exhausted. A debug build of ggml aborts
270 /// before returning — see [`GgmlContext::new`] for how to size the pool.
271 #[must_use]
272 pub fn scale(&self, a: &GgmlTensor, s: f32) -> GgmlTensor {
273 let t = unsafe { llama_cpp_sys_4::ggml_scale(self.ctx.as_ptr(), a.0.as_ptr(), s) };
274 GgmlTensor(NonNull::new(t).expect("ggml_scale returned null"))
275 }
276
277 /// Cast tensor to a different type.
278 ///
279 /// # Panics
280 ///
281 /// Panics if ggml returns null, which in a release build means the
282 /// context's memory pool is exhausted. A debug build of ggml aborts
283 /// before returning — see [`GgmlContext::new`] for how to size the pool.
284 #[must_use]
285 pub fn cast(&self, a: &GgmlTensor, typ: ggml_type) -> GgmlTensor {
286 let t = unsafe { llama_cpp_sys_4::ggml_cast(self.ctx.as_ptr(), a.0.as_ptr(), typ) };
287 GgmlTensor(NonNull::new(t).expect("ggml_cast returned null"))
288 }
289
290 /// Make tensor contiguous in memory.
291 ///
292 /// # Panics
293 ///
294 /// Panics if ggml returns null, which in a release build means the
295 /// context's memory pool is exhausted. A debug build of ggml aborts
296 /// before returning — see [`GgmlContext::new`] for how to size the pool.
297 #[must_use]
298 pub fn cont(&self, a: &GgmlTensor) -> GgmlTensor {
299 let t = unsafe { llama_cpp_sys_4::ggml_cont(self.ctx.as_ptr(), a.0.as_ptr()) };
300 GgmlTensor(NonNull::new(t).expect("ggml_cont returned null"))
301 }
302
303 /// Transpose a tensor.
304 ///
305 /// # Panics
306 ///
307 /// Panics if ggml returns null, which in a release build means the
308 /// context's memory pool is exhausted. A debug build of ggml aborts
309 /// before returning — see [`GgmlContext::new`] for how to size the pool.
310 #[must_use]
311 pub fn transpose(&self, a: &GgmlTensor) -> GgmlTensor {
312 let t = unsafe { llama_cpp_sys_4::ggml_transpose(self.ctx.as_ptr(), a.0.as_ptr()) };
313 GgmlTensor(NonNull::new(t).expect("ggml_transpose returned null"))
314 }
315
316 /// Reshape to 1D.
317 ///
318 /// # Panics
319 ///
320 /// Panics if ggml returns null, which in a release build means the
321 /// context's memory pool is exhausted. A debug build of ggml aborts
322 /// before returning — see [`GgmlContext::new`] for how to size the pool.
323 #[must_use]
324 pub fn reshape_1d(&self, a: &GgmlTensor, ne0: i64) -> GgmlTensor {
325 let t = unsafe { llama_cpp_sys_4::ggml_reshape_1d(self.ctx.as_ptr(), a.0.as_ptr(), ne0) };
326 GgmlTensor(NonNull::new(t).expect("ggml_reshape_1d returned null"))
327 }
328
329 /// Reshape to 2D.
330 ///
331 /// # Panics
332 ///
333 /// Panics if ggml returns null, which in a release build means the
334 /// context's memory pool is exhausted. A debug build of ggml aborts
335 /// before returning — see [`GgmlContext::new`] for how to size the pool.
336 #[must_use]
337 pub fn reshape_2d(&self, a: &GgmlTensor, ne0: i64, ne1: i64) -> GgmlTensor {
338 let t =
339 unsafe { llama_cpp_sys_4::ggml_reshape_2d(self.ctx.as_ptr(), a.0.as_ptr(), ne0, ne1) };
340 GgmlTensor(NonNull::new(t).expect("ggml_reshape_2d returned null"))
341 }
342
343 /// Create a 1D view of a tensor.
344 ///
345 /// # Panics
346 ///
347 /// Panics if ggml returns null, which in a release build means the
348 /// context's memory pool is exhausted. A debug build of ggml aborts
349 /// before returning — see [`GgmlContext::new`] for how to size the pool.
350 #[must_use]
351 pub fn view_1d(&self, a: &GgmlTensor, ne0: i64, offset: usize) -> GgmlTensor {
352 let t =
353 unsafe { llama_cpp_sys_4::ggml_view_1d(self.ctx.as_ptr(), a.0.as_ptr(), ne0, offset) };
354 GgmlTensor(NonNull::new(t).expect("ggml_view_1d returned null"))
355 }
356
357 // ── Graph creation ───────────────────────────────────────
358
359 /// Create a new computation graph.
360 ///
361 /// # Panics
362 ///
363 /// Panics if ggml returns null, which in a release build means the
364 /// context's memory pool is exhausted. A debug build of ggml aborts
365 /// before returning — see [`GgmlContext::new`] for how to size the pool.
366 #[must_use]
367 pub fn new_graph(&self) -> GgmlGraph {
368 let g = unsafe { llama_cpp_sys_4::ggml_new_graph(self.ctx.as_ptr()) };
369 GgmlGraph(NonNull::new(g).expect("ggml_new_graph returned null"))
370 }
371
372 // ── Tensor iteration ─────────────────────────────────────
373
374 /// Get the first tensor in this context.
375 #[must_use]
376 pub fn first_tensor(&self) -> Option<GgmlTensor> {
377 let t = unsafe { llama_cpp_sys_4::ggml_get_first_tensor(self.ctx.as_ptr()) };
378 NonNull::new(t).map(GgmlTensor)
379 }
380
381 /// Get the next tensor after `tensor` in this context.
382 #[must_use]
383 pub fn next_tensor(&self, tensor: &GgmlTensor) -> Option<GgmlTensor> {
384 let t =
385 unsafe { llama_cpp_sys_4::ggml_get_next_tensor(self.ctx.as_ptr(), tensor.0.as_ptr()) };
386 NonNull::new(t).map(GgmlTensor)
387 }
388}
389
390impl Drop for GgmlContext {
391 fn drop(&mut self) {
392 unsafe { llama_cpp_sys_4::ggml_free(self.ctx.as_ptr()) }
393 }
394}
395
396// ── Tensor ──────────────────────────────────────────────────
397
398/// A wrapper around a `ggml_tensor` pointer.
399///
400/// Tensors are owned by their `GgmlContext` and must not outlive it.
401/// This wrapper does NOT free the tensor on drop.
402#[derive(Clone, Copy)]
403pub struct GgmlTensor(pub(crate) NonNull<llama_cpp_sys_4::ggml_tensor>);
404
405impl GgmlTensor {
406 /// Get the raw tensor pointer.
407 #[must_use]
408 pub fn as_ptr(&self) -> *mut llama_cpp_sys_4::ggml_tensor {
409 self.0.as_ptr()
410 }
411
412 /// Set the tensor's name.
413 ///
414 /// # Panics
415 ///
416 /// Panics if ggml returns null, which in a release build means the
417 /// context's memory pool is exhausted. A debug build of ggml aborts
418 /// before returning — see [`GgmlContext::new`] for how to size the pool.
419 pub fn set_name(&self, name: &str) {
420 let c_name = std::ffi::CString::new(name).expect("name contains null bytes");
421 unsafe { llama_cpp_sys_4::ggml_set_name(self.0.as_ptr(), c_name.as_ptr()) };
422 }
423
424 /// Get the number of elements.
425 #[must_use]
426 pub fn nelements(&self) -> i64 {
427 unsafe { llama_cpp_sys_4::ggml_nelements(self.0.as_ptr()) }
428 }
429
430 /// Get the total size in bytes.
431 #[must_use]
432 pub fn nbytes(&self) -> usize {
433 unsafe { llama_cpp_sys_4::ggml_nbytes(self.0.as_ptr()) }
434 }
435
436 /// Get the element size in bytes.
437 #[must_use]
438 pub fn element_size(&self) -> usize {
439 unsafe { llama_cpp_sys_4::ggml_element_size(self.0.as_ptr()) }
440 }
441
442 /// Get the tensor type.
443 #[must_use]
444 pub fn typ(&self) -> ggml_type {
445 unsafe { (*self.0.as_ptr()).type_ }
446 }
447
448 /// Get the tensor dimensions (ne).
449 #[must_use]
450 pub fn ne(&self) -> [i64; 4] {
451 unsafe { (*self.0.as_ptr()).ne }
452 }
453
454 /// Get the tensor name.
455 #[must_use]
456 pub fn name(&self) -> &str {
457 unsafe {
458 let ptr = (*self.0.as_ptr()).name.as_ptr();
459 CStr::from_ptr(ptr).to_str().unwrap_or("")
460 }
461 }
462}
463
464impl std::fmt::Debug for GgmlTensor {
465 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
466 let ne = self.ne();
467 write!(
468 f,
469 "GgmlTensor({:?}, [{}, {}, {}, {}], {} bytes)",
470 self.name(),
471 ne[0],
472 ne[1],
473 ne[2],
474 ne[3],
475 self.nbytes()
476 )
477 }
478}
479
480// ── Graph ───────────────────────────────────────────────────
481
482/// A wrapper around `ggml_cgraph`.
483///
484/// Graphs are owned by their `GgmlContext` and must not outlive it.
485pub struct GgmlGraph(NonNull<llama_cpp_sys_4::ggml_cgraph>);
486
487impl std::fmt::Debug for GgmlGraph {
488 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
489 f.debug_struct("GgmlGraph").finish()
490 }
491}
492
493impl GgmlGraph {
494 /// Get the raw graph pointer.
495 pub fn as_ptr(&mut self) -> *mut llama_cpp_sys_4::ggml_cgraph {
496 self.0.as_ptr()
497 }
498
499 /// Add a tensor and its dependencies to the forward computation graph.
500 pub fn build_forward(&mut self, tensor: &GgmlTensor) {
501 unsafe { llama_cpp_sys_4::ggml_build_forward_expand(self.0.as_ptr(), tensor.0.as_ptr()) }
502 }
503
504 /// Get a node (output tensor) by index. Use -1 for the last node.
505 ///
506 /// # Panics
507 ///
508 /// Panics if ggml returns null, which in a release build means the
509 /// context's memory pool is exhausted. A debug build of ggml aborts
510 /// before returning — see [`GgmlContext::new`] for how to size the pool.
511 #[must_use]
512 pub fn node(&mut self, i: i32) -> GgmlTensor {
513 let t = unsafe { llama_cpp_sys_4::ggml_graph_node(self.0.as_ptr(), i) };
514 GgmlTensor(NonNull::new(t).expect("graph_node returned null"))
515 }
516}
517
518// ── Backend ─────────────────────────────────────────────────
519
520/// A safe wrapper around `ggml_backend`.
521#[derive(Debug)]
522pub struct GgmlBackend {
523 backend: llama_cpp_sys_4::ggml_backend_t,
524}
525
526impl GgmlBackend {
527 /// Create a CPU backend.
528 ///
529 /// # Panics
530 ///
531 /// Panics if ggml cannot initialise the CPU backend, which would mean the
532 /// build has no CPU backend compiled in.
533 #[must_use]
534 pub fn cpu() -> Self {
535 let backend = unsafe { llama_cpp_sys_4::ggml_backend_cpu_init() };
536 assert!(!backend.is_null(), "ggml_backend_cpu_init returned null");
537 Self { backend }
538 }
539
540 /// Set the number of threads for the CPU backend.
541 pub fn set_n_threads(&self, n_threads: i32) {
542 unsafe { llama_cpp_sys_4::ggml_backend_cpu_set_n_threads(self.backend, n_threads) }
543 }
544
545 /// Allocate all tensors in a context on this backend.
546 ///
547 /// Returns the buffer handle which must be kept alive.
548 #[must_use]
549 pub fn alloc_ctx_tensors(
550 &self,
551 ctx: &GgmlContext,
552 ) -> *mut llama_cpp_sys_4::ggml_backend_buffer {
553 unsafe { llama_cpp_sys_4::ggml_backend_alloc_ctx_tensors(ctx.as_ptr(), self.backend) }
554 }
555
556 /// Compute a graph.
557 pub fn graph_compute(&self, graph: &mut GgmlGraph) {
558 unsafe { llama_cpp_sys_4::ggml_backend_graph_compute(self.backend, graph.as_ptr()) };
559 }
560
561 /// Get the default buffer type for this backend.
562 #[must_use]
563 pub fn default_buffer_type(&self) -> llama_cpp_sys_4::ggml_backend_buffer_type_t {
564 unsafe { llama_cpp_sys_4::ggml_backend_get_default_buffer_type(self.backend) }
565 }
566
567 /// Get the raw backend pointer.
568 #[must_use]
569 pub fn as_ptr(&self) -> llama_cpp_sys_4::ggml_backend_t {
570 self.backend
571 }
572}
573
574impl Drop for GgmlBackend {
575 fn drop(&mut self) {
576 unsafe { llama_cpp_sys_4::ggml_backend_free(self.backend) }
577 }
578}
579
580// ── Graph allocator ─────────────────────────────────────────
581
582/// A safe wrapper around `ggml_gallocr`.
583#[derive(Debug)]
584pub struct GgmlAllocr {
585 alloc: llama_cpp_sys_4::ggml_gallocr_t,
586}
587
588impl GgmlAllocr {
589 /// Create a new graph allocator for the given backend.
590 ///
591 /// # Panics
592 ///
593 /// Panics if ggml cannot allocate the graph allocator.
594 #[must_use]
595 pub fn new(backend: &GgmlBackend) -> Self {
596 let alloc = unsafe { llama_cpp_sys_4::ggml_gallocr_new(backend.default_buffer_type()) };
597 assert!(!alloc.is_null(), "ggml_gallocr_new returned null");
598 Self { alloc }
599 }
600
601 /// Allocate all tensors in a graph.
602 pub fn alloc_graph(&self, graph: &mut GgmlGraph) -> bool {
603 unsafe { llama_cpp_sys_4::ggml_gallocr_alloc_graph(self.alloc, graph.as_ptr()) }
604 }
605}
606
607impl Drop for GgmlAllocr {
608 fn drop(&mut self) {
609 unsafe { llama_cpp_sys_4::ggml_gallocr_free(self.alloc) }
610 }
611}
612
613// ── Utility functions ───────────────────────────────────────
614
615/// Set tensor data from a byte slice.
616///
617/// # Safety
618///
619/// The tensor must be allocated and the data must be the correct size.
620pub unsafe fn tensor_set(tensor: &GgmlTensor, data: &[u8]) {
621 llama_cpp_sys_4::ggml_backend_tensor_set(
622 tensor.0.as_ptr(),
623 data.as_ptr().cast(),
624 0,
625 data.len(),
626 );
627}
628
629/// Get tensor data into a byte slice.
630///
631/// # Safety
632///
633/// The tensor must be allocated and the buffer must be large enough.
634pub unsafe fn tensor_get(tensor: &GgmlTensor, data: &mut [u8]) {
635 llama_cpp_sys_4::ggml_backend_tensor_get(
636 tensor.0.as_ptr(),
637 data.as_mut_ptr().cast(),
638 0,
639 data.len(),
640 );
641}
642
643/// Free a backend buffer.
644///
645/// # Safety
646///
647/// The buffer must be valid and not already freed.
648pub unsafe fn buffer_free(buffer: *mut llama_cpp_sys_4::ggml_backend_buffer) {
649 llama_cpp_sys_4::ggml_backend_buffer_free(buffer);
650}
651
652/// Get the overhead in bytes for tensor metadata.
653#[must_use]
654pub fn tensor_overhead() -> usize {
655 unsafe { llama_cpp_sys_4::ggml_tensor_overhead() }
656}
657
658/// Get the overhead in bytes for a computation graph.
659#[must_use]
660pub fn graph_overhead() -> usize {
661 unsafe { llama_cpp_sys_4::ggml_graph_overhead() }
662}
663
664/// Check if a type is quantized.
665#[must_use]
666pub fn is_quantized(typ: ggml_type) -> bool {
667 unsafe { llama_cpp_sys_4::ggml_is_quantized(typ) }
668}
669
670/// Get the name of a ggml type.
671#[must_use]
672pub fn type_name(typ: ggml_type) -> &'static str {
673 unsafe {
674 let ptr = llama_cpp_sys_4::ggml_type_name(typ);
675 if ptr.is_null() {
676 "unknown"
677 } else {
678 CStr::from_ptr(ptr).to_str().unwrap_or("unknown")
679 }
680 }
681}