add benchs/CCBench helper module (tree of benchmarks)

This commit is contained in:
Simon Cruanes 2014-11-13 15:41:10 +01:00
parent 0c1e7c30e8
commit 2606833518
2 changed files with 366 additions and 0 deletions

251
benchs/CCBench.ml Normal file
View file

@ -0,0 +1,251 @@
(*
copyright (c) 2013-2014, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
this software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. in no event shall the copyright holder or contributors be liable
for any direct, indirect, incidental, special, exemplary, or consequential
damages (including, but not limited to, procurement of substitute goods or
services; loss of use, data, or profits; or business interruption) however
caused and on any theory of liability, whether in contract, strict liability,
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.
*)
(** {1 helpers for benchmarks} *)
let print_line_ fmt () =
Format.pp_print_string fmt (CCString.repeat "*" 80);
Format.pp_print_newline fmt ()
let print_list_ ?(sep=", ") pp_item fmt l =
let rec print fmt l = match l with
| x::((_::_) as l) ->
pp_item fmt x;
Format.pp_print_string fmt sep;
Format.pp_print_cut fmt ();
print fmt l
| x::[] -> pp_item fmt x
| [] -> ()
in
print fmt l
(** {2 Bench Tree} *)
module SMap = Map.Make(String)
type single_bench = unit -> Benchmark.samples
type bench =
| Multiple of bench list * bench SMap.t
| Bench of single_bench
| WithInt of ((int -> bench) * int) list
let is_multiple = function
| Multiple _ -> true
| _ -> false
let rec merge_ t1 t2 = match t1, t2 with
| Multiple (l, map), ((Bench _ | WithInt _) as x) ->
Multiple (x :: l, map)
| Multiple (l1, m1), Multiple (l2, m2) ->
let m = SMap.merge
(fun _ o1 o2 -> merge_opt_ o1 o2)
m1 m2
in
Multiple (l1 @ l2, m)
| (Bench _ | WithInt _), Multiple _ -> merge_ t2 t1
| Bench _, _
| WithInt _, _ ->
Multiple ([t1; t2], SMap.empty) (* composite *)
and merge_opt_ o1 o2 = match o1, o2 with
| None, None -> None
| Some o, None
| None, Some o -> Some o
| Some o1, Some o2 -> Some (merge_ o1 o2)
let mk_list = function
| [] -> invalid_arg "mk_list"
| x :: tail -> List.fold_left merge_ x tail
let raw f = Bench f
let throughput1 ?min_count ?style ?fwidth ?fdigits ?repeat time ?name f x =
Bench (fun () ->
Benchmark.throughput1 ?min_count ?style ?fwidth ?fdigits ?repeat time ?name f x)
let throughputN ?style ?fwidth ?fdigits ?repeat time f =
Bench (fun () ->
Benchmark.throughputN ?style ?fwidth ?fdigits ?repeat time f)
let (>::) n t =
if n = "" then invalid_arg ">::";
Multiple ([], SMap.singleton n t)
let (>:::) n l =
if n = "" then invalid_arg ">:::";
Multiple ([], SMap.singleton n (mk_list l))
let with_int f = function
| [] -> invalid_arg "with_int: empty list"
| l -> WithInt (List.map (fun n -> f, n) l)
let map_int l =
if l = [] then invalid_arg "map_int";
WithInt l
(* print the structure of the tree *)
let rec print fmt = function
| Multiple (l, m) ->
Format.fprintf fmt "@[<hv>%a%a@]"
(print_list_ ~sep:"," print) l
print_map m
| WithInt l ->
Format.fprintf fmt "@[<hv>[%a]@]"
(print_list_ print_pair)
(List.map (fun (f, n) -> n, f n) l)
| Bench _ -> Format.fprintf fmt "<>"
and print_pair fmt (n,t) =
Format.fprintf fmt "@[<h>%d: %a@]" n print t
and print_map fmt m =
Format.pp_open_hvbox fmt 0;
SMap.iter (fun n t -> Format.fprintf fmt "@[%s.%a@]" n print t) m;
Format.pp_close_box fmt ()
(** {2 Path} *)
type path = string list
let print_path fmt path =
Format.fprintf fmt "@[<h>%a@]"
(print_list_ ~sep:"." Format.pp_print_string) path
let str_split_ ~by s =
let len_by = String.length by in
assert (len_by > 0);
let l = ref [] in
let n = String.length s in
let rec search prev i =
if i >= n
then (
if i>prev then l := String.sub s prev (n-prev) :: !l ;
List.rev !l
)
else if is_prefix i 0
then begin
l := (String.sub s prev (i-prev)) :: !l; (* save substring *)
search (i+len_by) (i+len_by)
end
else search prev (i+1)
and is_prefix i j =
if j = len_by
then true
else if i = n
then false
else s.[i] = by.[j] && is_prefix (i+1) (j+1)
in search 0 0
let parse_path s = str_split_ ~by:"." s
let () =
assert (parse_path "foo.bar" = ["foo";"bar"]);
assert (parse_path "foo" = ["foo"]);
assert (parse_path "" = []);
()
let prefix path t = List.fold_right (fun s t -> s >:: t) path t
(** {2 Run} *)
(* run one atomic single_bench *)
let run_single_bench_ fmt path f =
print_line_ fmt ();
Format.fprintf fmt "run single_bench %a@." print_path (List.rev path);
let res = f () in
Benchmark.tabulate res
(* run all benchs *)
let rec run_all fmt path t = match t with
| Bench f -> run_single_bench_ fmt path f
| Multiple (l, m) ->
List.iter (run_all fmt path) l;
SMap.iter
(fun n t' ->
let path = n :: path in
run_all fmt path t'
) m
| WithInt l ->
List.iter (fun (f, n) -> run_all fmt path (f n)) l
let run fmt t = run_all fmt [] t
let sprintf_ format =
let b = Buffer.create 32 in
let fmt = Format.formatter_of_buffer b in
Format.kfprintf
(fun fmt -> Format.pp_print_flush fmt (); Buffer.contents b) fmt format
(* run all within a path *)
let rec run_path_rec_ fmt path remaining t = match t, remaining with
| _, [] -> run_all fmt path t
| Multiple (_, m), s :: remaining' ->
begin try
let t' = SMap.find s m in
run_path_rec_ fmt (s::path) remaining' t'
with Not_found ->
let msg = sprintf_ "could not find %s under path %a"
s print_path (List.rev path) in
failwith msg
end
| WithInt l, _ ->
List.iter (fun (f, n) -> run_path_rec_ fmt path remaining (f n)) l
| Bench _, _::_ -> ()
let run_path fmt t path = run_path_rec_ fmt [] path t
let run_main ?(argv=Sys.argv) ?(out=Format.std_formatter) t =
let path = ref [] in
let do_print_tree = ref false in
let set_path_ s = path := parse_path s in
let options =
[ "-p", Arg.String set_path_, "only apply to subpath"
; "-tree", Arg.Set do_print_tree, "print bench tree"
] in
try
Arg.parse_argv argv options (fun _ -> ()) "run benchmarks [options]";
if !do_print_tree
then Format.fprintf out "@[%a@]@." print t
else (
Format.printf "run on path %a@." print_path !path;
run_path out t !path (* regular path *)
)
with Arg.Help msg ->
Format.pp_print_string out msg
(** {2 Global Registration} *)
module Glob = struct
let tree_ = ref (Multiple ([], SMap.empty))
let get () = !tree_
let register ?(path=[]) new_t =
tree_ := merge_ !tree_ (prefix path new_t)
let register' ~path new_t =
register ~path:(parse_path path) new_t
let run_main ?argv ?out () =
run_main ?argv ?out !tree_
end

