Skip to main content

tropical_gemm/core/
workspace.rs

1use crate::types::TropicalScalar;
2
3/// Reusable CPU packing storage, shared by sequential calls through an exclusive borrow.
4///
5/// Each parallel output task gets its own pair of buffers. Storage grows to the
6/// largest panels and task count used so far; call [`Self::clear`] to release it.
7/// This reuses packing allocations, not result matrices or Rayon task metadata.
8/// The scalar parameter permits reuse across semirings with the same scalar type.
9///
10/// ```
11/// use tropical_gemm::{GemmWorkspace, TropicalGemm, TropicalMaxPlus};
12/// let mut workspace = GemmWorkspace::<f32>::new();
13/// let mut c = vec![TropicalMaxPlus(0.0); 4];
14/// for _ in 0..3 {
15///     TropicalGemm::new(2, 2, 2).execute_with_workspace(
16///         &[1.0; 4], 2, &[2.0; 4], 2, &mut c, 2, &mut workspace,
17///     );
18/// }
19/// assert!(c.iter().all(|v| v.0 == 3.0));
20/// ```
21#[derive(Debug)]
22pub struct GemmWorkspace<T: TropicalScalar> {
23    buffers: Vec<PackingBuffers<T>>,
24}
25
26impl<T: TropicalScalar> Default for GemmWorkspace<T> {
27    fn default() -> Self {
28        Self {
29            buffers: Vec::new(),
30        }
31    }
32}
33
34impl<T: TropicalScalar> GemmWorkspace<T> {
35    pub fn new() -> Self {
36        Self::default()
37    }
38
39    /// Bytes reserved by scalar packing buffers (excluding task metadata).
40    pub fn capacity_bytes(&self) -> usize {
41        self.buffers
42            .iter()
43            .map(|b| {
44                b.a.capacity() * std::mem::size_of::<T>()
45                    + b.b.capacity() * std::mem::size_of::<T>()
46            })
47            .sum()
48    }
49
50    /// Release all packing buffers. The next call will allocate them again.
51    pub fn clear(&mut self) {
52        self.buffers.clear();
53    }
54
55    pub(super) fn tasks(&mut self, count: usize) -> &mut [PackingBuffers<T>] {
56        self.buffers
57            .resize_with(count.max(self.buffers.len()), PackingBuffers::default);
58        &mut self.buffers[..count]
59    }
60}
61
62#[derive(Debug)]
63pub(super) struct PackingBuffers<T: TropicalScalar> {
64    pub a: Vec<T>,
65    pub b: Vec<T>,
66}
67
68impl<T: TropicalScalar> Default for PackingBuffers<T> {
69    fn default() -> Self {
70        Self {
71            a: Vec::new(),
72            b: Vec::new(),
73        }
74    }
75}
76
77impl<T: TropicalScalar> PackingBuffers<T> {
78    pub fn prepare(&mut self, a_len: usize, b_len: usize) {
79        // Packing overwrites every used element, including edge padding. Old
80        // contents need no clearing, and unused capacity never enters a kernel.
81        if self.a.len() < a_len {
82            self.a.resize(a_len, T::scalar_zero());
83        }
84        if self.b.len() < b_len {
85            self.b.resize(b_len, T::scalar_zero());
86        }
87    }
88}