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]
98pub(crate) fn take_page_or_prev(size: BytePageSize) -> Option<StorageVec> {
99 let class = size as usize;
100 with_cache(|cst| {
101 if let Some(page) = cst.pages[class].pop() {
102 cst.metrics.hit(class);
103 return Some(page);
104 }
105 cst.metrics.miss(class);
106
107 let prev = size.prev() as usize;
108 if prev != class
109 && let Some(page) = cst.pages[prev].pop()
110 {
111 cst.metrics.hit(prev);
112 cst.metrics.fallback(class);
113 return Some(page);
114 }
115 None
116 })
117 .flatten()
118}
119
120#[inline]
129pub(crate) unsafe fn put_page(ptr: *mut Header, size: BytePageSize) -> bool {
130 let class = size as usize;
131 with_cache(|cst| {
132 if cst.pages[class].len() < cst.limits[class] {
133 cst.pages[class].push(unsafe { StorageVec::recycle(ptr) });
136 cst.metrics.cached(class);
137 true
138 } else {
139 cst.metrics.freed(class);
140 false
141 }
142 })
143 .unwrap_or(false)
144}
145
146pub(crate) fn set_pages_cache(count: usize) {
148 with_cache(|cst| {
149 for class in 0..CLASSES {
150 cst.set_limit(class, count);
151 }
152 });
153}
154
155pub(crate) fn set_page_cache_size(size: BytePageSize, count: usize) {
157 if size != BytePageSize::Unset {
158 with_cache(|cst| cst.set_limit(size as usize, count));
159 }
160}
161
162#[cfg(test)]
163pub(crate) fn reset() {
168 CACHE.with(|c| {
169 drop(c.take());
170 c.set(Some(Box::default()));
171 });
172}
173
174#[cfg(test)]
175pub(crate) fn cached_pages(size: BytePageSize) -> usize {
177 with_cache(|cst| cst.pages[size as usize].len()).unwrap()
178}
179
180#[derive(Debug, Default)]
197pub struct PageCacheMetrics {
198 classes: [Class; CLASSES],
199}
200
201#[derive(Debug, Default)]
202struct Class {
203 hits: AtomicU64,
204 misses: AtomicU64,
205 fallbacks: AtomicU64,
206 cached: AtomicU64,
207 freed: AtomicU64,
208 added: AtomicU64,
211 removed: AtomicU64,
212}
213
214#[derive(Copy, Clone, Debug, PartialEq, Eq)]
216pub struct PageCacheSnapshot {
217 sizes: [PageCacheStats; CLASSES],
218}
219
220#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
222#[non_exhaustive]
223pub struct PageCacheStats {
224 pub size: BytePageSize,
226 pub hits: u64,
229 pub misses: u64,
234 pub fallbacks: u64,
237 pub cached: u64,
239 pub freed: u64,
241 pub pages: usize,
243 pub bytes: usize,
245}
246
247impl PageCacheMetrics {
248 pub fn new() -> Arc<Self> {
250 Arc::default()
251 }
252
253 pub fn current() -> Arc<Self> {
258 with_cache(|cst| cst.metrics.clone()).unwrap_or_default()
259 }
260
261 pub fn set_current(metrics: Arc<Self>) {
267 with_cache(|cst| {
268 for (class, pages) in cst.pages.iter().enumerate() {
269 cst.metrics.remove_pages(class, pages.len());
270 metrics.add_pages(class, pages.len());
271 }
272 cst.metrics = metrics;
273 });
274 }
275
276 pub fn snapshot(&self) -> PageCacheSnapshot {
278 let mut snapshot = PageCacheSnapshot::default();
279 for (stats, class) in snapshot.sizes.iter_mut().zip(&self.classes) {
280 stats.hits = class.hits.load(Relaxed);
281 stats.misses = class.misses.load(Relaxed);
282 stats.fallbacks = class.fallbacks.load(Relaxed);
283 stats.cached = class.cached.load(Relaxed);
284 stats.freed = class.freed.load(Relaxed);
285 let removed = class.removed.load(Relaxed);
286 let hits = stats.hits;
287 stats.pages =
289 (stats.cached + class.added.load(Relaxed)).saturating_sub(hits + removed) as usize;
290 stats.bytes = stats.pages * stats.size.alloc_size();
291 }
292 snapshot
293 }
294
295 pub(crate) fn hit(&self, class: usize) {
296 inc(&self.classes[class].hits, 1);
297 }
298
299 pub(crate) fn miss(&self, class: usize) {
300 inc(&self.classes[class].misses, 1);
301 }
302
303 pub(crate) fn fallback(&self, class: usize) {
304 inc(&self.classes[class].fallbacks, 1);
305 }
306
307 pub(crate) fn cached(&self, class: usize) {
308 inc(&self.classes[class].cached, 1);
309 }
310
311 pub(crate) fn freed(&self, class: usize) {
312 inc(&self.classes[class].freed, 1);
313 }
314
315 pub(crate) fn remove_pages(&self, class: usize, pages: usize) {
317 if pages != 0 {
318 inc(&self.classes[class].removed, pages as u64);
319 }
320 }
321
322 pub(crate) fn add_pages(&self, class: usize, pages: usize) {
323 if pages != 0 {
324 inc(&self.classes[class].added, pages as u64);
325 }
326 }
327}
328
329#[inline]
331fn inc(counter: &AtomicU64, n: u64) {
332 counter.store(counter.load(Relaxed).wrapping_add(n), Relaxed);
333}
334
335impl PageCacheSnapshot {
336 pub fn get(&self, size: BytePageSize) -> Option<&PageCacheStats> {
339 self.sizes.get(size as usize)
340 }
341
342 pub fn iter(&self) -> impl Iterator<Item = &PageCacheStats> {
344 self.sizes.iter()
345 }
346
347 pub fn total(&self) -> PageCacheStats {
349 self.sizes.iter().fold(
350 PageCacheStats {
351 size: BytePageSize::Unset,
352 ..PageCacheStats::default()
353 },
354 |mut total, s| {
355 total.hits += s.hits;
356 total.misses += s.misses;
357 total.fallbacks += s.fallbacks;
358 total.cached += s.cached;
359 total.freed += s.freed;
360 total.pages += s.pages;
361 total.bytes += s.bytes;
362 total
363 },
364 )
365 }
366}
367
368impl Default for PageCacheSnapshot {
369 fn default() -> Self {
370 Self {
371 sizes: PAGE_SIZES.map(|size| PageCacheStats {
372 size,
373 ..PageCacheStats::default()
374 }),
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use crate::*;
383
384 #[test]
389 fn cached() {
390 reset();
391
392 let mut st = StorageVec::sized(BytePageSize::Size8);
393 assert_eq!(st.page_size(), BytePageSize::Size8);
394
395 st.put_u8(b'h');
396 let addr = st.0;
397 drop(st);
398
399 let st = StorageVec::sized(BytePageSize::Size8);
400 assert_eq!(addr, st.0);
401 }
402
403 #[test]
409 fn cache_metrics() {
410 reset();
411 let metrics = PageCacheMetrics::current();
412 assert_eq!(metrics.snapshot(), PageCacheSnapshot::default());
413
414 let size = BytePageSize::Size8;
415 let st = StorageVec::sized(size);
416 let s = *metrics.snapshot().get(size).unwrap();
417 assert_eq!(
418 (s.size, s.hits, s.misses, s.cached, s.pages),
419 (size, 0, 1, 0, 0)
420 );
421
422 drop(st);
423 let s = *metrics.snapshot().get(size).unwrap();
424 assert_eq!(
425 (s.hits, s.misses, s.cached, s.freed, s.pages),
426 (0, 1, 1, 0, 1)
427 );
428 assert_eq!(s.bytes, 8 * 1024);
429
430 let st = StorageVec::sized(size);
431 let s = *metrics.snapshot().get(size).unwrap();
432 assert_eq!(
433 (s.hits, s.misses, s.cached, s.pages, s.bytes),
434 (1, 1, 1, 0, 0)
435 );
436
437 crate::set_page_cache_size(size, 0);
438 drop(st);
439 let s = *metrics.snapshot().get(size).unwrap();
440 assert_eq!((s.cached, s.freed, s.pages), (1, 1, 0));
441
442 drop(StorageVec::sized(BytePageSize::Size4));
444 drop(StorageVec::with_capacity(100));
445 let snapshot = metrics.snapshot();
446 let s = *snapshot.get(BytePageSize::Size4).unwrap();
447 assert_eq!((s.misses, s.cached, s.pages), (1, 1, 1));
448 assert!(snapshot.get(BytePageSize::Unset).is_none());
449 assert_eq!(snapshot.iter().count(), 9);
450 let total = snapshot.total();
451 assert_eq!(total.size, BytePageSize::Unset);
452 assert_eq!(
453 (total.hits, total.misses, total.cached, total.freed),
454 (1, 2, 2, 1)
455 );
456 assert_eq!((total.pages, total.bytes), (1, 4 * 1024));
457
458 reset();
460 assert_eq!(metrics.snapshot().total().pages, 0);
461 assert!(!Arc::ptr_eq(&metrics, &PageCacheMetrics::current()));
462 }
463
464 #[test]
470 fn cache_metrics_set_current() {
471 reset();
472 let size = BytePageSize::Size16;
473 let old = PageCacheMetrics::current();
474 drop(StorageVec::sized(size));
475 assert_eq!(old.snapshot().get(size).unwrap().pages, 1);
476
477 let metrics = PageCacheMetrics::new();
478 PageCacheMetrics::set_current(metrics.clone());
479 assert!(Arc::ptr_eq(&metrics, &PageCacheMetrics::current()));
480 assert_eq!(old.snapshot().get(size).unwrap().pages, 0);
481 assert_eq!(metrics.snapshot().get(size).unwrap().pages, 1);
482
483 drop(StorageVec::sized(size));
484 let s = *metrics.snapshot().get(size).unwrap();
485 assert_eq!((s.hits, s.misses, s.cached, s.pages), (1, 0, 1, 1));
486 assert_eq!(old.snapshot().get(size).unwrap().hits, 0);
487
488 let other = std::thread::spawn(PageCacheMetrics::current)
490 .join()
491 .unwrap();
492 assert!(!Arc::ptr_eq(&metrics, &other));
493 reset();
494 }
495
496 #[test]
501 fn default_cache_limit_per_page_size() {
502 assert_eq!(DEFAULT_PAGES_CACHE, [128, 64, 64, 32, 16, 8, 16, 2, 1]);
503 for size in crate::size::PAGE_SIZES {
504 reset();
505
506 let limit = DEFAULT_PAGES_CACHE[size as usize];
507 let pages: Vec<_> = (0..limit + 4).map(|_| StorageVec::sized(size)).collect();
508 drop(pages);
509 assert_eq!(cached_pages(size), limit, "{size:?}");
510 }
511 reset();
512 }
513
514 #[test]
520 #[allow(deprecated)]
521 fn pages_cache_size() {
522 reset();
523
524 crate::set_pages_cache(1);
525 drop((
526 StorageVec::sized(BytePageSize::Size4),
527 StorageVec::sized(BytePageSize::Size4),
528 ));
529 assert_eq!(cached_pages(BytePageSize::Size4), 1);
530
531 crate::set_pages_cache(0);
532 drop(StorageVec::sized(BytePageSize::Size8));
533 assert_eq!(cached_pages(BytePageSize::Size8), 0);
534
535 crate::set_pages_cache(16);
537 let st = StorageVec::sized(BytePageSize::Size16);
538 let cache = CACHE.with(Cell::take);
539 drop(st);
540 CACHE.with(|c| c.set(cache));
541 assert_eq!(cached_pages(BytePageSize::Size16), 0);
542
543 let cache = CACHE.with(Cell::take);
545 crate::set_pages_cache(3);
546 CACHE.with(|c| c.set(cache));
547 assert_eq!(cache_limits(), [16; CLASSES]);
548 }
549
550 fn cache_limits() -> [usize; CLASSES] {
551 CACHE.with(|c| {
552 let cst = c.take().unwrap();
553 let limits = cst.limits;
554 c.set(Some(cst));
555 limits
556 })
557 }
558
559 #[test]
565 #[allow(deprecated)]
566 fn page_cache_size() {
567 reset();
568
569 crate::set_page_cache_size(BytePageSize::Size64, 1);
570 crate::set_page_cache_size(BytePageSize::Unset, 5);
571 let mut expected = DEFAULT_PAGES_CACHE;
572 expected[BytePageSize::Size64 as usize] = 1;
573 assert_eq!(cache_limits(), expected);
574
575 drop((
576 StorageVec::sized(BytePageSize::Size64),
577 StorageVec::sized(BytePageSize::Size64),
578 StorageVec::sized(BytePageSize::Size48),
579 StorageVec::sized(BytePageSize::Size48),
580 ));
581 assert_eq!(cached_pages(BytePageSize::Size64), 1);
582 assert_eq!(cached_pages(BytePageSize::Size48), 2);
583
584 let cache = CACHE.with(Cell::take);
586 crate::set_page_cache_size(BytePageSize::Size48, 3);
587 CACHE.with(|c| c.set(cache));
588 assert_eq!(cache_limits(), expected);
589
590 crate::set_pages_cache(2);
591 assert_eq!(cache_limits(), [2; CLASSES]);
592 reset();
593 }
594
595 #[test]
601 #[allow(deprecated)]
602 fn lowered_limit_releases_pages() {
603 reset();
604 let metrics = PageCacheMetrics::current();
605 let pages = |size| metrics.snapshot().get(size).unwrap().pages;
606
607 let (s4, s64) = (BytePageSize::Size4, BytePageSize::Size64);
608 drop([0; 4].map(|_| StorageVec::sized(s64)));
609 drop([0; 4].map(|_| StorageVec::sized(s4)));
610 assert_eq!((cached_pages(s64), pages(s64)), (4, 4));
611
612 crate::set_page_cache_size(s64, 1);
613 assert_eq!((cached_pages(s64), pages(s64)), (1, 1));
614 assert_eq!((cached_pages(s4), pages(s4)), (4, 4));
615 crate::set_page_cache_size(s64, 0);
616 assert_eq!((cached_pages(s64), pages(s64)), (0, 0));
617
618 crate::set_pages_cache(1);
620 assert_eq!((cached_pages(s4), pages(s4)), (1, 1));
621 let total = metrics.snapshot().total();
622 assert_eq!((total.cached, total.freed, total.pages), (8, 0, 1));
623
624 crate::set_page_cache_size(s4, 8);
626 assert_eq!((cached_pages(s4), pages(s4)), (1, 1));
627 reset();
628 }
629
630 #[test]
636 fn bytes_mut_with_page_size() {
637 reset();
638
639 let mut buf = BytesMut::with_page_size(BytePageSize::Size8);
640 assert_eq!(buf.page_size(), BytePageSize::Size8);
641 assert_eq!(buf.capacity(), BytePageSize::Size8.capacity());
642 let ptr = buf.as_ptr();
643
644 buf.extend_from_slice(&[1; 1000]);
646 let head = buf.split_to(500);
647 drop(buf);
648 assert_eq!(cached_pages(BytePageSize::Size8), 0);
649 drop(head);
650 assert_eq!(cached_pages(BytePageSize::Size8), 1);
651
652 let buf = BytesMut::with_page_size(BytePageSize::Size8);
653 assert_eq!(buf.as_ptr(), ptr);
654 assert_eq!(buf.capacity(), BytePageSize::Size8.capacity());
655 assert_eq!(cached_pages(BytePageSize::Size8), 0);
656
657 let buf = BytesMut::with_page_size(BytePageSize::Unset);
659 assert_eq!(buf.page_size(), BytePageSize::Size64);
660 assert_eq!(buf.capacity(), BytePageSize::Size64.capacity());
661 drop(buf);
662 assert_eq!(cached_pages(BytePageSize::Size64), 1);
663
664 let buf = BytesMut::with_capacity(BytePageSize::Size64.capacity() + 1);
666 assert_eq!(buf.page_size(), BytePageSize::Unset);
667 assert_eq!(BytesMut::new().page_size(), BytePageSize::Unset);
668 assert_eq!(BytesMut::with_capacity(64).page_size(), BytePageSize::Unset);
669 assert_eq!(
670 BytesMut::copy_from_slice(b"hello").page_size(),
671 BytePageSize::Unset
672 );
673 drop(buf);
674 for size in crate::size::PAGE_SIZES {
675 let expected = usize::from(size == BytePageSize::Size64);
676 assert_eq!(cached_pages(size), expected);
677 }
678 }
679
680 #[test]
687 fn new_page_falls_back_to_smaller_size() {
688 reset();
689 let metrics = PageCacheMetrics::current();
690 let (s8, s16) = (BytePageSize::Size8, BytePageSize::Size16);
691
692 let st = StorageVec::sized(s8);
693 let addr = st.0;
694 drop(st);
695
696 let st = StorageVec::sized(s16);
697 assert_eq!(st.0, addr);
698 assert_eq!(st.page_size(), s8);
699 assert_eq!(cached_pages(s8), 0);
700 let snapshot = metrics.snapshot();
701 let s = *snapshot.get(s16).unwrap();
702 assert_eq!((s.hits, s.misses, s.fallbacks, s.pages), (0, 1, 1, 0));
703 let s = *snapshot.get(s8).unwrap();
704 assert_eq!(
705 (s.hits, s.misses, s.fallbacks, s.cached, s.pages),
706 (1, 1, 0, 1, 0)
707 );
708
709 let st2 = StorageVec::sized(s16);
710 assert_eq!(st2.page_size(), s16);
711 let snapshot = metrics.snapshot();
712 let s = *snapshot.get(s16).unwrap();
713 assert_eq!((s.hits, s.misses, s.fallbacks), (0, 2, 1));
714 let total = snapshot.total();
715 assert_eq!((total.hits, total.misses, total.fallbacks), (1, 3, 1));
716 drop((st, st2));
717 reset();
718 }
719
720 #[test]
726 fn new_page_falls_back_one_size_only() {
727 reset();
728 let (s4, s8, s16) = (
729 BytePageSize::Size4,
730 BytePageSize::Size8,
731 BytePageSize::Size16,
732 );
733
734 drop(StorageVec::sized_exact(s4));
735 let st = StorageVec::sized(s16);
736 assert_eq!(st.page_size(), s16);
737 assert_eq!(cached_pages(s4), 1);
738
739 let p8 = StorageVec::sized_exact(s8);
740 drop(st);
741 drop(p8);
742 let st = StorageVec::sized(s16);
743 assert_eq!(st.page_size(), s16);
744 assert_eq!((cached_pages(s8), cached_pages(s16)), (1, 0));
745 drop(st);
746 reset();
747 }
748
749 #[test]
757 fn new_buffers_fall_back_to_smaller_page() {
758 reset();
759 let metrics = PageCacheMetrics::current();
760 let (s8, s16) = (BytePageSize::Size8, BytePageSize::Size16);
761
762 drop(StorageVec::sized_exact(s8));
763 let buf = BytesMut::with_page_size(s16);
764 assert_eq!(buf.page_size(), s8);
765 assert_eq!(buf.capacity(), s8.capacity());
766 drop(buf);
767
768 drop(StorageVec::sized_exact(BytePageSize::Size48));
769 let buf = BytesMut::with_page_size(BytePageSize::Unset);
770 assert_eq!(buf.page_size(), BytePageSize::Size48);
771 drop(buf);
772
773 #[cfg(feature = "tracker")]
774 {
775 let tracker = BytesTracker::new();
776 let buf = BytesMut::with_page_size_in(s16, &tracker);
777 assert_eq!(buf.page_size(), s8);
778 assert_eq!(tracker.snapshot().live_bytes, s8.alloc_size());
779 }
780
781 assert_eq!(cached_pages(s8), 1);
782 let mut pages = BytePages::new(s16);
783 pages.put_slice(b"hello");
784 assert_eq!(cached_pages(s8), 0);
785 let expected = if cfg!(feature = "tracker") { 3 } else { 2 };
786 assert_eq!(metrics.snapshot().get(s16).unwrap().fallbacks, expected);
787 drop(pages);
788 reset();
789 }
790}