senax_common/cache/
fast_cache.rs1use crossbeam::epoch::{self, Atomic, Owned, Shared};
2use std::sync::{
3 Arc,
4 atomic::{AtomicBool, AtomicU64, Ordering},
5};
6
7use super::msec::{MSec, get_cache_time};
8use crate::cache::db_cache::CacheVal;
9
10const SET_ASSOCIATIVE: u64 = 12;
11const HASH_SHIFT: usize = 24;
12const TIME_MASK: u64 = (1 << HASH_SHIFT) - 1;
13
14pub struct FastCache {
15 index: Vec<AtomicU64>,
16 data: Vec<Atomic<Data>>,
17 ttl: u64,
18 lock: AtomicBool,
19}
20
21struct Data {
22 hash: u128,
23 value: Arc<dyn CacheVal>,
24}
25
26impl Data {
27 fn is_timeout(&self, msec: MSec, ttl: u64) -> bool {
28 self.value._time().less_than_ttl(msec, ttl)
29 }
30}
31
32impl FastCache {
34 pub fn new(size: u64, ttl: u64) -> FastCache {
35 let size = (size
36 / (std::mem::size_of::<AtomicU64>() + std::mem::size_of::<Atomic<Data>>()) as u64)
37 as usize;
38 let size = 1usize << (std::mem::size_of::<usize>() as u32 * 8 - size.leading_zeros() - 1);
39 let mut index = Vec::with_capacity(size);
40 let mut data = Vec::with_capacity(size);
41 for _i in 0..size {
42 index.push(AtomicU64::default());
43 data.push(Atomic::null());
44 }
45
46 FastCache {
47 index,
48 data,
49 lock: AtomicBool::new(false),
50 ttl,
51 }
52 }
53
54 pub fn insert(
56 &self,
57 hash: u128,
58 value: Arc<dyn CacheVal>,
59 ) -> Option<(u128, Arc<dyn CacheVal>)> {
60 let hash_u64 = (hash as u64) ^ ((hash >> 64) as u64);
61 let _lock_guard = self.lock();
62 let (now, msec) = get_cache_time();
63 let index_mask = self.index.len() as u64 - 1;
64 let hash_idx = hash_u64 & index_mask;
65 let mut idx = 0;
66 let mut time = self.index[hash_idx as usize].load(Ordering::Acquire) & TIME_MASK;
67 let mut zero_found = false;
68 let guard = &epoch::pin();
69 for i in 0..SET_ASSOCIATIVE {
70 let pos = ((hash_idx + i) & index_mask) as usize;
71 let candidate = self.index[pos].load(Ordering::Acquire);
72 if !zero_found && candidate == 0 {
73 zero_found = true;
74 idx = i;
75 continue;
76 }
77 if (candidate & !TIME_MASK) == (hash_u64 & !TIME_MASK) {
78 let ptr = self.data[pos].load_consume(guard);
79 if let Some(data) = unsafe { ptr.as_ref() }
80 && data.hash == hash
81 {
82 idx = i;
83 break;
84 }
85 }
86 if !zero_found && u24_less_than(candidate & TIME_MASK, time) {
87 time = candidate & TIME_MASK;
88 idx = i;
89 }
90 }
91 let mut hash_time = (hash_u64 & !TIME_MASK) | (now & TIME_MASK);
92 if hash_time == 0 {
93 hash_time = (hash_u64 & !TIME_MASK) | ((now - 1) & TIME_MASK);
94 }
95 let pos = ((hash_idx + idx) & index_mask) as usize;
96 let old = self.data[pos].swap(Owned::new(Data { hash, value }), Ordering::SeqCst, guard);
97 self.index[pos].store(hash_time, Ordering::Release);
98
99 if !old.is_null() {
100 let ret = unsafe { old.as_ref() }
101 .filter(|v| !v.is_timeout(msec, self.ttl) && v.hash != hash)
102 .map(|v| (v.hash, v.value.clone()));
103 unsafe { guard.defer_destroy(old) };
104 guard.flush();
105 return ret;
106 }
107
108 None
109 }
110
111 pub fn get(&self, hash: u128, now: u64, msec: MSec) -> Option<Arc<dyn CacheVal>> {
112 let index_mask = self.index.len() as u64 - 1;
113 let hash_u64 = (hash as u64) ^ ((hash >> 64) as u64);
114 let hash_idx = hash_u64 & index_mask;
115 let guard = &epoch::pin();
116 for i in 0..SET_ASSOCIATIVE {
117 let pos = ((hash_idx + i) & index_mask) as usize;
118 let candidate = self.index[pos].load(Ordering::Acquire);
119 if candidate != 0 && (candidate & !TIME_MASK) == (hash_u64 & !TIME_MASK) {
120 let ptr = self.data[pos].load_consume(guard);
121 if let Some(data) = unsafe { ptr.as_ref() }
122 && data.hash == hash
123 {
124 if data.is_timeout(msec, self.ttl) {
125 let _lock_guard = self.lock();
126 let old = self.data[pos].swap(Shared::null(), Ordering::SeqCst, guard);
127 if !old.is_null() {
128 unsafe { guard.defer_destroy(old) };
129 guard.flush();
130 }
131 self.index[pos].store(0, Ordering::Release);
132 return None;
133 }
134 let hash_time = (hash_u64 & !TIME_MASK) | (now & TIME_MASK);
135 if candidate != hash_time && hash_time != 0 {
136 let _ = self.index[pos].compare_exchange_weak(
137 candidate,
138 hash_time,
139 Ordering::Relaxed,
140 Ordering::Relaxed,
141 );
142 }
143 return Some(Arc::clone(&data.value));
144 }
145 }
146 }
147 None
148 }
149
150 pub fn invalidate(&self, hash: u128) {
151 let _lock_guard = self.lock();
152 let index_mask = self.index.len() as u64 - 1;
153 let hash_u64 = (hash as u64) ^ ((hash >> 64) as u64);
154 let hash_idx = hash_u64 & index_mask;
155 for i in 0..SET_ASSOCIATIVE {
156 let pos = ((hash_idx + i) & index_mask) as usize;
157 let candidate = self.index[pos].load(Ordering::Acquire);
158 if candidate != 0 && (candidate & !TIME_MASK) == (hash_u64 & !TIME_MASK) {
159 let guard = &epoch::pin();
160 let ptr = self.data[pos].load_consume(guard);
161 if let Some(data) = unsafe { ptr.as_ref() }
162 && data.hash == hash
163 {
164 let old = self.data[pos].swap(Shared::null(), Ordering::SeqCst, guard);
165 if !old.is_null() {
166 unsafe { guard.defer_destroy(old) };
167 guard.flush();
168 }
169 self.index[pos].store(0, Ordering::Release);
170 }
171 return;
172 }
173 }
174 }
175
176 pub fn invalidate_all_of(&self, type_id: u64) {
177 let _lock_guard = self.lock();
178 let guard = &epoch::pin();
179 for i in 0..self.data.len() {
180 let ptr = self.data[i].load_consume(guard);
181 if let Some(data) = unsafe { ptr.as_ref() }
182 && data.value._type_id() == type_id
183 {
184 let old = self.data[i].swap(Shared::null(), Ordering::SeqCst, guard);
185 if !old.is_null() {
186 unsafe { guard.defer_destroy(old) };
187 }
188 self.index[i].store(0, Ordering::Release);
189 }
190 }
191 guard.flush();
192 }
193
194 pub fn invalidate_all(&self) {
195 let _lock_guard = self.lock();
196 let guard = &epoch::pin();
197 for i in 0..self.data.len() {
198 let ptr = self.data[i].load_consume(guard);
199 if !ptr.is_null() {
200 let old = self.data[i].swap(Shared::null(), Ordering::SeqCst, guard);
201 if !old.is_null() {
202 unsafe { guard.defer_destroy(old) };
203 }
204 self.index[i].store(0, Ordering::Release);
205 }
206 }
207 guard.flush();
208 }
209
210 fn lock(&self) -> LockGuard<'_> {
211 while self
212 .lock
213 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
214 .is_err()
215 {
216 std::thread::yield_now();
217 }
218 LockGuard { lock: &self.lock }
219 }
220}
221
222struct LockGuard<'a> {
223 lock: &'a AtomicBool,
224}
225
226impl<'a> Drop for LockGuard<'a> {
227 fn drop(&mut self) {
228 self.lock.store(false, Ordering::Release);
229 }
230}
231
232impl Drop for FastCache {
233 fn drop(&mut self) {
234 self.invalidate_all();
235 }
236}
237
238fn u24_less_than(lhs: u64, rhs: u64) -> bool {
239 let lhs = lhs & 0xFFFFFF;
240 let rhs = rhs & 0xFFFFFF;
241 let lhs = lhs | ((lhs & 0x800000) * 0x1FFFFFFFFFE);
242 let rhs = rhs | ((rhs & 0x800000) * 0x1FFFFFFFFFE);
243 lhs.wrapping_sub(rhs) > u64::MAX / 2
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[derive(Clone, Debug)]
251 struct A(u32);
252 impl CacheVal for A {
253 fn _size(&self) -> u32 {
254 10
255 }
256 fn _type_id(&self) -> u64 {
257 Self::__type_id()
258 }
259 fn __type_id() -> u64 {
260 1
261 }
262 fn _shard_id(&self) -> crate::ShardId {
263 1
264 }
265 fn _time(&self) -> MSec {
266 MSec::now()
267 }
268 fn _estimate() -> usize {
269 10
270 }
271 fn _encode(&self) -> anyhow::Result<Vec<u8>> {
272 Ok(Vec::new())
273 }
274 fn _decode(_v: &[u8]) -> anyhow::Result<Self> {
275 Ok(Self(1))
276 }
277 }
278
279 #[test]
280 fn test_u40_less_than() {
281 assert!(u24_less_than(1, 2));
282 assert!(!u24_less_than(1, 1));
283 assert!(!u24_less_than(2, 1));
284 assert!(u24_less_than(0xffffff, 0));
285 assert!(u24_less_than(0xfffffe, 0xffffff));
286 }
287 #[test]
288 fn test() {
289 let cache = Arc::new(FastCache::new(16, 1000));
290 let cache2 = cache.clone();
291 let (now, msec) = get_cache_time();
292 std::thread::spawn(move || {
293 cache.insert(1, Arc::new(A(1)));
294 cache.insert(1, Arc::new(A(1)));
295 let _result = cache.get(1, now, msec);
296 })
298 .join()
299 .unwrap();
300 std::thread::spawn(move || {
301 cache2.insert(2, Arc::new(A(2)));
302 let _result = cache2.get(2, now, msec);
303 })
305 .join()
306 .unwrap();
307 }
308}