package ppx_bench

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file benchmark_accumulator.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
let unique_id =
  let r = ref 0 in
  fun () -> incr r; !r

(* Used to track the current libname in such a way that for functor applications, it is
   the calling libraries name that gets registered. *)
module Current_libname = struct
  let null = "<unknown>"
  let libname_ref = ref null

  let set str = libname_ref := str
  let unset () = libname_ref := null
  let get () = !libname_ref
end

module Current_bench_module_stack = struct
  let t = ref []

  let push s = t := s :: !t

  let pop_exn () = t := List.tl !t

  let to_name () =
    match !t with
    | [] -> None
    | ms -> Some (String.concat "." (List.rev ms))
end

(* This is the main data structure of this module. An [Entry.t] represents a benchmark
   along with some metadata about is position, arguments etc. *)
module Entry = struct

  type 'a indexed_spec = {
    arg_name   : string;
    arg_values : int list;
    thunk      : int -> unit -> 'a;
  }

  type test_spec =
    | Regular_thunk : ([`init] -> unit -> 'a) -> test_spec
    | Indexed_thunk : 'a indexed_spec -> test_spec

  type t = {
    unique_id         : int;
    code              : string;
    type_conv_path    : string;
    name              : string;
    filename          : string;
    line              : int;
    startpos          : int;
    endpos            : int;
    test_spec         : test_spec;
    bench_module_name : string option;
  }

  let compare t1 t2 = compare t1.unique_id t2.unique_id

  let get_indexed_arg_name t =
    match t.test_spec with
    | Regular_thunk _ -> None
    | Indexed_thunk {arg_name; _} -> Some arg_name

  (* Extracts module name from ["filename.ml.Module"], which is the format of [ext_name]
     as set by [typeconv]. *)
  let get_module_name_opt t =
    let str = t.type_conv_path in
    let len = String.length str in
    let rec loop i =
      if i + 4 <= len
      then
        if String.sub str i 4 = ".ml."
        then Some (String.sub str (i + 4) (len - i - 4))
        else loop (i + 1)
      else None
    in
    loop 0
end

(* Inspect system environment variables to decide if benchmarks are being run. This is
   called by the code generated by the [pa_bench] syntax to decide if the global hashtable
   should be populated. *)
let add_environment_var =
  let v =
    try Sys.getenv "BENCHMARKS_RUNNER" with
    | Not_found -> ""
  in
  v = "TRUE"

(* This hashtable contains all the benchmarks from all the of libraries that have been
   loaded. At the time the benchmarks are registering themselves with [ppx_bench_lib] we
   don't yet know which libraries will need to be run.  *)
let libs_to_entries : (string, Entry.t list) Hashtbl.t = Hashtbl.create 10

let lookup_rev_lib ~libname =
  try Hashtbl.find libs_to_entries libname
  with Not_found -> []

let lookup_lib ~libname =
  List.rev (lookup_rev_lib ~libname)

let force_drop =
  (* Useful for js_of_ocaml to perform deadcode elimination.
     see ppx/ppx_inline_test/runtime-lib/runtime.ml [Action.get] for more details *)
  try ignore (Sys.getenv "FORCE_DROP_BENCH" : string); true
  with Not_found -> false

let get_mode () =
  if force_drop
  then `Ignore
  else `Collect

let add_bench
      ~name
      ~code
      ~filename
      ~type_conv_path
      ~line
      ~startpos
      ~endpos
      test_spec
  =
  match get_mode () with
  | `Ignore -> ()
  | `Collect ->
    let libname = Current_libname.get () in
    let entry = { Entry.
      code; unique_id = unique_id ();
      type_conv_path; bench_module_name = Current_bench_module_stack.to_name ();
      name; filename; line; startpos; endpos; test_spec;
    } in
    Hashtbl.add libs_to_entries libname (entry :: lookup_rev_lib ~libname)

let add_bench_module
    ~name
    ~code:_
    ~type_conv_path:_
    ~filename:_
    ~line:_
    ~startpos:_
    ~endpos:_
    f =
  match get_mode () with
  | `Ignore -> ()
  | `Collect ->
    (* Running f registers the benchmarks using BENCH *)
    Current_bench_module_stack.push name;
    try
      f ();
      Current_bench_module_stack.pop_exn ();
    with ex ->
      Current_bench_module_stack.pop_exn ();
      raise ex
OCaml

Innovation. Community. Security.