diff options
author | Matthew Gumport <[email protected]> | 2024-06-21 15:18:33 -0700 |
---|---|---|
committer | Edward Wang <[email protected]> | 2024-06-28 12:34:25 -0700 |
commit | 38a9d556b557a10f7815f50fca6a49de146ab5be (patch) | |
tree | 21d45e0595cc9aaab2e717a00cd577f78cd5eddb | |
parent | a483902c46fc43736fc5a27a5048b292493536b3 (diff) | |
download | pingora-38a9d556b557a10f7815f50fca6a49de146ab5be.tar.gz pingora-38a9d556b557a10f7815f50fca6a49de146ab5be.zip |
make seeded estimator and plumbing for tests
I started fidgeting with another one of the tests breaking because of the
estimator not being deterministic, and then decided to do this another way.
This adds `seeded` and `seeded_compact` constructors to the estimator which are
used in tests to override the LFU. These are hidden behind `cfg(test)`. The
`random_status` field of the UFO object is also overridden with the same seeds
as used for the seeded LFU.
These make the tests pass consistently without requiring further monkeying.
-rw-r--r-- | .bleep | 2 | ||||
-rw-r--r-- | tinyufo/src/estimation.rs | 43 | ||||
-rw-r--r-- | tinyufo/src/lib.rs | 52 |
3 files changed, 77 insertions, 20 deletions
@@ -1 +1 @@ -a3c90366d4cf12236bba71d5f95289e220efcdae
\ No newline at end of file +361d88592075f7f98f581b139d0349f1b70190a2
\ No newline at end of file diff --git a/tinyufo/src/estimation.rs b/tinyufo/src/estimation.rs index 6f165ac..11b9d92 100644 --- a/tinyufo/src/estimation.rs +++ b/tinyufo/src/estimation.rs @@ -36,23 +36,36 @@ impl Estimator { fn optimal(items: usize) -> Self { let (slots, hashes) = Self::optimal_paras(items); - Self::new(hashes, slots) + Self::new(hashes, slots, RandomState::new) } fn compact(items: usize) -> Self { let (slots, hashes) = Self::optimal_paras(items / 100); - Self::new(hashes, slots) + Self::new(hashes, slots, RandomState::new) } - /// Create a new `Estimator` with the given amount of hashes and columns (slots). - pub fn new(hashes: usize, slots: usize) -> Self { + #[cfg(test)] + fn seeded(items: usize) -> Self { + let (slots, hashes) = Self::optimal_paras(items); + Self::new(hashes, slots, || RandomState::with_seeds(2, 3, 4, 5)) + } + + #[cfg(test)] + fn seeded_compact(items: usize) -> Self { + let (slots, hashes) = Self::optimal_paras(items / 100); + Self::new(hashes, slots, || RandomState::with_seeds(2, 3, 4, 5)) + } + + /// Create a new `Estimator` with the given amount of hashes and columns (slots) using + /// the given random source. + pub fn new(hashes: usize, slots: usize, random: impl Fn() -> RandomState) -> Self { let mut estimator = Vec::with_capacity(hashes); for _ in 0..hashes { let mut slot = Vec::with_capacity(slots); for _ in 0..slots { slot.push(AtomicU8::new(0)); } - estimator.push((slot.into_boxed_slice(), RandomState::new())); + estimator.push((slot.into_boxed_slice(), random())); } Estimator { @@ -161,6 +174,26 @@ impl TinyLfu { window_limit: cache_size * 8, } } + + #[cfg(test)] + pub fn new_seeded(cache_size: usize) -> Self { + Self { + estimator: Estimator::seeded(cache_size), + window_counter: Default::default(), + // 8x: just a heuristic to balance the memory usage and accuracy + window_limit: cache_size * 8, + } + } + + #[cfg(test)] + pub fn new_compact_seeded(cache_size: usize) -> Self { + Self { + estimator: Estimator::seeded_compact(cache_size), + window_counter: Default::default(), + // 8x: just a heuristic to balance the memory usage and accuracy + window_limit: cache_size * 8, + } + } } #[cfg(test)] diff --git a/tinyufo/src/lib.rs b/tinyufo/src/lib.rs index 805373e..eb4cbaf 100644 --- a/tinyufo/src/lib.rs +++ b/tinyufo/src/lib.rs @@ -473,7 +473,9 @@ mod tests { #[test] fn test_evict_from_small() { - let cache = TinyUfo::new(5, 5); + let mut cache = TinyUfo::new(5, 5); + cache.random_status = RandomState::with_seeds(2, 3, 4, 5); + cache.queues.estimator = TinyLfu::new_seeded(5); cache.put(1, 1, 1); cache.put(2, 2, 2); @@ -496,7 +498,9 @@ mod tests { #[test] fn test_evict_from_small_to_main() { - let cache = TinyUfo::new(5, 5); + let mut cache = TinyUfo::new(5, 5); + cache.random_status = RandomState::with_seeds(2, 3, 4, 5); + cache.queues.estimator = TinyLfu::new_seeded(5); cache.put(1, 1, 1); cache.put(2, 2, 2); @@ -510,20 +514,30 @@ mod tests { assert_eq!(cache.peek_queue(2), Some(SMALL)); assert_eq!(cache.peek_queue(3), Some(SMALL)); - let evicted = cache.put(4, 4, 1); + let evicted = cache.put(4, 4, 2); assert_eq!(evicted.len(), 1); - assert_eq!(evicted[0].data, 2); + assert_eq!(evicted[0].weight, 2); assert_eq!(cache.peek_queue(1), Some(MAIN)); - // 2 is evicted because 1 is in main - assert_eq!(cache.peek_queue(2), None); - assert_eq!(cache.peek_queue(3), Some(SMALL)); - assert_eq!(cache.peek_queue(4), Some(SMALL)); + // either 2, 3, or 4 was evicted. Check evicted for which. + let mut remaining = vec![2, 3, 4]; + remaining.remove( + remaining + .iter() + .position(|x| *x == evicted[0].data) + .unwrap(), + ); + assert_eq!(cache.peek_queue(evicted[0].key), None); + for k in remaining { + assert_eq!(cache.peek_queue(k), Some(SMALL)); + } } #[test] fn test_evict_reentry() { - let cache = TinyUfo::new(5, 5); + let mut cache = TinyUfo::new(5, 5); + cache.random_status = RandomState::with_seeds(2, 3, 4, 5); + cache.queues.estimator = TinyLfu::new_seeded(5); cache.put(1, 1, 1); cache.put(2, 2, 2); @@ -555,7 +569,9 @@ mod tests { #[test] fn test_evict_entry_denied() { - let cache = TinyUfo::new(5, 5); + let mut cache = TinyUfo::new(5, 5); + cache.random_status = RandomState::with_seeds(2, 3, 4, 5); + cache.queues.estimator = TinyLfu::new_seeded(5); cache.put(1, 1, 1); cache.put(2, 2, 2); @@ -583,7 +599,9 @@ mod tests { #[test] fn test_force_put() { - let cache = TinyUfo::new(5, 5); + let mut cache = TinyUfo::new(5, 5); + cache.random_status = RandomState::with_seeds(2, 3, 4, 5); + cache.queues.estimator = TinyLfu::new_seeded(5); cache.put(1, 1, 1); cache.put(2, 2, 2); @@ -612,7 +630,9 @@ mod tests { #[test] fn test_evict_from_main() { - let cache = TinyUfo::new(5, 5); + let mut cache = TinyUfo::new(5, 5); + cache.random_status = RandomState::with_seeds(2, 3, 4, 5); + cache.queues.estimator = TinyLfu::new_seeded(5); cache.put(1, 1, 1); cache.put(2, 2, 2); @@ -649,7 +669,9 @@ mod tests { #[test] fn test_evict_from_small_compact() { - let cache = TinyUfo::new(5, 5); + let mut cache = TinyUfo::new(5, 5); + cache.random_status = RandomState::with_seeds(2, 3, 4, 5); + cache.queues.estimator = TinyLfu::new_compact_seeded(5); cache.put(1, 1, 1); cache.put(2, 2, 2); @@ -672,7 +694,9 @@ mod tests { #[test] fn test_evict_from_small_to_main_compact() { - let cache = TinyUfo::new(5, 5); + let mut cache = TinyUfo::new(5, 5); + cache.random_status = RandomState::with_seeds(2, 3, 4, 5); + cache.queues.estimator = TinyLfu::new_compact_seeded(5); cache.put(1, 1, 1); cache.put(2, 2, 2); |