115
benchs/CCBench.mli Normal file
View file

@ -0,0 +1,115 @@
(*
copyright (c) 2013-2014, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
this software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. in no event shall the copyright holder or contributors be liable
for any direct, indirect, incidental, special, exemplary, or consequential
damages (including, but not limited to, procurement of substitute goods or
services; loss of use, data, or profits; or business interruption) however
caused and on any theory of liability, whether in contract, strict liability,
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.
*)
(** {1 helpers for benchmarks} *)
(** {2 Benchmark Tree}
Naming benchmark within a hierarchy that allows to filter them *)
type bench
val throughput1 :
?min_count:Int64.t ->
?style:Benchmark.style ->
?fwidth:int ->
?fdigits:int ->
?repeat:int -> int -> ?name:string -> ('a -> 'b) -> 'a -> bench
val throughputN :
?style:Benchmark.style ->
?fwidth:int ->
?fdigits:int ->
?repeat:int -> int -> (string * ('a -> 'b) * 'a) list -> bench
val raw : (unit -> Benchmark.samples) -> bench
(** Give control to the user to produce her samples *)
val (>::) : string -> bench -> bench
val mk_list : bench list -> bench
val (>:::) : string -> bench list -> bench
val with_int : (int -> bench) -> int list -> bench
(** Parametrize a bench with several values *)
val map_int : ((int -> bench) * int) list -> bench
(** One function for each integer.
@raise Invalid_argument if the two lists don't have the same length
or are empty *)
val print : Format.formatter -> bench -> unit
(** Print the tree of benchmarks *)
(** {2 Path}
A path in a benchmark tree *)
type path = string list
val print_path : Format.formatter -> path -> unit
val parse_path : string -> path
(** split a string into a path at the "." separators *)
val prefix : path -> bench -> bench
(** Add the path as a prefix to the tree *)
(** {2 Running} *)
val run : Format.formatter -> bench -> unit
(** [run fmt t] runs all benchmarks of [t] and print the results to [fmt] *)
val run_path : Format.formatter -> bench -> path -> unit
(** Run only a sub-tree of the benchmarks *)
val run_main :
?argv:string array ->
?out:Format.formatter ->
bench -> unit
(** Main function: parses the command line arguments and runs benchmarks
accordingly *)
(** {2 Global Registration} *)
module Glob : sig
val register : ?path:path -> bench -> unit
(** Register a benchmark to the global register of benchmarks (a global tree) *)
val register' : path:string -> bench -> unit
(** Same as {!register} but applies {!parse_path} first to its argument *)
val get : unit -> bench
(** Global bench tree *)
val run_main :
?argv:string array ->
?out:Format.formatter ->
unit -> unit
(** Same as {!run_main} but on the global tree of benchmarks *)
end