1
use ahash::RandomState;
2
use core::hash::Hash;
3
use std::hash::BuildHasher;
4

            
5
use crate::Ldd;
6
use crate::LddRef;
7
use crate::Storage;
8

            
9
use super::ldd::LddIndex;
10
use super::ldd::SharedProtectionSet;
11

            
12
/// The operation cache can significantly speed up operations by caching
13
/// intermediate results. This is necessary since the maximal sharing means that
14
/// the same inputs can be encountered many times while evaluating the
15
/// operation.
16
///
17
/// For all operations defined in `operations.rs` where caching helps we
18
/// introduce a cache. The cache that belongs to one operation is identified by
19
/// the value of [UnaryFunction], [BinaryOperator] or [TernaryOperator].
20
pub struct OperationCache {
21
    protection_set: SharedProtectionSet,
22
    caches1: Vec<Cache<LddIndex, usize>>,
23
    caches2: Vec<Cache<(LddIndex, LddIndex), LddIndex>>,
24
    caches3: Vec<Cache<(LddIndex, LddIndex, LddIndex), LddIndex>>,
25
}
26

            
27
impl OperationCache {
28
2932
    pub fn new(protection_set: SharedProtectionSet) -> OperationCache {
29
2932
        OperationCache {
30
2932
            protection_set,
31
2932
            caches1: vec![Cache::new()],
32
2932
            caches2: vec![Cache::new(); 3],
33
2932
            caches3: vec![Cache::new()],
34
2932
        }
35
2932
    }
36

            
37
    /// Clear all existing caches. This must be done during garbage collection
38
    /// since caches have references to elements in the node table that are not
39
    /// protected.
40
240
    pub fn clear(&mut self) {
41
240
        for cache in self.caches1.iter_mut() {
42
240
            cache.clear();
43
240
        }
44

            
45
720
        for cache in self.caches2.iter_mut() {
46
720
            cache.clear();
47
720
        }
48

            
49
240
        for cache in self.caches3.iter_mut() {
50
240
            cache.clear();
51
240
        }
52
240
    }
53

            
54
    /// Returns the number of elements in the operation cache.
55
240
    pub fn len(&self) -> usize {
56
240
        let mut result: usize = 0;
57

            
58
240
        for cache in self.caches1.iter() {
59
240
            result += cache.len();
60
240
        }
61

            
62
720
        for cache in self.caches2.iter() {
63
720
            result += cache.len();
64
720
        }
65

            
66
240
        for cache in self.caches3.iter() {
67
240
            result += cache.len();
68
240
        }
69

            
70
240
        result
71
240
    }
72

            
73
    /// Returns true iff the operation cache is empty.
74
    pub fn is_empty(&self) -> bool {
75
        self.len() == 0
76
    }
77

            
78
    /// Puts a limit on the operation cache size. This will ensure that
79
    /// self.len() <= n if self.limit(n) has been set.
80
240
    pub fn limit(&mut self, size: usize) {
81
240
        for cache in self.caches1.iter_mut() {
82
240
            cache.limit(size / 4);
83
240
        }
84

            
85
720
        for cache in self.caches2.iter_mut() {
86
720
            cache.limit(size / 4);
87
720
        }
88

            
89
240
        for cache in self.caches3.iter_mut() {
90
240
            cache.limit(size / 4);
91
240
        }
92
240
    }
93

            
94
153782
    fn get_cache1(&mut self, operator: &UnaryFunction) -> &mut Cache<LddIndex, usize> {
95
153782
        match operator {
96
153782
            UnaryFunction::Len => &mut self.caches1[0],
97
153782
        }
98
153782
    }
99

            
100
13672562
    fn get_cache2(&mut self, operator: &BinaryOperator) -> &mut Cache<(LddIndex, LddIndex), LddIndex> {
101
13672562
        match operator {
102
9869293
            BinaryOperator::Union => &mut self.caches2[0],
103
3095073
            BinaryOperator::Merge => &mut self.caches2[1],
104
708196
            BinaryOperator::Minus => &mut self.caches2[2],
105
        }
106
13672562
    }
107

            
108
6293128
    fn get_cache3(&mut self, operator: &TernaryOperator) -> &mut Cache<(LddIndex, LddIndex, LddIndex), LddIndex> {
109
6293128
        match operator {
110
6293128
            TernaryOperator::RelationalProduct => &mut self.caches3[0],
111
6293128
        }
112
6293128
    }
113

            
114
    /// Create an Ldd from the given index. Only safe because this is a private function.
115
1544210
    fn create(&mut self, index: LddIndex) -> Ldd {
116
1544210
        Ldd::new(&self.protection_set, index)
117
1544210
    }
118
}
119

            
120
/// Implements an associative mapping between key value pairs, but has a limit
121
/// on the maximum amount of elements stored. The cache requires that default
122
/// values of K are never used in calls to get and insert, because these are
123
/// used to indicate empty cache entries.
124
pub struct Cache<K, V, S = RandomState> {
125
    table: Vec<(K, V)>,
126
    hash_builder: S,
127
}
128

            
129
impl<K: Default + Clone, V: Clone + Default> Cache<K, V, RandomState> {
130
8796
    pub fn new() -> Cache<K, V, RandomState> {
131
8796
        Cache {
132
8796
            table: vec![Default::default(); 1024],
133
8796
            hash_builder: RandomState::default(),
134
8796
        }
135
8796
    }
136
}
137

            
138
impl<K: Default + Clone, V: Clone + Default> Default for Cache<K, V, RandomState> {
139
    fn default() -> Self {
140
        Self::new()
141
    }
142
}
143

            
144
impl<K: Default + Clone, V: Clone + Default, S> Cache<K, V, S> {
145
    /// Removes all elements stored in the cache.
146
1200
    pub fn clear(&mut self) {
147
1200
        let capacity = self.table.len();
148

            
149
1200
        self.table.clear();
150
1200
        self.table.resize(capacity, Default::default());
151
1200
    }
152

            
153
    /// Puts a limit on the maximum self.len() of this cache.
154
1200
    pub fn limit(&mut self, size: usize) {
155
1200
        let power_of_two = size.next_power_of_two();
156

            
157
1200
        self.table.clear();
158
1200
        self.table.resize(power_of_two, Default::default());
159
1200
    }
160

            
161
    /// Returns the amount of elements in the cache.
162
1200
    pub fn len(&self) -> usize {
163
1200
        self.table.len()
164
1200
    }
165

            
166
    /// Returns true iff the cache is empty.
167
    pub fn is_empty(&self) -> bool {
168
        self.len() == 0
169
    }
170
}
171

            
172
impl<K: Default + Eq + Hash, V, S: BuildHasher> Cache<K, V, S> {
173
    /// Check whether key is in the storage, if so returns Some(value) and None otherwise.
174
10848007
    pub fn get(&mut self, key: &K) -> Option<&V> {
175
10848007
        debug_assert!(*key != K::default(), "The key may never be equal to its default value.");
176

            
177
        // Compute the index in the table.
178
10848007
        let index = self.hash_builder.hash_one(key) % (self.table.len() as u64);
179

            
180
10848007
        let entry = &self.table[index as usize];
181
10848007
        if entry.0 == *key { Some(&entry.1) } else { None }
182
10848007
    }
183

            
184
    /// Inserts the given key value pair into the cache. Might evict other pairs in the cache.
185
9271465
    pub fn insert(&mut self, key: K, value: V) {
186
9271465
        debug_assert!(key != K::default(), "The key may never be equal to its default value.");
187

            
188
        // Compute the index in the table.
189

            
190
9271465
        let index = self.hash_builder.hash_one(&key) % (self.table.len() as u64);
191
9271465
        self.table[index as usize] = (key, value);
192
9271465
    }
193
}
194

            
195
impl<K: Clone, V: Clone, S: Clone> Clone for Cache<K, V, S> {
196
5864
    fn clone(&self) -> Self {
197
5864
        Cache {
198
5864
            table: self.table.clone(),
199
5864
            hash_builder: self.hash_builder.clone(),
200
5864
        }
201
5864
    }
202
}
203

            
204
/// Any function from LDD -> usize.
205
pub enum UnaryFunction {
206
    Len,
207
}
208

            
209
/// Any operator from LDD x LDD -> LDD.
210
pub enum BinaryOperator {
211
    Union,
212
    Merge,
213
    Minus,
214
}
215

            
216
/// Any operator from LDD x LDD x LDD -> LDD.
217
pub enum TernaryOperator {
218
    RelationalProduct,
219
}
220

            
221
/// Implements an operation cache for a unary LDD operator.
222
93057
pub fn cache_unary_function<F>(storage: &mut Storage, operator: UnaryFunction, a: &LddRef, f: F) -> usize
223
93057
where
224
93057
    F: Fn(&mut Storage, &LddRef<'_>) -> usize,
225
{
226
93057
    let key = a.index();
227
93057
    if let Some(result) = storage.operation_cache().get_cache1(&operator).get(&key) {
228
32332
        *result
229
    } else {
230
60725
        let result = f(storage, a);
231
60725
        storage.operation_cache().get_cache1(&operator).insert(key, result);
232
60725
        result
233
    }
234
93057
}
235

            
236
/// Implements an operation cache for a binary LDD operator.
237
7307674
pub fn cache_binary_op<F>(storage: &mut Storage, operator: BinaryOperator, a: &LddRef, b: &LddRef, f: F) -> Ldd
238
7307674
where
239
7307674
    F: Fn(&mut Storage, &LddRef<'_>, &LddRef<'_>) -> Ldd,
240
{
241
7307674
    let key = (a.index(), b.index());
242
7307674
    if let Some(result) = storage.operation_cache().get_cache2(&operator).get(&key) {
243
942786
        let result = *result; // Necessary to decouple borrow from storage and the call to create.
244
942786
        storage.operation_cache().create(result)
245
    } else {
246
6364888
        let result = f(storage, a, b);
247
6364888
        storage
248
6364888
            .operation_cache()
249
6364888
            .get_cache2(&operator)
250
6364888
            .insert(key, result.index());
251
6364888
        result
252
    }
253
7307674
}
254

            
255
/// Implements an operation cache for a commutative binary LDD operator, i.e.,
256
/// an operator f such that f(a,b) = f(b,a) for all LDD a and b.
257
5339493
pub fn cache_comm_binary_op<F>(storage: &mut Storage, operator: BinaryOperator, a: &LddRef, b: &LddRef, f: F) -> Ldd
258
5339493
where
259
5339493
    F: Fn(&mut Storage, &LddRef<'_>, &LddRef<'_>) -> Ldd,
260
{
261
    // Reorder the inputs to improve caching behaviour (can potentially half the cache size)
262
5339493
    if a.index() < b.index() {
263
4594520
        cache_binary_op(storage, operator, a, b, f)
264
    } else {
265
744973
        cache_binary_op(storage, operator, b, a, f)
266
    }
267
5339493
}
268

            
269
/// Implements an operation cache for a terniary LDD operator.
270
3447276
pub fn cache_terniary_op<F>(
271
3447276
    storage: &mut Storage,
272
3447276
    operator: TernaryOperator,
273
3447276
    a: &LddRef,
274
3447276
    b: &LddRef,
275
3447276
    c: &LddRef,
276
3447276
    f: F,
277
3447276
) -> Ldd
278
3447276
where
279
3447276
    F: Fn(&mut Storage, &LddRef<'_>, &LddRef<'_>, &LddRef<'_>) -> Ldd,
280
{
281
3447276
    let key = (a.index(), b.index(), c.index());
282
3447276
    if let Some(result) = storage.operation_cache().get_cache3(&operator).get(&key) {
283
601424
        let result = *result; // Necessary to decouple borrow from storage and the call to create.
284
601424
        storage.operation_cache().create(result)
285
    } else {
286
2845852
        let result = f(storage, a, b, c);
287
2845852
        storage
288
2845852
            .operation_cache()
289
2845852
            .get_cache3(&operator)
290
2845852
            .insert(key, result.index());
291
2845852
        result
292
    }
293
3447276
}