Skip to main content

rsl_interpolation/types/
cache.rs

1use crate::Accelerator;
2use crate::DomainError;
3use crate::z_idx;
4
5#[derive(Debug, Clone, Copy)]
6/// Cache holding values retrieved from the `za` array.
7///
8/// This object caches the values extracted from the `za` array, which are more likely to be used
9/// again, either by evaluating the spline to a nearby point, or/and by evaluating it's derivates
10/// as well.
11///
12/// Only the **grid** points are cached, **not any interpolated values**.
13///
14/// This optimization seems to be quite effective (up to +50% from my testing), and could be
15/// important in cases where `za` arrays are big and don't fit in the CPU's cache. It is of course,
16/// very situational, since it also depends on the way one evaluates his splines.
17///
18/// The overhead of cache misses should be truly negligible, since the process just falls back to
19/// calculating the values in the usual manner.
20pub struct Cache<T> {
21    acc_indices: (usize, usize),
22    xgrid_values: (T, T),
23    ygrid_values: (T, T),
24    zgrid_values: (T, T, T, T),
25    zx_values: (T, T, T, T),
26    zy_values: (T, T, T, T),
27    zxy_values: (T, T, T, T),
28    partials: (T, T),
29    uninit: bool,
30}
31
32impl<T> Cache<T>
33where
34    T: crate::Num,
35{
36    /// Creates a new empty [`Cache2d`]
37    pub fn new() -> Self {
38        let def = T::nan();
39        Self {
40            acc_indices: (0, 0),
41            xgrid_values: (def, def),
42            ygrid_values: (def, def),
43            zgrid_values: (def, def, def, def),
44            zx_values: (def, def, def, def),
45            zy_values: (def, def, def, def),
46            zxy_values: (def, def, def, def),
47            partials: (def, def),
48            uninit: true,
49        }
50    }
51
52    pub fn reset(&mut self) {
53        *self = Self::new()
54    }
55
56    /// Resets the indices. Useful for benchmarking, to avoid the overhead of resetting all the
57    /// fields at each iteration.
58    pub fn soft_reset(&mut self) {
59        self.acc_indices = (0, 0)
60    }
61
62    pub(crate) fn is_uptodate(&mut self, xa: &[T], ya: &[T], x: T, y: T) -> bool {
63        // The first time that the Cache is being called, the values are uninitialized, but the
64        // interpolator does not know that. This forces the Cache to be updated the first time it
65        // is called after initialization.
66        //
67        // Every evaluation after that is not affected.
68        if self.uninit {
69            self.uninit = false;
70            return false;
71        }
72
73        let xi = self.acc_indices.0;
74        let yi = self.acc_indices.1;
75        let x_inbounds: bool = (x > xa[xi]) && (x < xa[xi + 1]);
76        let y_inbounds: bool = (y > ya[yi]) && (y < ya[yi + 1]);
77        x_inbounds && y_inbounds
78    }
79
80    #[allow(clippy::too_many_arguments)]
81    pub(crate) fn update_step1(
82        &mut self,
83        xa: &[T],
84        ya: &[T],
85        za: &[T],
86        x: T,
87        y: T,
88        xacc: &mut Accelerator,
89        yacc: &mut Accelerator,
90    ) -> Result<(), DomainError> {
91        self.update_acc_indices(xa, ya, x, y, xacc, yacc);
92        self.update_xy_grid_values(xa, ya);
93        self.update_z_grid_values(za, xa.len(), ya.len())?;
94        self.update_partials();
95        Ok(())
96    }
97
98    #[allow(clippy::too_many_arguments)]
99    pub(crate) fn update_step2(
100        &mut self,
101        xa: &[T],
102        ya: &[T],
103        zx: &[T],
104        zy: &[T],
105        zxy: &[T],
106        dt: T,
107        du: T,
108    ) -> Result<(), DomainError> {
109        self.update_zxminmaxing(zx, xa.len(), ya.len(), dt)?;
110        self.update_zyminmaxing(zy, xa.len(), ya.len(), du)?;
111        self.update_zxyminmaxing(zxy, xa.len(), ya.len(), dt * du)?;
112        Ok(())
113    }
114}
115
116/// Methods for updating the cache's fields. Should be called in a specific order.
117impl<T> Cache<T>
118where
119    T: crate::Num,
120{
121    fn update_acc_indices(
122        &mut self,
123        xa: &[T],
124        ya: &[T],
125        x: T,
126        y: T,
127        xacc: &mut Accelerator,
128        yacc: &mut Accelerator,
129    ) {
130        self.acc_indices.0 = xacc.find(xa, x);
131        self.acc_indices.1 = yacc.find(ya, y);
132    }
133
134    fn update_xy_grid_values(&mut self, xa: &[T], ya: &[T]) {
135        let xi = self.acc_indices.0;
136        let yi = self.acc_indices.1;
137        self.xgrid_values = (xa[xi], xa[xi + 1]);
138        self.ygrid_values = (ya[yi], ya[yi + 1]);
139    }
140
141    fn update_z_grid_values(
142        &mut self,
143        za: &[T],
144        xlen: usize,
145        ylen: usize,
146    ) -> Result<(), DomainError> {
147        let xi = self.acc_indices.0;
148        let yi = self.acc_indices.1;
149        self.zgrid_values.0 = za[z_idx(xi, yi, xlen, ylen)?];
150        self.zgrid_values.1 = za[z_idx(xi, yi + 1, xlen, ylen)?];
151        self.zgrid_values.2 = za[z_idx(xi + 1, yi, xlen, ylen)?];
152        self.zgrid_values.3 = za[z_idx(xi + 1, yi + 1, xlen, ylen)?];
153        Ok(())
154    }
155
156    fn update_partials(&mut self) {
157        self.partials.0 = self.xgrid_values.1 - self.xgrid_values.0;
158        self.partials.1 = self.ygrid_values.1 - self.ygrid_values.0;
159    }
160
161    fn update_zxminmaxing(
162        &mut self,
163        zx: &[T],
164        xsize: usize,
165        ysize: usize,
166        d: T,
167    ) -> Result<(), DomainError> {
168        let xi = self.acc_indices.0;
169        let yi = self.acc_indices.1;
170        self.zx_values.0 = zx[z_idx(xi, yi, xsize, ysize)?] / d;
171        self.zx_values.1 = zx[z_idx(xi, yi + 1, xsize, ysize)?] / d;
172        self.zx_values.2 = zx[z_idx(xi + 1, yi, xsize, ysize)?] / d;
173        self.zx_values.3 = zx[z_idx(xi + 1, yi + 1, xsize, ysize)?] / d;
174        Ok(())
175    }
176
177    fn update_zyminmaxing(
178        &mut self,
179        zy: &[T],
180        xsize: usize,
181        ysize: usize,
182        d: T,
183    ) -> Result<(), DomainError> {
184        let xi = self.acc_indices.0;
185        let yi = self.acc_indices.1;
186        self.zy_values.0 = zy[z_idx(xi, yi, xsize, ysize)?] / d;
187        self.zy_values.1 = zy[z_idx(xi, yi + 1, xsize, ysize)?] / d;
188        self.zy_values.2 = zy[z_idx(xi + 1, yi, xsize, ysize)?] / d;
189        self.zy_values.3 = zy[z_idx(xi + 1, yi + 1, xsize, ysize)?] / d;
190        Ok(())
191    }
192
193    fn update_zxyminmaxing(
194        &mut self,
195        zxy: &[T],
196        xsize: usize,
197        ysize: usize,
198        d: T,
199    ) -> Result<(), DomainError> {
200        let xi = self.acc_indices.0;
201        let yi = self.acc_indices.1;
202        self.zxy_values.0 = zxy[z_idx(xi, yi, xsize, ysize)?] / d;
203        self.zxy_values.1 = zxy[z_idx(xi, yi + 1, xsize, ysize)?] / d;
204        self.zxy_values.2 = zxy[z_idx(xi + 1, yi, xsize, ysize)?] / d;
205        self.zxy_values.3 = zxy[z_idx(xi + 1, yi + 1, xsize, ysize)?] / d;
206        Ok(())
207    }
208}
209
210/// Getter methods for grid point quantities
211impl<T> Cache<T>
212where
213    T: crate::Num,
214{
215    pub(crate) fn get_xy_indices(&self) -> (usize, usize) {
216        (self.acc_indices.0, self.acc_indices.1)
217    }
218
219    pub(crate) fn get_xy_grid_values(&self) -> (T, T, T, T) {
220        (
221            self.xgrid_values.0,
222            self.xgrid_values.1,
223            self.ygrid_values.0,
224            self.ygrid_values.1,
225        )
226    }
227
228    pub(crate) fn get_z_grid_values(&self) -> (T, T, T, T) {
229        self.zgrid_values
230    }
231
232    pub(crate) fn get_partials(&self) -> (T, T) {
233        self.partials
234    }
235
236    pub(crate) fn get_zxminmaxxing(&self) -> (T, T, T, T) {
237        self.zx_values
238    }
239
240    pub(crate) fn get_zyminmaxxing(&self) -> (T, T, T, T) {
241        self.zy_values
242    }
243
244    pub(crate) fn get_zxyminmaxxing(&self) -> (T, T, T, T) {
245        self.zxy_values
246    }
247}
248
249impl<T> Default for Cache<T>
250where
251    T: crate::Num,
252{
253    fn default() -> Self {
254        Self::new()
255    }
256}