mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
Cache.S2 can cache pair of heterogeneous arguments;
Cache.Dummy and Cache.Dummy2 (no caching); benchmarking Cache
This commit is contained in:
parent
22e948e374
commit
694b4929b0
3 changed files with 127 additions and 20 deletions
84
cache.ml
84
cache.ml
|
|
@ -48,12 +48,18 @@ module type S = sig
|
||||||
|
|
||||||
val with_cache : 'a t -> (key -> 'a) -> key -> 'a
|
val with_cache : 'a t -> (key -> 'a) -> key -> 'a
|
||||||
(** Wrap the function with the cache *)
|
(** Wrap the function with the cache *)
|
||||||
|
|
||||||
|
val with_cache_rec : int -> ((key -> 'a) -> key -> 'a) -> ('a t * (key -> 'a))
|
||||||
|
(** Partially apply the given function with a cached version of itself.
|
||||||
|
The cache has as size the first (int) argument.
|
||||||
|
It returns both the cache, and the specialized function *)
|
||||||
end
|
end
|
||||||
|
|
||||||
(** Signature of a cache for pairs of values *)
|
(** Signature of a cache for pairs of values *)
|
||||||
module type S2 = sig
|
module type S2 = sig
|
||||||
type 'a t
|
type 'a t
|
||||||
type key
|
type key1
|
||||||
|
type key2
|
||||||
|
|
||||||
val create : int -> 'a t
|
val create : int -> 'a t
|
||||||
(** Create a new cache of the given size. *)
|
(** Create a new cache of the given size. *)
|
||||||
|
|
@ -61,10 +67,38 @@ module type S2 = sig
|
||||||
val clear : 'a t -> unit
|
val clear : 'a t -> unit
|
||||||
(** Clear content of the cache *)
|
(** Clear content of the cache *)
|
||||||
|
|
||||||
val with_cache : 'a t -> (key -> key -> 'a) -> key -> key -> 'a
|
val with_cache : 'a t -> (key1 -> key2 -> 'a) -> key1 -> key2 -> 'a
|
||||||
(** Wrap the function with the cache *)
|
(** Wrap the function with the cache *)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
(** {2 Dummy cache (no caching) *)
|
||||||
|
|
||||||
|
module Dummy(X : sig type t end) = struct
|
||||||
|
type 'a t = unit
|
||||||
|
and key = X.t
|
||||||
|
|
||||||
|
let create size = ()
|
||||||
|
|
||||||
|
let clear () = ()
|
||||||
|
|
||||||
|
let with_cache () f x = f x
|
||||||
|
|
||||||
|
let with_cache_rec size f =
|
||||||
|
let rec f' x = f f' x in
|
||||||
|
(), f'
|
||||||
|
end
|
||||||
|
|
||||||
|
module Dummy2(X : sig type t end)(Y : sig type t end) = struct
|
||||||
|
type 'a t = unit
|
||||||
|
and key1 = X.t
|
||||||
|
and key2 = Y.t
|
||||||
|
|
||||||
|
let create size = ()
|
||||||
|
|
||||||
|
let clear () = ()
|
||||||
|
|
||||||
|
let with_cache () f x1 x2 = f x1 x2
|
||||||
|
end
|
||||||
|
|
||||||
(** {2 Small linear cache} *)
|
(** {2 Small linear cache} *)
|
||||||
|
|
||||||
|
|
@ -97,21 +131,30 @@ module Linear(X : EQ) = struct
|
||||||
let rec search i =
|
let rec search i =
|
||||||
(* function that performs the lookup *)
|
(* function that performs the lookup *)
|
||||||
if i = n then begin
|
if i = n then begin
|
||||||
(* cache miss *)
|
(* cache miss *)
|
||||||
let y = f x in
|
let y = f x in
|
||||||
insert cache x y;
|
insert cache x y;
|
||||||
y
|
y
|
||||||
end else match cache.(i) with
|
end else match cache.(i) with
|
||||||
| Pair (x',y) when X.equal x x' -> y
|
| Pair (x',y) when X.equal x x' -> y
|
||||||
| Empty | Pair _ -> search (i+1)
|
| Empty | Pair _ -> search (i+1)
|
||||||
in
|
in
|
||||||
search 0
|
search 0
|
||||||
|
|
||||||
|
(** 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
|
end
|
||||||
|
|
||||||
module Linear2(X : EQ) = struct
|
module Linear2(X : EQ)(Y : EQ) = struct
|
||||||
type 'a t = 'a bucket array
|
type 'a t = 'a bucket array
|
||||||
and 'a bucket = Empty | Assoc of key * key * 'a
|
and 'a bucket = Empty | Assoc of key1 * key2 * 'a
|
||||||
and key = X.t
|
and key1 = X.t
|
||||||
|
and key2 = Y.t
|
||||||
|
|
||||||
let create size =
|
let create size =
|
||||||
assert (size >= 1);
|
assert (size >= 1);
|
||||||
|
|
@ -139,7 +182,7 @@ module Linear2(X : EQ) = struct
|
||||||
insert cache x1 x2 y;
|
insert cache x1 x2 y;
|
||||||
y
|
y
|
||||||
end else match cache.(i) with
|
end else match cache.(i) with
|
||||||
| Assoc (x1',x2',y) when X.equal x1 x1' && X.equal x2 x2' -> y
|
| Assoc (x1',x2',y) when X.equal x1 x1' && Y.equal x2 x2' -> y
|
||||||
| Empty | Assoc _ -> search (i+1)
|
| Empty | Assoc _ -> search (i+1)
|
||||||
in
|
in
|
||||||
search 0
|
search 0
|
||||||
|
|
@ -174,11 +217,17 @@ module Replacing(X : HASH) = struct
|
||||||
let y = f x in
|
let y = f x in
|
||||||
c.(i) <- Assoc (x, y);
|
c.(i) <- Assoc (x, y);
|
||||||
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
|
end
|
||||||
|
|
||||||
module Replacing2(X : HASH) = struct
|
module Replacing2(X : HASH)(Y : HASH) = struct
|
||||||
type key = X.t
|
|
||||||
|
|
||||||
(** A slot of the array contains a (key, value, true)
|
(** A slot of the array contains a (key, value, true)
|
||||||
if key->value is stored there (at index hash(key) % length),
|
if key->value is stored there (at index hash(key) % length),
|
||||||
(null, null, false) otherwise.
|
(null, null, false) otherwise.
|
||||||
|
|
@ -186,7 +235,10 @@ module Replacing2(X : HASH) = struct
|
||||||
The first slot in the array contains the function
|
The first slot in the array contains the function
|
||||||
used to produce the value upon a cache miss. *)
|
used to produce the value upon a cache miss. *)
|
||||||
type 'a t = 'a bucket array
|
type 'a t = 'a bucket array
|
||||||
and 'a bucket = Empty | Assoc of key * key * 'a
|
and 'a bucket = Empty | Assoc of key1 * key2 * 'a
|
||||||
|
and key1 = X.t
|
||||||
|
and key2 = Y.t
|
||||||
|
|
||||||
|
|
||||||
let create size =
|
let create size =
|
||||||
Array.create size Empty
|
Array.create size Empty
|
||||||
|
|
@ -195,9 +247,9 @@ module Replacing2(X : HASH) = struct
|
||||||
Array.fill c 0 (Array.length c) Empty
|
Array.fill c 0 (Array.length c) Empty
|
||||||
|
|
||||||
let with_cache c f x1 x2 =
|
let with_cache c f x1 x2 =
|
||||||
let i = (((X.hash x1 + 17) lxor X.hash x2) mod Array.length c) in
|
let i = (((X.hash x1 + 17) lxor Y.hash x2) mod Array.length c) in
|
||||||
match c.(i) with
|
match c.(i) with
|
||||||
| Assoc (x1', x2', y) when X.equal x1 x1' && X.equal x2 x2' ->
|
| Assoc (x1', x2', y) when X.equal x1 x1' && Y.equal x2 x2' ->
|
||||||
y (* cache hit *)
|
y (* cache hit *)
|
||||||
| Assoc _ | Empty -> (* cache miss *)
|
| Assoc _ | Empty -> (* cache miss *)
|
||||||
let y = f x1 x2 in
|
let y = f x1 x2 in
|
||||||
|
|
|
||||||
20
cache.mli
20
cache.mli
|
|
@ -50,12 +50,18 @@ module type S = sig
|
||||||
|
|
||||||
val with_cache : 'a t -> (key -> 'a) -> key -> 'a
|
val with_cache : 'a t -> (key -> 'a) -> key -> 'a
|
||||||
(** Wrap the function with the cache *)
|
(** Wrap the function with the cache *)
|
||||||
|
|
||||||
|
val with_cache_rec : int -> ((key -> 'a) -> key -> 'a) -> ('a t * (key -> 'a))
|
||||||
|
(** Partially apply the given function with a cached version of itself.
|
||||||
|
The cache has as size the first (int) argument.
|
||||||
|
It returns both the cache, and the specialized function *)
|
||||||
end
|
end
|
||||||
|
|
||||||
(** Signature of a cache for pairs of values *)
|
(** Signature of a cache for pairs of values *)
|
||||||
module type S2 = sig
|
module type S2 = sig
|
||||||
type 'a t
|
type 'a t
|
||||||
type key
|
type key1
|
||||||
|
type key2
|
||||||
|
|
||||||
val create : int -> 'a t
|
val create : int -> 'a t
|
||||||
(** Create a new cache of the given size. *)
|
(** Create a new cache of the given size. *)
|
||||||
|
|
@ -63,10 +69,16 @@ module type S2 = sig
|
||||||
val clear : 'a t -> unit
|
val clear : 'a t -> unit
|
||||||
(** Clear content of the cache *)
|
(** Clear content of the cache *)
|
||||||
|
|
||||||
val with_cache : 'a t -> (key -> key -> 'a) -> key -> key -> 'a
|
val with_cache : 'a t -> (key1 -> key2 -> 'a) -> key1 -> key2 -> 'a
|
||||||
(** Wrap the function with the cache *)
|
(** Wrap the function with the cache *)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
(** {2 Dummy cache (no caching) *)
|
||||||
|
|
||||||
|
module Dummy(X : sig type t end) : S with type key = X.t
|
||||||
|
|
||||||
|
module Dummy2(X : sig type t end)(Y : sig type t end) : S2 with type key1 = X.t and type key2 = Y.t
|
||||||
|
|
||||||
(** {2 Small linear cache} *)
|
(** {2 Small linear cache} *)
|
||||||
|
|
||||||
(** This cache stores (key,value) pairs in an array, that is traversed
|
(** This cache stores (key,value) pairs in an array, that is traversed
|
||||||
|
|
@ -74,13 +86,13 @@ end
|
||||||
|
|
||||||
module Linear(X : EQ) : S with type key = X.t
|
module Linear(X : EQ) : S with type key = X.t
|
||||||
|
|
||||||
module Linear2(X : EQ) : S2 with type key = X.t
|
module Linear2(X : EQ)(Y : EQ) : S2 with type key1 = X.t and type key2 = Y.t
|
||||||
|
|
||||||
(** {2 Hashtables that resolve collisions by replacing} *)
|
(** {2 Hashtables that resolve collisions by replacing} *)
|
||||||
|
|
||||||
module Replacing(X : HASH) : S with type key = X.t
|
module Replacing(X : HASH) : S with type key = X.t
|
||||||
|
|
||||||
module Replacing2(X : HASH) : S2 with type key = X.t
|
module Replacing2(X : HASH)(Y : HASH) : S2 with type key1 = X.t and type key2 = Y.t
|
||||||
|
|
||||||
(* TODO LRU cache *)
|
(* TODO LRU cache *)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,49 @@
|
||||||
|
|
||||||
(** Benchmarking *)
|
(** Benchmarking *)
|
||||||
|
|
||||||
|
(** {2 Cache} *)
|
||||||
|
|
||||||
|
(** Cached fibonacci function *)
|
||||||
|
module Fibo(C : Cache.S with type key = int) = struct
|
||||||
|
let fib ~size =
|
||||||
|
let rec fib fib' n =
|
||||||
|
match n with
|
||||||
|
| 0 -> 1
|
||||||
|
| 1 -> 1
|
||||||
|
| n ->
|
||||||
|
fib' (n-1) + fib' (n-2)
|
||||||
|
in
|
||||||
|
let _cache, cached_fib = C.with_cache_rec size fib in
|
||||||
|
cached_fib
|
||||||
|
end
|
||||||
|
|
||||||
|
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 *)
|
||||||
|
let module LinearFibo = Fibo(LinearIntCache) in
|
||||||
|
let module ReplacingFibo = Fibo(ReplacingIntCache) in
|
||||||
|
let module DummyFibo = Fibo(DummyIntCache) in
|
||||||
|
(* benchmark caches with fibo function *)
|
||||||
|
let bench_fib fib () =
|
||||||
|
ignore (List.map fib [5;10;20;30;35])
|
||||||
|
in
|
||||||
|
Bench.bench
|
||||||
|
[ "linear_fib", bench_fib (LinearFibo.fib ~size:5);
|
||||||
|
"replacing_fib", bench_fib (ReplacingFibo.fib ~size:128);
|
||||||
|
"dummy_fib", bench_fib (DummyFibo.fib ~size:5);
|
||||||
|
]
|
||||||
|
|
||||||
|
(** {2 PHashtbl} *)
|
||||||
|
|
||||||
module IHashtbl = Hashtbl.Make(struct
|
module IHashtbl = Hashtbl.Make(struct
|
||||||
type t = int
|
type t = int
|
||||||
let equal i j = i - j = 0
|
let equal i j = i - j = 0
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue