aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatthew Gumport <[email protected]>2024-06-21 15:18:33 -0700
committerEdward Wang <[email protected]>2024-06-28 12:34:25 -0700
commit38a9d556b557a10f7815f50fca6a49de146ab5be (patch)
tree21d45e0595cc9aaab2e717a00cd577f78cd5eddb
parenta483902c46fc43736fc5a27a5048b292493536b3 (diff)
downloadpingora-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--.bleep2
-rw-r--r--tinyufo/src/estimation.rs43
-rw-r--r--tinyufo/src/lib.rs52
3 files changed, 77 insertions, 20 deletions
diff --git a/.bleep b/.bleep
index 0db9482..cee92a7 100644
--- a/.bleep
+++ b/.bleep
@@ -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);