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
2028
    pub fn new(protection_set: SharedProtectionSet) -> OperationCache {
29
2028
        OperationCache {
30
2028
            protection_set,
31
2028
            caches1: vec![Cache::new()],
32
2028
            caches2: vec![Cache::new(); 3],
33
2028
            caches3: vec![Cache::new()],
34
2028
        }
35
2028
    }
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
49399
    fn get_cache1(&mut self, operator: &UnaryFunction) -> &mut Cache<LddIndex, usize> {
95
49399
        match operator {
96
49399
            UnaryFunction::Len => &mut self.caches1[0],
97
49399
        }
98
49399
    }
99

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

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

            
114
    /// Create an Ldd from the given index. Only safe because this is a private function.
115
180549
    fn create(&mut self, index: LddIndex) -> Ldd {
116
180549
        Ldd::new(&self.protection_set, index)
117
180549
    }
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
6084
    pub fn new() -> Cache<K, V, RandomState> {
131
6084
        Cache {
132
6084
            table: vec![Default::default(); 1024],
133
6084
            hash_builder: RandomState::default(),
134
6084
        }
135
6084
    }
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
4493629
    pub fn get(&mut self, key: &K) -> Option<&V> {
175
4493629
        debug_assert!(*key != K::default(), "The key may never be equal to its default value.");
176

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

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

            
184
    /// Inserts the given key value pair into the cache. Might evict other pairs in the cache.
185
4310967
    pub fn insert(&mut self, key: K, value: V) {
186
4310967
        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
4310967
        let index = self.hash_builder.hash_one(&key) % (self.table.len() as u64);
191
4310967
        self.table[index as usize] = (key, value);
192
4310967
    }
193
}
194

            
195
impl<K: Clone, V: Clone, S: Clone> Clone for Cache<K, V, S> {
196
4056
    fn clone(&self) -> Self {
197
4056
        Cache {
198
4056
            table: self.table.clone(),
199
4056
            hash_builder: self.hash_builder.clone(),
200
4056
        }
201
4056
    }
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
25756
pub fn cache_unary_function<F>(storage: &mut Storage, operator: UnaryFunction, a: &LddRef, f: F) -> usize
223
25756
where
224
25756
    F: Fn(&mut Storage, &LddRef<'_>) -> usize,
225
{
226
25756
    let key = a.index();
227
25756
    if let Some(result) = storage.operation_cache().get_cache1(&operator).get(&key) {
228
2113
        *result
229
    } else {
230
23643
        let result = f(storage, a);
231
23643
        storage.operation_cache().get_cache1(&operator).insert(key, result);
232
23643
        result
233
    }
234
25756
}
235

            
236
/// Implements an operation cache for a binary LDD operator.
237
3961332
pub fn cache_binary_op<F>(storage: &mut Storage, operator: BinaryOperator, a: &LddRef, b: &LddRef, f: F) -> Ldd
238
3961332
where
239
3961332
    F: Fn(&mut Storage, &LddRef<'_>, &LddRef<'_>) -> Ldd,
240
{
241
3961332
    let key = (a.index(), b.index());
242
3961332
    if let Some(result) = storage.operation_cache().get_cache2(&operator).get(&key) {
243
125784
        let result = *result; // Necessary to decouple borrow from storage and the call to create.
244
125784
        storage.operation_cache().create(result)
245
    } else {
246
3835548
        let result = f(storage, a, b);
247
3835548
        storage
248
3835548
            .operation_cache()
249
3835548
            .get_cache2(&operator)
250
3835548
            .insert(key, result.index());
251
3835548
        result
252
    }
253
3961332
}
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
2378487
pub fn cache_comm_binary_op<F>(storage: &mut Storage, operator: BinaryOperator, a: &LddRef, b: &LddRef, f: F) -> Ldd
258
2378487
where
259
2378487
    F: Fn(&mut Storage, &LddRef<'_>, &LddRef<'_>) -> Ldd,
260
{
261
    // Reorder the inputs to improve caching behaviour (can potentially half the cache size)
262
2378487
    if a.index() < b.index() {
263
2308476
        cache_binary_op(storage, operator, a, b, f)
264
    } else {
265
70011
        cache_binary_op(storage, operator, b, a, f)
266
    }
267
2378487
}
268

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