1use std::cell::Cell;
3use std::sync::{Arc, atomic::AtomicU64, atomic::Ordering::Relaxed};
4
5use crate::stvec::{Header, StorageVec};
6use crate::{BytePageSize, size::PAGE_SIZES};
7
8const CLASSES: usize = PAGE_SIZES.len();
10const _: () = assert!(CLASSES == BytePageSize::Unset as usize);
11
12const DEFAULT_PAGES_CACHE: [usize; CLASSES] = [128, 64, 64, 32, 16, 8, 16, 2, 1];
16
17thread_local! {
18 static CACHE: Cell<Option<Box<Cache>>> = Cell::new(Some(Box::default()));
19}
20
21struct Cache {
22 limits: [usize; CLASSES],
23 pages: [Vec<StorageVec>; CLASSES],
24 metrics: Arc<PageCacheMetrics>,
25}
26
27impl Default for Cache {
28 fn default() -> Self {
29 Self {
30 limits: DEFAULT_PAGES_CACHE,
31 pages: Default::default(),
32 metrics: PageCacheMetrics::new(),
33 }
34 }
35}
36
37impl Drop for Cache {
38 fn drop(&mut self) {
39 for (class, pages) in self.pages.iter().enumerate() {
40 self.metrics.remove_pages(class, pages.len());
41 }
42 }
43}
44
45impl Cache {
46 fn set_limit(&mut self, class: usize, limit: usize) {
48 self.limits[class] = limit;
49 let pages = &mut self.pages[class];
50 if pages.len() > limit {
51 self.metrics.remove_pages(class, pages.len() - limit);
52 pages.truncate(limit);
55 }
56 }
57}
58
59#[inline]
64fn with_cache<R>(f: impl FnOnce(&mut Cache) -> R) -> Option<R> {
65 CACHE
66 .try_with(|c| {
67 let mut cst = c.take()?;
68 let res = f(&mut cst);
69 c.set(Some(cst));
70 Some(res)
71 })
72 .ok()
73 .flatten()
74}
75
76#[inline]
78pub(crate) fn take_page(size: BytePageSize) -> Option<StorageVec> {
79 let class = size as usize;
80 with_cache(|cst| {
81 let page = cst.pages[class].pop();
82 if page.is_some() {
83 cst.metrics.hit(class);
84 } else {
85 cst.metrics.miss(class);
86 }
87 page
88 })
89 .flatten()
90}
91
92#[inline]
101pub(crate) unsafe fn put_page(ptr: *mut Header, size: BytePageSize) -> bool {
102 let class = size as usize;
103 with_cache(|cst| {
104 if cst.pages[class].len() < cst.limits[class] {
105 cst.pages[class].push(unsafe { StorageVec::recycle(ptr) });
106 cst.metrics.cached(class);
107 true
108 } else {
109 cst.metrics.freed(class);
110 false
111 }
112 })
113 .unwrap_or(false)
114}
115
116pub(crate) fn set_pages_cache(count: usize) {
118 with_cache(|cst| {
119 for class in 0..CLASSES {
120 cst.set_limit(class, count);
121 }
122 });
123}
124
125pub(crate) fn set_page_cache_size(size: BytePageSize, count: usize) {
127 if size != BytePageSize::Unset {
128 with_cache(|cst| cst.set_limit(size as usize, count));
129 }
130}
131
132#[cfg(test)]
133pub(crate) fn reset() {
135 CACHE.with(|c| c.set(Some(Box::default())));
136}
137
138#[cfg(test)]
139pub(crate) fn cached_pages(size: BytePageSize) -> usize {
141 with_cache(|cst| cst.pages[size as usize].len()).unwrap()
142}
143
144#[derive(Debug, Default)]
161pub struct PageCacheMetrics {
162 classes: [Class; CLASSES],
163}
164
165#[derive(Debug, Default)]
166struct Class {
167 hits: AtomicU64,
168 misses: AtomicU64,
169 cached: AtomicU64,
170 freed: AtomicU64,
171 added: AtomicU64,
174 removed: AtomicU64,
175}
176
177#[derive(Copy, Clone, Debug, PartialEq, Eq)]
179pub struct PageCacheSnapshot {
180 sizes: [PageCacheStats; CLASSES],
181}
182
183#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
185#[non_exhaustive]
186pub struct PageCacheStats {
187 pub size: BytePageSize,
189 pub hits: u64,
191 pub misses: u64,
193 pub cached: u64,
195 pub freed: u64,
197 pub pages: usize,
199 pub bytes: usize,
201}
202
203impl PageCacheMetrics {
204 pub fn new() -> Arc<Self> {
206 Arc::default()
207 }
208
209 pub fn current() -> Arc<Self> {
214 with_cache(|cst| cst.metrics.clone()).unwrap_or_default()
215 }
216
217 pub fn set_current(metrics: Arc<Self>) {
223 with_cache(|cst| {
224 for (class, pages) in cst.pages.iter().enumerate() {
225 cst.metrics.remove_pages(class, pages.len());
226 metrics.add_pages(class, pages.len());
227 }
228 cst.metrics = metrics;
229 });
230 }
231
232 pub fn snapshot(&self) -> PageCacheSnapshot {
234 let mut snapshot = PageCacheSnapshot::default();
235 for (stats, class) in snapshot.sizes.iter_mut().zip(&self.classes) {
236 stats.hits = class.hits.load(Relaxed);
237 stats.misses = class.misses.load(Relaxed);
238 stats.cached = class.cached.load(Relaxed);
239 stats.freed = class.freed.load(Relaxed);
240 let removed = class.removed.load(Relaxed);
241 let hits = stats.hits;
242 stats.pages =
244 (stats.cached + class.added.load(Relaxed)).saturating_sub(hits + removed) as usize;
245 stats.bytes = stats.pages * stats.size.alloc_size();
246 }
247 snapshot
248 }
249
250 pub(crate) fn hit(&self, class: usize) {
251 inc(&self.classes[class].hits, 1);
252 }
253
254 pub(crate) fn miss(&self, class: usize) {
255 inc(&self.classes[class].misses, 1);
256 }
257
258 pub(crate) fn cached(&self, class: usize) {
259 inc(&self.classes[class].cached, 1);
260 }
261
262 pub(crate) fn freed(&self, class: usize) {
263 inc(&self.classes[class].freed, 1);
264 }
265
266 pub(crate) fn remove_pages(&self, class: usize, pages: usize) {
268 if pages != 0 {
269 inc(&self.classes[class].removed, pages as u64);
270 }
271 }
272
273 pub(crate) fn add_pages(&self, class: usize, pages: usize) {
274 if pages != 0 {
275 inc(&self.classes[class].added, pages as u64);
276 }
277 }
278}
279
280#[inline]
282fn inc(counter: &AtomicU64, n: u64) {
283 counter.store(counter.load(Relaxed).wrapping_add(n), Relaxed);
284}
285
286impl PageCacheSnapshot {
287 pub fn get(&self, size: BytePageSize) -> Option<&PageCacheStats> {
290 self.sizes.get(size as usize)
291 }
292
293 pub fn iter(&self) -> impl Iterator<Item = &PageCacheStats> {
295 self.sizes.iter()
296 }
297
298 pub fn total(&self) -> PageCacheStats {
300 self.sizes.iter().fold(
301 PageCacheStats {
302 size: BytePageSize::Unset,
303 ..PageCacheStats::default()
304 },
305 |mut total, s| {
306 total.hits += s.hits;
307 total.misses += s.misses;
308 total.cached += s.cached;
309 total.freed += s.freed;
310 total.pages += s.pages;
311 total.bytes += s.bytes;
312 total
313 },
314 )
315 }
316}
317
318impl Default for PageCacheSnapshot {
319 fn default() -> Self {
320 Self {
321 sizes: PAGE_SIZES.map(|size| PageCacheStats {
322 size,
323 ..PageCacheStats::default()
324 }),
325 }
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use crate::*;
333
334 #[test]
335 fn cached() {
336 reset();
337
338 let mut st = StorageVec::sized(BytePageSize::Size8);
339 assert_eq!(st.page_size(), BytePageSize::Size8);
340
341 st.put_u8(b'h');
342 let addr = st.0;
343 drop(st);
344
345 let st = StorageVec::sized(BytePageSize::Size8);
346 assert_eq!(addr, st.0);
347 }
348
349 #[test]
350 fn cache_metrics() {
351 reset();
352 let metrics = PageCacheMetrics::current();
353 assert_eq!(metrics.snapshot(), PageCacheSnapshot::default());
354
355 let size = BytePageSize::Size8;
356 let st = StorageVec::sized(size);
357 let s = *metrics.snapshot().get(size).unwrap();
358 assert_eq!(
359 (s.size, s.hits, s.misses, s.cached, s.pages),
360 (size, 0, 1, 0, 0)
361 );
362
363 drop(st);
364 let s = *metrics.snapshot().get(size).unwrap();
365 assert_eq!(
366 (s.hits, s.misses, s.cached, s.freed, s.pages),
367 (0, 1, 1, 0, 1)
368 );
369 assert_eq!(s.bytes, 8 * 1024);
370
371 let st = StorageVec::sized(size);
372 let s = *metrics.snapshot().get(size).unwrap();
373 assert_eq!(
374 (s.hits, s.misses, s.cached, s.pages, s.bytes),
375 (1, 1, 1, 0, 0)
376 );
377
378 crate::set_page_cache_size(size, 0);
379 drop(st);
380 let s = *metrics.snapshot().get(size).unwrap();
381 assert_eq!((s.cached, s.freed, s.pages), (1, 1, 0));
382
383 drop(StorageVec::sized(BytePageSize::Size4));
385 drop(StorageVec::with_capacity(100));
386 let snapshot = metrics.snapshot();
387 let s = *snapshot.get(BytePageSize::Size4).unwrap();
388 assert_eq!((s.misses, s.cached, s.pages), (1, 1, 1));
389 assert!(snapshot.get(BytePageSize::Unset).is_none());
390 assert_eq!(snapshot.iter().count(), 9);
391 let total = snapshot.total();
392 assert_eq!(total.size, BytePageSize::Unset);
393 assert_eq!(
394 (total.hits, total.misses, total.cached, total.freed),
395 (1, 2, 2, 1)
396 );
397 assert_eq!((total.pages, total.bytes), (1, 4 * 1024));
398
399 reset();
401 assert_eq!(metrics.snapshot().total().pages, 0);
402 assert!(!Arc::ptr_eq(&metrics, &PageCacheMetrics::current()));
403 }
404
405 #[test]
406 fn cache_metrics_set_current() {
407 reset();
408 let size = BytePageSize::Size16;
409 let old = PageCacheMetrics::current();
410 drop(StorageVec::sized(size));
411 assert_eq!(old.snapshot().get(size).unwrap().pages, 1);
412
413 let metrics = PageCacheMetrics::new();
414 PageCacheMetrics::set_current(metrics.clone());
415 assert!(Arc::ptr_eq(&metrics, &PageCacheMetrics::current()));
416 assert_eq!(old.snapshot().get(size).unwrap().pages, 0);
417 assert_eq!(metrics.snapshot().get(size).unwrap().pages, 1);
418
419 drop(StorageVec::sized(size));
420 let s = *metrics.snapshot().get(size).unwrap();
421 assert_eq!((s.hits, s.misses, s.cached, s.pages), (1, 0, 1, 1));
422 assert_eq!(old.snapshot().get(size).unwrap().hits, 0);
423
424 let other = std::thread::spawn(PageCacheMetrics::current)
426 .join()
427 .unwrap();
428 assert!(!Arc::ptr_eq(&metrics, &other));
429 reset();
430 }
431
432 #[test]
433 fn default_cache_limit_per_page_size() {
434 assert_eq!(DEFAULT_PAGES_CACHE, [128, 64, 64, 32, 16, 8, 16, 2, 1]);
435 for size in crate::size::PAGE_SIZES {
436 reset();
437
438 let limit = DEFAULT_PAGES_CACHE[size as usize];
439 let pages: Vec<_> = (0..limit + 4).map(|_| StorageVec::sized(size)).collect();
440 drop(pages);
441 assert_eq!(cached_pages(size), limit, "{size:?}");
442 }
443 reset();
444 }
445
446 #[test]
447 #[allow(deprecated)]
448 fn pages_cache_size() {
449 reset();
450
451 crate::set_pages_cache(1);
452 drop((
453 StorageVec::sized(BytePageSize::Size4),
454 StorageVec::sized(BytePageSize::Size4),
455 ));
456 assert_eq!(cached_pages(BytePageSize::Size4), 1);
457
458 crate::set_pages_cache(0);
459 drop(StorageVec::sized(BytePageSize::Size8));
460 assert_eq!(cached_pages(BytePageSize::Size8), 0);
461
462 crate::set_pages_cache(16);
464 let st = StorageVec::sized(BytePageSize::Size16);
465 let cache = CACHE.with(Cell::take);
466 drop(st);
467 CACHE.with(|c| c.set(cache));
468 assert_eq!(cached_pages(BytePageSize::Size16), 0);
469
470 let cache = CACHE.with(Cell::take);
472 crate::set_pages_cache(3);
473 CACHE.with(|c| c.set(cache));
474 assert_eq!(cache_limits(), [16; CLASSES]);
475 }
476
477 fn cache_limits() -> [usize; CLASSES] {
478 CACHE.with(|c| {
479 let cst = c.take().unwrap();
480 let limits = cst.limits;
481 c.set(Some(cst));
482 limits
483 })
484 }
485
486 #[test]
487 #[allow(deprecated)]
488 fn page_cache_size() {
489 reset();
490
491 crate::set_page_cache_size(BytePageSize::Size64, 1);
492 crate::set_page_cache_size(BytePageSize::Unset, 5);
493 let mut expected = DEFAULT_PAGES_CACHE;
494 expected[BytePageSize::Size64 as usize] = 1;
495 assert_eq!(cache_limits(), expected);
496
497 drop((
498 StorageVec::sized(BytePageSize::Size64),
499 StorageVec::sized(BytePageSize::Size64),
500 StorageVec::sized(BytePageSize::Size48),
501 StorageVec::sized(BytePageSize::Size48),
502 ));
503 assert_eq!(cached_pages(BytePageSize::Size64), 1);
504 assert_eq!(cached_pages(BytePageSize::Size48), 2);
505
506 let cache = CACHE.with(Cell::take);
508 crate::set_page_cache_size(BytePageSize::Size48, 3);
509 CACHE.with(|c| c.set(cache));
510 assert_eq!(cache_limits(), expected);
511
512 crate::set_pages_cache(2);
513 assert_eq!(cache_limits(), [2; CLASSES]);
514 reset();
515 }
516
517 #[test]
518 #[allow(deprecated)]
519 fn lowered_limit_releases_pages() {
520 reset();
521 let metrics = PageCacheMetrics::current();
522 let pages = |size| metrics.snapshot().get(size).unwrap().pages;
523
524 let (s4, s64) = (BytePageSize::Size4, BytePageSize::Size64);
525 drop([0; 4].map(|_| StorageVec::sized(s64)));
526 drop([0; 4].map(|_| StorageVec::sized(s4)));
527 assert_eq!((cached_pages(s64), pages(s64)), (4, 4));
528
529 crate::set_page_cache_size(s64, 1);
530 assert_eq!((cached_pages(s64), pages(s64)), (1, 1));
531 assert_eq!((cached_pages(s4), pages(s4)), (4, 4));
532 crate::set_page_cache_size(s64, 0);
533 assert_eq!((cached_pages(s64), pages(s64)), (0, 0));
534
535 crate::set_pages_cache(1);
537 assert_eq!((cached_pages(s4), pages(s4)), (1, 1));
538 let total = metrics.snapshot().total();
539 assert_eq!((total.cached, total.freed, total.pages), (8, 0, 1));
540
541 crate::set_page_cache_size(s4, 8);
543 assert_eq!((cached_pages(s4), pages(s4)), (1, 1));
544 reset();
545 }
546
547 #[test]
548 fn bytes_mut_with_page_size() {
549 reset();
550
551 let mut buf = BytesMut::with_page_size(BytePageSize::Size8);
552 assert_eq!(buf.page_size(), BytePageSize::Size8);
553 assert_eq!(buf.capacity(), BytePageSize::Size8.capacity());
554 let ptr = buf.as_ptr();
555
556 buf.extend_from_slice(&[1; 1000]);
558 let head = buf.split_to(500);
559 drop(buf);
560 assert_eq!(cached_pages(BytePageSize::Size8), 0);
561 drop(head);
562 assert_eq!(cached_pages(BytePageSize::Size8), 1);
563
564 let buf = BytesMut::with_page_size(BytePageSize::Size8);
565 assert_eq!(buf.as_ptr(), ptr);
566 assert_eq!(buf.capacity(), BytePageSize::Size8.capacity());
567 assert_eq!(cached_pages(BytePageSize::Size8), 0);
568
569 let buf = BytesMut::with_page_size(BytePageSize::Unset);
571 assert_eq!(buf.page_size(), BytePageSize::Size64);
572 assert_eq!(buf.capacity(), BytePageSize::Size64.capacity());
573 drop(buf);
574 assert_eq!(cached_pages(BytePageSize::Size64), 1);
575
576 let buf = BytesMut::with_capacity(BytePageSize::Size64.capacity() + 1);
578 assert_eq!(buf.page_size(), BytePageSize::Unset);
579 assert_eq!(BytesMut::new().page_size(), BytePageSize::Unset);
580 assert_eq!(BytesMut::with_capacity(64).page_size(), BytePageSize::Unset);
581 assert_eq!(
582 BytesMut::copy_from_slice(b"hello").page_size(),
583 BytePageSize::Unset
584 );
585 drop(buf);
586 for size in crate::size::PAGE_SIZES {
587 let expected = usize::from(size == BytePageSize::Size64);
588 assert_eq!(cached_pages(size), expected);
589 }
590 }
591}