fix the LRU algorithm to really be "least recently used"

This commit is contained in:
Simon Cruanes 2014-11-17 09:16:07 +01:00
parent fbc278907a
commit 62135fc9b7

View file

@ -152,13 +152,13 @@ module LRU(X:HASH) = struct
type 'a t = { type 'a t = {
table : 'a node H.t; (* hashtable key -> node *) table : 'a node H.t; (* hashtable key -> node *)
mutable first : 'a node option; mutable first : 'a node option;
mutable last : 'a node option;
size : int; (* max size *) size : int; (* max size *)
} }
and 'a node = { and 'a node = {
mutable key : key; mutable key : key;
mutable value : 'a; mutable value : 'a;
mutable next : 'a node option; mutable next : 'a node;
mutable prev : 'a node;
} (** Meta data for the value, making a chained list *) } (** Meta data for the value, making a chained list *)
let make size = let make size =
@ -166,71 +166,82 @@ module LRU(X:HASH) = struct
{ table = H.create size; { table = H.create size;
size; size;
first=None; first=None;
last=None;
} }
let clear c = let clear c =
H.clear c.table; H.clear c.table;
c.first <- None; c.first <- None;
c.last <- None;
() ()
let get c x = (H.find c.table x).value
let get_opt = function let get_opt = function
| None -> assert false | None -> assert false
| Some x -> x | Some x -> x
(* reverse the list *)
let rec reverse_ prev = function
| None -> prev
| Some n as node ->
let next = n.next in
n.next <- prev;
reverse_ node next
(* take first from queue *) (* take first from queue *)
let take_ c = let take_ c =
match c.first with match c.first with
| Some n when n.next == n ->
(* last element *)
c.first <- None;
n
| Some n -> | Some n ->
c.first <- n.next; c.first <- Some n.next;
n.prev.next <- n.next;
n.next.prev <- n.prev;
n n
| None -> | None ->
(* re-fill front list *) failwith "LRU: empty queue"
match reverse_ None c.last with
| None -> assert false
| Some n ->
c.first <- n.next;
n
(* push at back of queue *)
let push_ c n = let push_ c n =
n.next <- c.last; match c.first with
c.last <- Some n | None ->
n.next <- n;
n.prev <- n;
c.first <- Some n
| Some n1 when n1==n -> ()
| Some n1 ->
n.prev <- n1.prev;
n.next <- n1;
n1.prev.next <- n;
n1.prev <- n
(* remove from queue *)
let remove_ n =
n.prev.next <- n.next;
n.next.prev <- n.prev
(* Replace least recently used element of [c] by x->y *) (* Replace least recently used element of [c] by x->y *)
let replace_ c x y = let replace_ c x y =
(* remove old *) (* remove old *)
let n = take_ c in let n = take_ c in
H.remove c.table n.key; H.remove c.table n.key;
(* add x->y *) (* add x->y, at the back of the queue *)
H.add c.table x n;
n.key <- x; n.key <- x;
n.value <- y; n.value <- y;
(* push at back of queue *) H.add c.table x n;
push_ c n; push_ c n;
() ()
(* Insert x->y in the cache, increasing its entry count *) (* Insert x->y in the cache, increasing its entry count *)
let insert_ c x y = let insert_ c x y =
let n = { let rec n = {
key = x; key = x;
value = y; value = y;
next = c.last; next = n;
prev = n;
} in } in
H.add c.table x n; H.add c.table x n;
c.last <- Some n; push_ c n;
() ()
let get c x =
let n = H.find c.table x in
(* put n at the back of the queue *)
remove_ n;
push_ c n;
n.value
let set c x y = let set c x y =
let len = H.length c.table in let len = H.length c.table in
assert (len <= c.size); assert (len <= c.size);