mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 19:25:28 -05:00
LRU cache implemented
This commit is contained in:
parent
694b4929b0
commit
001e28ad5f
3 changed files with 135 additions and 14 deletions
104
cache.ml
104
cache.ml
|
|
@ -208,6 +208,8 @@ module Replacing(X : HASH) = struct
|
||||||
let clear c =
|
let clear c =
|
||||||
Array.fill c 0 (Array.length c) Empty
|
Array.fill c 0 (Array.length c) Empty
|
||||||
|
|
||||||
|
(** Try to find [f x] in the cache, otherwise compute it
|
||||||
|
and cache the result *)
|
||||||
let with_cache c f x =
|
let with_cache c f x =
|
||||||
let i = (X.hash x) mod (Array.length c) in
|
let i = (X.hash x) mod (Array.length c) in
|
||||||
match c.(i) with
|
match c.(i) with
|
||||||
|
|
@ -256,3 +258,105 @@ module Replacing2(X : HASH)(Y : HASH) = struct
|
||||||
c.(i) <- Assoc (x1, x2, y);
|
c.(i) <- Assoc (x1, x2, y);
|
||||||
y
|
y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
(** {2 Hashtables with Least Recently Used eviction policy *)
|
||||||
|
|
||||||
|
module LRU(X : HASH) = struct
|
||||||
|
type key = X.t
|
||||||
|
|
||||||
|
module H = Hashtbl.Make(X)
|
||||||
|
|
||||||
|
type 'a t = {
|
||||||
|
table : 'a node H.t; (* hashtable key -> node *)
|
||||||
|
first : 'a node; (* dummy node for the entry of the list *)
|
||||||
|
mutable len : int; (* number of entries *)
|
||||||
|
size : int; (* max size *)
|
||||||
|
}
|
||||||
|
and 'a node = {
|
||||||
|
mutable key : key;
|
||||||
|
mutable value : 'a;
|
||||||
|
mutable next : 'a node;
|
||||||
|
mutable prev : 'a node;
|
||||||
|
} (** Meta data for the value *)
|
||||||
|
|
||||||
|
let create size =
|
||||||
|
let rec first =
|
||||||
|
{ key = Obj.magic 0; value = Obj.magic 0; next=first; prev=first; }
|
||||||
|
in
|
||||||
|
{ table = H.create size;
|
||||||
|
len = 0;
|
||||||
|
size;
|
||||||
|
first;
|
||||||
|
}
|
||||||
|
|
||||||
|
(** Clear the content of the cache *)
|
||||||
|
let clear c =
|
||||||
|
c.len <- 0;
|
||||||
|
H.clear c.table;
|
||||||
|
c.first.next <- c.first;
|
||||||
|
c.first.prev <- c.first;
|
||||||
|
()
|
||||||
|
|
||||||
|
(** Find an element, or raise Not_found *)
|
||||||
|
let find c x =
|
||||||
|
let n = H.find c.table x in
|
||||||
|
assert (X.equal n.key x);
|
||||||
|
n.value
|
||||||
|
|
||||||
|
(** Replace least recently used element of [c] by x->y *)
|
||||||
|
let replace c x y =
|
||||||
|
let n = c.first.next in
|
||||||
|
(* remove old element *)
|
||||||
|
H.remove c.table n.key;
|
||||||
|
(* insertion in hashtable *)
|
||||||
|
H.add c.table x n;
|
||||||
|
(* re-use the node for x,y *)
|
||||||
|
n.key <- x;
|
||||||
|
n.value <- y;
|
||||||
|
(* remove from front of queue *)
|
||||||
|
n.next.prev <- c.first;
|
||||||
|
c.first.next <- n.next;
|
||||||
|
(* insert at back of queue *)
|
||||||
|
let last = c.first.prev in
|
||||||
|
last.next <- n;
|
||||||
|
c.first.prev <- n;
|
||||||
|
n.next <- c.first;
|
||||||
|
n.prev <- last;
|
||||||
|
()
|
||||||
|
|
||||||
|
(** Insert x->y in the cache, increasing its entry count *)
|
||||||
|
let insert c x y =
|
||||||
|
c.len <- c.len + 1;
|
||||||
|
let n = {
|
||||||
|
key = x;
|
||||||
|
value = y;
|
||||||
|
next = c.first;
|
||||||
|
prev = c.first.prev;
|
||||||
|
} in
|
||||||
|
(* insertion in hashtable *)
|
||||||
|
H.add c.table x n;
|
||||||
|
(* insertion at back of queue *)
|
||||||
|
c.first.prev.next <- n;
|
||||||
|
c.first.prev <- n;
|
||||||
|
()
|
||||||
|
|
||||||
|
(** Try to find [f x] in the cache, otherwise compute it
|
||||||
|
and cache the result *)
|
||||||
|
let with_cache c f x =
|
||||||
|
try
|
||||||
|
find c x
|
||||||
|
with Not_found ->
|
||||||
|
let y = f x in
|
||||||
|
(if c.len = c.size
|
||||||
|
then replace c x y
|
||||||
|
else insert c x y);
|
||||||
|
y
|
||||||
|
|
||||||
|
(** Partially apply the given function with a new cache of the
|
||||||
|
given size. It returns both the cache, and the specialized function *)
|
||||||
|
let with_cache_rec size f =
|
||||||
|
let cache = create size in
|
||||||
|
(* make a recursive version of [f] that uses the cache *)
|
||||||
|
let rec f' x = with_cache cache (fun x -> f f' x) x in
|
||||||
|
cache, f'
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -94,5 +94,9 @@ module Replacing(X : HASH) : S with type key = X.t
|
||||||
|
|
||||||
module Replacing2(X : HASH)(Y : HASH) : S2 with type key1 = X.t and type key2 = Y.t
|
module Replacing2(X : HASH)(Y : HASH) : S2 with type key1 = X.t and type key2 = Y.t
|
||||||
|
|
||||||
(* TODO LRU cache *)
|
(** {2 Hashtables with Least Recently Used eviction policy *)
|
||||||
|
|
||||||
|
module LRU(X : HASH) : S with type key = X.t
|
||||||
|
|
||||||
|
(* TODO LRU2 *)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,9 @@ module Fibo(C : Cache.S with type key = int) = struct
|
||||||
let fib ~size =
|
let fib ~size =
|
||||||
let rec fib fib' n =
|
let rec fib fib' n =
|
||||||
match n with
|
match n with
|
||||||
| 0 -> 1
|
| 0 -> 0
|
||||||
| 1 -> 1
|
| 1 -> 1
|
||||||
|
| 2 -> 1
|
||||||
| n ->
|
| n ->
|
||||||
fib' (n-1) + fib' (n-2)
|
fib' (n-1) + fib' (n-2)
|
||||||
in
|
in
|
||||||
|
|
@ -17,28 +18,40 @@ module Fibo(C : Cache.S with type key = int) = struct
|
||||||
cached_fib
|
cached_fib
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module LinearIntCache = Cache.Linear(struct
|
||||||
|
type t = int
|
||||||
|
let equal i j = i = j
|
||||||
|
end)
|
||||||
|
|
||||||
|
module ReplacingIntCache = Cache.Replacing(struct
|
||||||
|
type t = int
|
||||||
|
let equal i j = i = j
|
||||||
|
let hash i = i
|
||||||
|
end)
|
||||||
|
|
||||||
|
module LRUIntCache = Cache.LRU(struct
|
||||||
|
type t = int
|
||||||
|
let equal i j = i = j
|
||||||
|
let hash i = i
|
||||||
|
end)
|
||||||
|
|
||||||
|
module DummyIntCache = Cache.Dummy(struct type t = int end)
|
||||||
|
|
||||||
let _ =
|
let _ =
|
||||||
let module LinearIntCache = Cache.Linear(struct
|
|
||||||
type t = int
|
|
||||||
let equal i j = i = j
|
|
||||||
end) in
|
|
||||||
let module ReplacingIntCache = Cache.Replacing(struct
|
|
||||||
type t = int
|
|
||||||
let equal i j = i = j
|
|
||||||
let hash i = i
|
|
||||||
end) in
|
|
||||||
let module DummyIntCache = Cache.Dummy(struct type t = int end) in
|
|
||||||
(* Fibonacci for those caching implementations *)
|
(* Fibonacci for those caching implementations *)
|
||||||
let module LinearFibo = Fibo(LinearIntCache) in
|
let module LinearFibo = Fibo(LinearIntCache) in
|
||||||
let module ReplacingFibo = Fibo(ReplacingIntCache) in
|
let module ReplacingFibo = Fibo(ReplacingIntCache) in
|
||||||
|
let module LRUFibo= Fibo(LRUIntCache) in
|
||||||
let module DummyFibo = Fibo(DummyIntCache) in
|
let module DummyFibo = Fibo(DummyIntCache) in
|
||||||
(* benchmark caches with fibo function *)
|
(* benchmark caches with fibo function *)
|
||||||
let bench_fib fib () =
|
let bench_fib fib () =
|
||||||
ignore (List.map fib [5;10;20;30;35])
|
ignore (List.map fib [5;10;20;30;35]);
|
||||||
|
()
|
||||||
in
|
in
|
||||||
Bench.bench
|
Bench.bench
|
||||||
[ "linear_fib", bench_fib (LinearFibo.fib ~size:5);
|
[ "linear_fib", bench_fib (LinearFibo.fib ~size:5);
|
||||||
"replacing_fib", bench_fib (ReplacingFibo.fib ~size:128);
|
"replacing_fib", bench_fib (ReplacingFibo.fib ~size:256);
|
||||||
|
"LRU_fib", bench_fib (LRUFibo.fib ~size:256);
|
||||||
"dummy_fib", bench_fib (DummyFibo.fib ~size:5);
|
"dummy_fib", bench_fib (DummyFibo.fib ~size:5);
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue