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