-
David Bühler authored
The new parameter is -eva-cache-size.
David Bühler authoredThe new parameter is -eva-cache-size.
binary_cache.ml 12.40 KiB
(**************************************************************************)
(* *)
(* This file is part of Frama-C. *)
(* *)
(* Copyright (C) 2007-2025 *)
(* CEA (Commissariat à l'énergie atomique et aux énergies *)
(* alternatives) *)
(* *)
(* you can redistribute it and/or modify it under the terms of the GNU *)
(* Lesser General Public License as published by the Free Software *)
(* Foundation, version 2.1. *)
(* *)
(* It is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
(* GNU Lesser General Public License for more details. *)
(* *)
(* See the GNU Lesser General Public License version 2.1 *)
(* for more details (enclosed in the file licenses/LGPLv2.1). *)
(* *)
(**************************************************************************)
let compute_cache_size i = 1 lsl (8 + i)
let default_value = 2
(* Should not be used to create cache before the value from -eva-cache-size
has been set by Eva. *)
let cache_size = ref (compute_cache_size default_value)
(* Set by Eva, according to the -eva-cache-size parameter. *)
let set_cache_size i = cache_size := compute_cache_size i
(** The caches of this module are lazy, for two reasons:
- some caches are never used, because the function that created them is
never called. This typically happens for functors implementing generic
datastructures, where not all functions are used in every module
(but every function with a static cache creates its cache nevertheless)
- Caches must be cleared as soon as some states change, in order to remain
coherent (for example, when the current project changes). When setting
multiple command-line options, the caches may be cleared after each option.
When caches are big, this becomes very time-consuming. To avoid this,
the functions [clear] do nothing when the caches have not been forced yet.
(This is not perfect: once a lazy cache has been forced, each 'clear'
operation becomes costly again.)
*)
module type Cacheable =
sig
type t
val hash : t -> int
val sentinel : t
val equal : t -> t -> bool
end
module type Result =
sig
type t
val sentinel : t
end
(** The Array_k modules (k = 2, 3, 4) below provide a small interface to arrays
of k-tuples. However, for performance reasons, they are implemented as
k-tuples of arrays of the same size. (The difference can be up to 10% on
some benchmarks.)
Note that there used to be an even faster implementation (about 2% on the
same benchmarks) as a flattened array of k times the size. It relied on a
precise understanding of the low-level OCaml memory model (and the Obj
module). However, that made some maintainers worry on the rare occasions
they had to look at this file.
*)
module Array = struct
include Stdlib.Array
let clear : 'a t -> 'a -> unit
= fun t a -> fill t 0 (length t) a
end
module Array_2 =
struct
type ('a, 'b) t = 'a array * 'b array
let (clear : ('a, 'b) t -> 'a -> 'b -> unit)
= fun (ta, tb) a b -> Array.(clear ta a; clear tb b)
let (make : int -> 'a -> 'b -> ('a, 'b) t)
= fun size a b -> Array.(make size a, make size b)
let (set : ('a, 'b) t -> int -> 'a -> 'b -> unit)
= fun (ta, tb) i a b -> Array.(set ta i a; set tb i b)
let (get0 : ('a, 'b) t -> int -> 'a)
= fun (ta, _) i -> Array.get ta i
let (get1 : ('a, 'b) t -> int -> 'b)
= fun (_, tb) i -> Array.get tb i
end
module Array_3 =
struct
type ('a, 'b, 'c) t = 'a array * 'b array * 'c array
let (clear : ('a, 'b, 'c) t -> 'a -> 'b -> 'c -> unit)
= fun (ta, tb, tc) a b c -> Array.(clear ta a; clear tb b; clear tc c)
let (make : int -> 'a -> 'b -> 'c -> ('a, 'b, 'c) t)
= fun size a b c -> Array.(make size a, make size b, make size c)
let (set : ('a, 'b, 'c) t -> int -> 'a -> 'b -> 'c -> unit)
= fun (ta, tb, tc) i a b c -> Array.(set ta i a; set tb i b; set tc i c)
let (get0 : ('a, 'b, 'c) t -> int -> 'a)
= fun (ta, _, _) i -> Array.get ta i
let (get1 : ('a, 'b, 'c) t -> int -> 'b)
= fun (_, tb, _) i -> Array.get tb i
let (get2 : ('a, 'b, 'c) t -> int -> 'c)
= fun (_, _, tc) i -> Array.get tc i
end
module Array_4 =
struct
type ('a, 'b, 'c, 'd) t = 'a array * 'b array * 'c array * 'd array
let (clear : ('a , 'b , 'c , 'd) t -> 'a -> 'b -> 'c -> 'd -> unit)
= fun (ta, tb, tc, td) a b c d ->
Array.(clear ta a; clear tb b; clear tc c; clear td d)
let (make : int -> 'a -> 'b -> 'c -> 'd -> ('a , 'b , 'c , 'd) t)
= fun size a b c d ->
Array.(make size a, make size b, make size c, make size d)
let (set : ('a, 'b, 'c, 'd) t -> int -> 'a -> 'b -> 'c -> 'd -> unit)
= fun (ta, tb, tc, td) i a b c d ->
Array.(set ta i a; set tb i b; set tc i c; set td i d)
let (get0 : ('a, 'b, 'c, 'd) t -> int -> 'a)
= fun (ta, _, _, _) i -> Array.get ta i
let (get1 : ('a, 'b, 'c, 'd) t -> int -> 'b)
= fun (_, tb, _, _) i -> Array.get tb i
let (get2 : ('a, 'b, 'c, 'd) t -> int -> 'c)
= fun (_, _, tc, _) i -> Array.get tc i
let (get3 : ('a, 'b, 'c, 'd) t -> int -> 'd)
= fun (_, _, _, td) i -> Array.get td i
end
module Symmetric_Binary (H: Cacheable) (R: Result) =
struct
let create () =
let size = !cache_size in
let array = Array_3.make size H.sentinel H.sentinel R.sentinel in
array, pred size
let cache = lazy (create ())
let clear () =
if Lazy.is_val cache then
let array, _ = Lazy.force cache in
Array_3.clear array H.sentinel H.sentinel R.sentinel
let hash = H.hash
let merge f a0 a1 =
let array, mask = Lazy.force cache in
let a0', a1', h0, h1 =
let h0 = hash a0 in
let h1 = hash a1 in
if h0 < h1
then a0, a1, h0, h1
else a1, a0, h1, h0
in
let has = h1 lsl 5 - h1 + h0
in
let has = has land mask in
if H.equal (Array_3.get0 array has) a0'
&& H.equal (Array_3.get1 array has) a1'
then
Array_3.get2 array has
else
let result = f a0 a1 in
Array_3.set array has a0' a1' result;
result
end
module Arity_One (H: Cacheable) (R: Result) =
struct
(* Creates the array used for the cache, and the mask to be used on keys
according to the array size. *)
let create () =
let size = !cache_size in
let array = Array_2.make size H.sentinel R.sentinel in
array, pred size
let cache = lazy (create ())
let clear () =
if Lazy.is_val cache then
let array, _ = Lazy.force cache in
Array_2.clear array H.sentinel R.sentinel
let merge f a0 =
let array, mask = Lazy.force cache in
let h0 = H.hash a0 in
let has = h0 land mask in
if H.equal (Array_2.get0 array has) a0
then
Array_2.get1 array has
else
let result = f a0 in
Array_2.set array has a0 result;
result
end
module Arity_Two (H0: Cacheable) (H1: Cacheable) (R: Result) =
struct
(* Creates the array used for the cache, and the mask to be used on keys
according to the array size. *)
let create () =
let size = !cache_size in
let array = Array_3.make size H0.sentinel H1.sentinel R.sentinel in
array, pred size
let cache = lazy (create ())
let clear () =
if Lazy.is_val cache then
let array, _ = Lazy.force cache in
Array_3.clear array H0.sentinel H1.sentinel R.sentinel
let merge f a0 a1 =
let array, mask = Lazy.force cache in
let h0 = H0.hash a0 in
let h1 = H1.hash a1 in
let has = h1 lsl 5 - h1 + h0
in
let has = has land mask in
if H0.equal (Array_3.get0 array has) a0
&& H1.equal (Array_3.get1 array has) a1
then
Array_3.get2 array has
else
let result = f a0 a1 in
Array_3.set array has a0 a1 result;
result
end
module Arity_Three (H0: Cacheable) (H1: Cacheable) (H2: Cacheable) (R: Result) =
struct
(* Creates the array used for the cache, and the mask to be used on keys
according to the array size. *)
let create () =
let size = !cache_size in
let array =
Array_4.make size H0.sentinel H1.sentinel H2.sentinel R.sentinel
in
array, pred size
let cache = lazy (create ())
let clear () =
if Lazy.is_val cache then
let array, _ = Lazy.force cache in
Array_4.clear array H0.sentinel H1.sentinel H2.sentinel R.sentinel
let merge f a0 a1 a2 =
let array, mask = Lazy.force cache in
let h0 = H0.hash a0 in
let h1 = H1.hash a1 in
let h2 = H2.hash a2 in
let has = h0 + 117 * h1 + 2375 * h2 in
let has = has land mask in
if H0.equal (Array_4.get0 array has) a0
&& H1.equal (Array_4.get1 array has) a1
&& H2.equal (Array_4.get2 array has) a2
then
Array_4.get3 array has
else
let result = f a0 a1 a2 in
Array_4.set array has a0 a1 a2 result;
result
end
module Array_Bit =
struct
let make size =
let size = (size + 7) lsr 3 in
Bytes.make size (char_of_int 0)
let get s i =
let c = i lsr 3 in
let b = 1 lsl (i land 7) in
(Char.code (Bytes.get s c)) land b <> 0
let set s i v =
let c = i lsr 3 in
let b = 1 lsl (i land 7) in
let oldcontents = Char.code (Bytes.get s c) in
let newcontents =
if v
then b lor oldcontents
else
let mask = lnot b in
oldcontents land mask
in
Bytes.set s c (Char.chr newcontents)
let clear s =
let zero = char_of_int 0 in
Bytes.fill s 0 (Bytes.length s) zero
end
module Binary_Predicate (H0: Cacheable) (H1: Cacheable) =
struct
(* Creates two arrays (one for the arguments, one for the boolean result),
and the mask to be used on keys according to the array size. *)
let create () =
let size = !cache_size in
let array_args = Array_2.make size H0.sentinel H1.sentinel in
let array_result = Array_Bit.make size in
array_args, array_result, pred size
let cache = lazy (create ())
let clear () =
if Lazy.is_val cache then
let array_args, array_result, _ = Lazy.force cache in
Array_2.clear array_args H0.sentinel H1.sentinel;
Array_Bit.clear array_result
let merge f a0 a1 =
let array_args, array_result, mask = Lazy.force cache in
let has =
let h0 = H0.hash a0 in
let h1 = H1.hash a1 in
599 * h0 + h1
in
let has = has land mask in
if H0.equal (Array_2.get0 array_args has) a0
&& H1.equal (Array_2.get1 array_args has) a1
then
Array_Bit.get array_result has
else
let r = f a0 a1 in
Array_2.set array_args has a0 a1;
Array_Bit.set array_result has r;
r
end
module Symmetric_Binary_Predicate (H0: Cacheable) =
struct
(* Creates two arrays (one for the arguments, one for the boolean result),
and the mask to be used on keys according to the array size. *)
let create () =
let size = !cache_size in
let array_args = Array_2.make size H0.sentinel H0.sentinel in
let array_result = Array_Bit.make size in
array_args, array_result, pred size
let cache = lazy (create ())
let clear () =
if Lazy.is_val cache then
let array_args, array_result, _ = Lazy.force cache in
Array_2.clear array_args H0.sentinel H0.sentinel;
Array_Bit.clear array_result
let hash = H0.hash
let merge f a0 a1 =
let array_args, array_result, mask = Lazy.force cache in
let a0, a1, h0, h1 =
let h0 = hash a0 in
let h1 = hash a1 in
if h0 < h1
then a0, a1, h0, h1
else a1, a0, h1, h0
in
let has = h1 lsl 5 - h1 + h0
in
let has = has land mask in
if H0.equal (Array_2.get0 array_args has) a0
&& H0.equal (Array_2.get1 array_args has) a1
then
Array_Bit.get array_result has
else
let r = f a0 a1 in
Array_2.set array_args has a0 a1;
Array_Bit.set array_result has r;
r
end
(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)