mirror of
https://github.com/cloudflare/pingora.git
synced 2024-09-20 02:31:35 +02:00
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.
This commit is contained in:
parent
bb679da450
commit
f8f08c7669
3 changed files with 77 additions and 20 deletions
2
.bleep
2
.bleep
|
@ -1 +1 @@
|
||||||
a3c90366d4cf12236bba71d5f95289e220efcdae
|
361d88592075f7f98f581b139d0349f1b70190a2
|
|
@ -36,23 +36,36 @@ impl Estimator {
|
||||||
|
|
||||||
fn optimal(items: usize) -> Self {
|
fn optimal(items: usize) -> Self {
|
||||||
let (slots, hashes) = Self::optimal_paras(items);
|
let (slots, hashes) = Self::optimal_paras(items);
|
||||||
Self::new(hashes, slots)
|
Self::new(hashes, slots, RandomState::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compact(items: usize) -> Self {
|
fn compact(items: usize) -> Self {
|
||||||
let (slots, hashes) = Self::optimal_paras(items / 100);
|
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).
|
#[cfg(test)]
|
||||||
pub fn new(hashes: usize, slots: usize) -> Self {
|
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);
|
let mut estimator = Vec::with_capacity(hashes);
|
||||||
for _ in 0..hashes {
|
for _ in 0..hashes {
|
||||||
let mut slot = Vec::with_capacity(slots);
|
let mut slot = Vec::with_capacity(slots);
|
||||||
for _ in 0..slots {
|
for _ in 0..slots {
|
||||||
slot.push(AtomicU8::new(0));
|
slot.push(AtomicU8::new(0));
|
||||||
}
|
}
|
||||||
estimator.push((slot.into_boxed_slice(), RandomState::new()));
|
estimator.push((slot.into_boxed_slice(), random()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Estimator {
|
Estimator {
|
||||||
|
@ -161,6 +174,26 @@ impl TinyLfu {
|
||||||
window_limit: cache_size * 8,
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -473,7 +473,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_evict_from_small() {
|
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(1, 1, 1);
|
||||||
cache.put(2, 2, 2);
|
cache.put(2, 2, 2);
|
||||||
|
@ -496,7 +498,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_evict_from_small_to_main() {
|
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(1, 1, 1);
|
||||||
cache.put(2, 2, 2);
|
cache.put(2, 2, 2);
|
||||||
|
@ -510,20 +514,30 @@ mod tests {
|
||||||
assert_eq!(cache.peek_queue(2), Some(SMALL));
|
assert_eq!(cache.peek_queue(2), Some(SMALL));
|
||||||
assert_eq!(cache.peek_queue(3), 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.len(), 1);
|
||||||
assert_eq!(evicted[0].data, 2);
|
assert_eq!(evicted[0].weight, 2);
|
||||||
|
|
||||||
assert_eq!(cache.peek_queue(1), Some(MAIN));
|
assert_eq!(cache.peek_queue(1), Some(MAIN));
|
||||||
// 2 is evicted because 1 is in main
|
// either 2, 3, or 4 was evicted. Check evicted for which.
|
||||||
assert_eq!(cache.peek_queue(2), None);
|
let mut remaining = vec![2, 3, 4];
|
||||||
assert_eq!(cache.peek_queue(3), Some(SMALL));
|
remaining.remove(
|
||||||
assert_eq!(cache.peek_queue(4), Some(SMALL));
|
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]
|
#[test]
|
||||||
fn test_evict_reentry() {
|
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(1, 1, 1);
|
||||||
cache.put(2, 2, 2);
|
cache.put(2, 2, 2);
|
||||||
|
@ -555,7 +569,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_evict_entry_denied() {
|
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(1, 1, 1);
|
||||||
cache.put(2, 2, 2);
|
cache.put(2, 2, 2);
|
||||||
|
@ -583,7 +599,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_force_put() {
|
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(1, 1, 1);
|
||||||
cache.put(2, 2, 2);
|
cache.put(2, 2, 2);
|
||||||
|
@ -612,7 +630,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_evict_from_main() {
|
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(1, 1, 1);
|
||||||
cache.put(2, 2, 2);
|
cache.put(2, 2, 2);
|
||||||
|
@ -649,7 +669,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_evict_from_small_compact() {
|
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(1, 1, 1);
|
||||||
cache.put(2, 2, 2);
|
cache.put(2, 2, 2);
|
||||||
|
@ -672,7 +694,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_evict_from_small_to_main_compact() {
|
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(1, 1, 1);
|
||||||
cache.put(2, 2, 2);
|
cache.put(2, 2, 2);
|
||||||
|
|
Loading…
Reference in a new issue