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
3038
    pub fn new(protection_set: SharedProtectionSet) -> OperationCache {
29
3038
        OperationCache {
30
3038
            protection_set,
31
3038
            caches1: vec![Cache::new()],
32
3038
            caches2: vec![Cache::new(); 4],
33
3038
            caches3: vec![Cache::new()],
34
3038
        }
35
3038
    }
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
960
        for cache in self.caches2.iter_mut() {
46
960
            cache.clear();
47
960
        }
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
960
        for cache in self.caches2.iter() {
63
960
            result += cache.len();
64
960
        }
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
960
        for cache in self.caches2.iter_mut() {
86
960
            cache.limit(size / 4);
87
960
        }
88

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

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

            
100
15664316
    fn get_cache2(&mut self, operator: &BinaryOperator) -> &mut Cache<(LddIndex, LddIndex), LddIndex> {
101
15664316
        match operator {
102
10677035
            BinaryOperator::Union => &mut self.caches2[0],
103
3097861
            BinaryOperator::Merge => &mut self.caches2[1],
104
1113317
            BinaryOperator::Minus => &mut self.caches2[2],
105
776103
            BinaryOperator::Project => &mut self.caches2[3],
106
        }
107
15664316
    }
108

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
189
        // Compute the index in the table.
190

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

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

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

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

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

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

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

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

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