mina_tree/
cache.rs

1/// Cache generic field(s) (or any data actually)
2macro_rules! cache {
3    ($F:ty, $compute:expr) => {{
4        // We want to support the generic field F, so we use std::any.
5        // Initializer is `const`.
6        // Note that the macro `thread_local!` uses internally the
7        // unstable `#[thread_local]`.
8        // We use `ManuallyDrop` to avoid dealing with destructor state
9        // so it's fast.
10        // The destructor won't be run, but we don't care.
11        //
12        // See
13        // <https://github.com/rust-lang/rust/blob/635c4a5e612b0ee8af6615635599702d3dce9906/library/std/src/sys/common/thread_local/fast_local.rs#>
14
15        use std::mem::ManuallyDrop;
16        use std::cell::RefCell;
17        use std::any::{Any, TypeId};
18
19        // Generics are always based on Fp or Fq
20        const NUM_MAX_GENERIC: usize = 2;
21
22        thread_local! {
23            static CACHE: ManuallyDrop<RefCell<[Option<Box<dyn Any>>; NUM_MAX_GENERIC]>> =
24                const { ManuallyDrop::new(RefCell::new([None, None])) };
25        }
26
27        CACHE.with(|cache| {
28            let mut cache = cache.borrow_mut();
29            let type_id = TypeId::of::<$F>();
30
31            cache.iter_mut().find(|c| match c {
32                None => true,
33                Some(any) => (&**any).type_id() == type_id,
34            })
35            .unwrap()
36            .get_or_insert_with(|| Box::new($compute))
37            .downcast_ref::<$F>()
38            .cloned()
39            .unwrap()
40        })
41    }};
42}
43
44/// Cache one field (or any data actually)
45macro_rules! cache_one {
46    ($F:ty, $compute:expr) => {{
47        // See comments in `cache` above
48        // Here we don't support generic
49
50        use std::{cell::RefCell, mem::ManuallyDrop};
51
52        thread_local! {
53            static CACHE: ManuallyDrop<RefCell<Option<Box<$F>>>> =
54                const { ManuallyDrop::new(RefCell::new(Option::None)) };
55        }
56
57        CACHE.with(|cache| {
58            let mut cache = cache.borrow_mut();
59            if let Some(cached) = cache.as_ref() {
60                return (**cached).clone();
61            }
62            let data = $compute;
63            let _ = cache.insert(Box::new(data.clone()));
64            data
65        })
66    }};
67}
68
69#[cfg(test)]
70mod tests {
71    use crate::proofs::field::FieldWitness;
72    use ark_ec::short_weierstrass_jacobian::GroupAffine;
73    use poly_commitment::srs::endos;
74    use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
75
76    #[cfg(target_family = "wasm")]
77    use wasm_bindgen_test::wasm_bindgen_test as test;
78
79    #[test]
80    fn test_cache() {
81        use mina_curves::pasta::{Fp, Fq};
82
83        static COUNTER: AtomicUsize = AtomicUsize::new(0);
84
85        fn my_test<F: FieldWitness>() -> (F, F::Scalar) {
86            cache!((F, F::Scalar), {
87                COUNTER.fetch_add(1, Relaxed);
88                endos::<GroupAffine<F::Parameters>>()
89            })
90        }
91
92        let counter = || COUNTER.load(Relaxed);
93
94        assert_eq!(counter(), 0);
95
96        dbg!(my_test::<Fp>());
97        assert_eq!(counter(), 1);
98        dbg!(my_test::<Fp>());
99        dbg!(my_test::<Fp>());
100        dbg!(my_test::<Fp>());
101        assert_eq!(counter(), 1);
102
103        dbg!(my_test::<Fq>());
104        assert_eq!(counter(), 2);
105        dbg!(my_test::<Fq>());
106        dbg!(my_test::<Fq>());
107        dbg!(my_test::<Fq>());
108        assert_eq!(counter(), 2);
109
110        dbg!(my_test::<Fp>());
111        dbg!(my_test::<Fq>());
112        dbg!(my_test::<Fp>());
113        dbg!(my_test::<Fq>());
114        assert_eq!(counter(), 2);
115    }
116}