big changes in the Cache module, with clean signatures and several

functors that implement distinct policies
This commit is contained in:
Simon Cruanes 2013-03-06 12:28:09 +01:00
parent 56d8de46a3
commit 22e948e374
3 changed files with 222 additions and 69 deletions

214
cache.ml
View file

@ -23,66 +23,184 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*) *)
(** an imperative cache for memoization of pairs *) (** {1 Memoization caches} *)
module type S = module type EQ = sig
sig type t
type key val equal : t -> t -> bool
end
type 'a t module type HASH = sig
include EQ
val hash : t -> int
end
(** create a cache with given size *) (** Signature of a cache for values *)
val create : int -> (key -> key -> 'a) -> 'a t module type S = sig
type 'a t
type key
(** find a value in the cache *) val create : int -> 'a t
val lookup : 'a t -> key -> key -> 'a (** Create a new cache of the given size. *)
(** clear the cache from its content *) val clear : 'a t -> unit
val clear : 'a t -> unit (** Clear content of the cache *)
end
module type CachedType = val with_cache : 'a t -> (key -> 'a) -> key -> 'a
sig (** Wrap the function with the cache *)
type t end
val hash : t -> int
val equal : t -> t -> bool (** Signature of a cache for pairs of values *)
end module type S2 = sig
type 'a t
type key
val create : int -> 'a t
(** Create a new cache of the given size. *)
val clear : 'a t -> unit
(** Clear content of the cache *)
val with_cache : 'a t -> (key -> key -> 'a) -> key -> key -> 'a
(** Wrap the function with the cache *)
end
module Make(HType : CachedType) = (** {2 Small linear cache} *)
struct
type key = HType.t
(** A slot of the array contains a (key, value, true) (** This cache stores (key,value) pairs in an array, that is traversed
if key->value is stored there (at index hash(key) % length), linearily. It is therefore only reasonable for small sizes (like 5). *)
(null, null, false) otherwise.
The first slot in the array contains the function module Linear(X : EQ) = struct
used to produce the value upon a cache miss. *) type 'a t = 'a bucket array
type 'a t = (key * key * 'a * bool) array and 'a bucket = Empty | Pair of key * 'a
and key = X.t
let my_null = (Obj.magic None, Obj.magic None, Obj.magic None, false) let create size =
assert (size >= 1);
Array.create size Empty
let set_fun c f = c.(0) <- Obj.magic f let clear cache =
Array.fill cache 0 (Array.length cache) Empty
let create size f = (** Insert the binding (x -> y) into the cache *)
let c = Array.create (size+1) my_null in let insert cache x y =
c.(0) <- Obj.magic f; let n = Array.length cache in
c (* shift other values toward the end *)
Array.blit cache 0 cache 1 (n-1);
cache.(0) <- Pair(x,y)
let lookup c k1 k2 = (** Try to find [f x] in the cache, otherwise compute it
let i = (((HType.hash k1 + 17) lxor HType.hash k2) mod (Array.length c -1)) + 1 in and cache the result *)
match c.(i) with let with_cache cache f x =
| (_, _, _, false) -> let n = Array.length cache in
let v = ((Obj.magic c.(0)) : key -> key -> 'a) k1 k2 in let rec search i =
c.(i) <- (k1, k2, v, true); v (* function that performs the lookup *)
| (k1', k2', _, true) when not (HType.equal k1 k1') || not (HType.equal k2 k2')-> if i = n then begin
let v = ((Obj.magic c.(0)) : key -> key -> 'a) k1 k2 in (* cache miss *)
c.(i) <- (k1, k2, v, true); v let y = f x in
| (_, _, v, true) -> v insert cache x y;
y
end else match cache.(i) with
| Pair (x',y) when X.equal x x' -> y
| Empty | Pair _ -> search (i+1)
in
search 0
end
let clear c = module Linear2(X : EQ) = struct
let f = c.(0) in type 'a t = 'a bucket array
Array.iteri (fun i _ -> c.(i) <- my_null) c; and 'a bucket = Empty | Assoc of key * key * 'a
c.(0) <- f and key = X.t
end
let create size =
assert (size >= 1);
Array.create size Empty
let clear cache =
Array.fill cache 0 (Array.length cache) Empty
(** Insert the binding (x -> y) into the cache *)
let insert cache x1 x2 y =
let n = Array.length cache in
(* shift other values toward the end *)
Array.blit cache 0 cache 1 (n-1);
cache.(0) <- Assoc(x1,x2,y)
(** Try to find [f x] in the cache, otherwise compute it
and cache the result *)
let with_cache cache f x1 x2 =
let n = Array.length cache in
let rec search i =
(* function that performs the lookup *)
if i = n then begin
(* cache miss *)
let y = f x1 x2 in
insert cache x1 x2 y;
y
end else match cache.(i) with
| Assoc (x1',x2',y) when X.equal x1 x1' && X.equal x2 x2' -> y
| Empty | Assoc _ -> search (i+1)
in
search 0
end
(** {2 An imperative cache of fixed size for memoization of pairs} *)
module Replacing(X : HASH) = struct
type key = X.t
(** A slot of the array contains a (key, value, true)
if key->value is stored there (at index hash(key) % length),
(null, null, false) otherwise.
The first slot in the array contains the function
used to produce the value upon a cache miss. *)
type 'a t = 'a bucket array
and 'a bucket = Empty | Assoc of key * 'a
let create size =
Array.create size Empty
let clear c =
Array.fill c 0 (Array.length c) Empty
let with_cache c f x =
let i = (X.hash x) mod (Array.length c) in
match c.(i) with
| Assoc (x', y) when X.equal x x' ->
y (* cache hit *)
| Assoc _ | Empty -> (* cache miss *)
let y = f x in
c.(i) <- Assoc (x, y);
y
end
module Replacing2(X : HASH) = struct
type key = X.t
(** A slot of the array contains a (key, value, true)
if key->value is stored there (at index hash(key) % length),
(null, null, false) otherwise.
The first slot in the array contains the function
used to produce the value upon a cache miss. *)
type 'a t = 'a bucket array
and 'a bucket = Empty | Assoc of key * key * 'a
let create size =
Array.create size Empty
let clear c =
Array.fill c 0 (Array.length c) Empty
let with_cache c f x1 x2 =
let i = (((X.hash x1 + 17) lxor X.hash x2) mod Array.length c) in
match c.(i) with
| Assoc (x1', x2', y) when X.equal x1 x1' && X.equal x2 x2' ->
y (* cache hit *)
| Assoc _ | Empty -> (* cache miss *)
let y = f x1 x2 in
c.(i) <- Assoc (x1, x2, y);
y
end

View file

@ -23,30 +23,64 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*) *)
(** An imperative cache of fixed size for memoization of pairs *) (** {1 Memoization caches} *)
module type S = (** {2 Signatures} *)
sig
type key
type 'a t module type EQ = sig
type t
val equal : t -> t -> bool
end
(** create a cache with given size *) module type HASH = sig
val create : int -> (key -> key -> 'a) -> 'a t include EQ
val hash : t -> int
end
(** find a value in the cache *) (** Signature of a cache for values *)
val lookup : 'a t -> key -> key -> 'a module type S = sig
type 'a t
type key
(** clear the cache from its content *) val create : int -> 'a t
val clear : 'a t -> unit (** Create a new cache of the given size. *)
end
module type CachedType = val clear : 'a t -> unit
sig (** Clear content of the cache *)
type t
val hash : t -> int val with_cache : 'a t -> (key -> 'a) -> key -> 'a
val equal : t -> t -> bool (** Wrap the function with the cache *)
end end
(** Signature of a cache for pairs of values *)
module type S2 = sig
type 'a t
type key
val create : int -> 'a t
(** Create a new cache of the given size. *)
val clear : 'a t -> unit
(** Clear content of the cache *)
val with_cache : 'a t -> (key -> key -> 'a) -> key -> key -> 'a
(** Wrap the function with the cache *)
end
(** {2 Small linear cache} *)
(** This cache stores (key,value) pairs in an array, that is traversed
linearily. It is therefore only reasonable for small sizes (like 5). *)
module Linear(X : EQ) : S with type key = X.t
module Linear2(X : EQ) : S2 with type key = X.t
(** {2 Hashtables that resolve collisions by replacing} *)
module Replacing(X : HASH) : S with type key = X.t
module Replacing2(X : HASH) : S2 with type key = X.t
(* TODO LRU cache *)
(** functorial implementation *)
module Make(CType : CachedType) : S with type key = CType.t

View file

@ -8,6 +8,7 @@ let suite =
Test_heap.suite; Test_heap.suite;
Test_graph.suite; Test_graph.suite;
Test_univ.suite; Test_univ.suite;
Test_cache.suite;
] ]
let _ = let _ =