mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-23 21:20:05 +00:00
use a proper standard deviation to compare oracle + approx LRU
This commit is contained in:
parent
2750830694
commit
7bd9e0120f
2 changed files with 45 additions and 13 deletions
11
src/lru.rs
11
src/lru.rs
|
@ -10,19 +10,16 @@ use std::sync::Arc;
|
||||||
|
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
/// A very simple concurrent lru list, built using a doubly linked
|
/// A simple and approximate concurrent lru list.
|
||||||
/// list of Arcs.
|
|
||||||
///
|
|
||||||
/// The list uses a very simple locking scheme and will probably
|
|
||||||
/// suffer under high contention. This could certainly be improved.
|
|
||||||
///
|
///
|
||||||
/// We assume but do not verify that each node is only used with one
|
/// We assume but do not verify that each node is only used with one
|
||||||
/// list. If this is not the case, it is not *unsafe*, but panics and
|
/// list. If this is not the case, it is not *unsafe*, but panics and
|
||||||
/// weird results will ensue.
|
/// weird results will ensue.
|
||||||
///
|
///
|
||||||
/// Each "node" in the list is of type `Node` and must implement
|
/// Each "node" in the list is of type `Node` and must implement
|
||||||
/// `LruNode`, which is a trait that gives access to a field of type
|
/// `LruNode`, which is a trait that gives access to a field that
|
||||||
/// `LruLinks<Node>`, which stores the prev/next points.
|
/// stores the index in the list. This index gives us a rough idea of
|
||||||
|
/// how recently the node has been used.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Lru<Node>
|
pub(crate) struct Lru<Node>
|
||||||
where
|
where
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use linked_hash_map::LinkedHashMap;
|
use linked_hash_map::LinkedHashMap;
|
||||||
|
use rand::distributions::{Distribution, Normal};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct TestNode {
|
struct TestNode {
|
||||||
|
@ -33,7 +34,12 @@ const PICK_SEED: &str = "Wippity WIP";
|
||||||
/// reproducible. Returns (oracle_hits, lru_hits) -- i.e., the number
|
/// reproducible. Returns (oracle_hits, lru_hits) -- i.e., the number
|
||||||
/// of times that the oracle had something in cache vs the number of
|
/// of times that the oracle had something in cache vs the number of
|
||||||
/// times that our LRU did.
|
/// times that our LRU did.
|
||||||
fn compare(num_nodes: usize, capacity: usize, requests: usize) -> (usize, usize) {
|
fn compare(
|
||||||
|
standard_deviation: usize,
|
||||||
|
num_nodes: usize,
|
||||||
|
capacity: usize,
|
||||||
|
requests: usize,
|
||||||
|
) -> (usize, usize) {
|
||||||
// Remember the clock each time we access a given element.
|
// Remember the clock each time we access a given element.
|
||||||
let mut last_access: Vec<usize> = (0..num_nodes).map(|_| 0).collect();
|
let mut last_access: Vec<usize> = (0..num_nodes).map(|_| 0).collect();
|
||||||
|
|
||||||
|
@ -51,8 +57,10 @@ fn compare(num_nodes: usize, capacity: usize, requests: usize) -> (usize, usize)
|
||||||
let mut lru_hits = 0;
|
let mut lru_hits = 0;
|
||||||
|
|
||||||
let mut pick_rng = super::rng_with_seed(PICK_SEED);
|
let mut pick_rng = super::rng_with_seed(PICK_SEED);
|
||||||
|
let normal = Normal::new((num_nodes / 2) as f64, standard_deviation as f64);
|
||||||
for clock in (0..requests).map(|n| n + 1) {
|
for clock in (0..requests).map(|n| n + 1) {
|
||||||
let request_id: usize = pick_rng.gen_range(0, num_nodes);
|
let request_id = (normal.sample(&mut pick_rng) as usize).min(num_nodes - 1);
|
||||||
|
assert!(request_id < num_nodes);
|
||||||
|
|
||||||
last_access[request_id] = clock;
|
last_access[request_id] = clock;
|
||||||
|
|
||||||
|
@ -81,9 +89,36 @@ fn compare(num_nodes: usize, capacity: usize, requests: usize) -> (usize, usize)
|
||||||
(oracle_hits, lru_hits)
|
(oracle_hits, lru_hits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare performance of approximate LRU vs the perfect oracle in
|
||||||
|
// various scenarios -- different standard deviations and total size.
|
||||||
|
// Note that the `lru_hits` variable is just recording the current
|
||||||
|
// state and would be expected to change if you tweak the
|
||||||
|
// implementation (`oracle_hits` ought not to change).
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scenario_a() {
|
fn scenario_20_of_1000() {
|
||||||
let (oracle_hits, lru_hits) = compare(1000, 100, 10000);
|
let (oracle_hits, lru_hits) = compare(20, 1000, 100, 10000);
|
||||||
assert_eq!(oracle_hits, 993);
|
assert_eq!(oracle_hits, 9662);
|
||||||
assert_eq!(lru_hits, 973);
|
assert_eq!(lru_hits, 9428);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_200_of_1000() {
|
||||||
|
let (oracle_hits, lru_hits) = compare(200, 1000, 100, 10000);
|
||||||
|
assert_eq!(oracle_hits, 1496);
|
||||||
|
assert_eq!(lru_hits, 1488);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_500_of_1000() {
|
||||||
|
let (oracle_hits, lru_hits) = compare(500, 1000, 100, 10000);
|
||||||
|
assert_eq!(oracle_hits, 3835);
|
||||||
|
assert_eq!(lru_hits, 3839);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_2000_of_10000() {
|
||||||
|
let (oracle_hits, lru_hits) = compare(2000, 10000, 100, 10000);
|
||||||
|
assert_eq!(oracle_hits, 256);
|
||||||
|
assert_eq!(lru_hits, 229);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue