File: multicore_bench.mli

package info (click to toggle)
ocaml-multicore-bench 0.1.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 320 kB
  • sloc: ml: 1,476; sh: 60; makefile: 6
file content (294 lines) | stat: -rw-r--r-- 9,901 bytes parent folder | download
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
(** Multicore bench is a framework for writing multicore benchmark executables
    to run on {{:https://github.com/ocurrent/current-bench}current-bench}.

    To use the framework one typically opens it

    {[
      open Multicore_bench
    ]}

    which brings a number of submodules into scope. *)

module Trend : sig
  (** Dealing with trends. *)

  type t = [ `Lower_is_better | `Higher_is_better ]
  (** Whether a lower or higher value is better. *)
end

module Metric : sig
  (** Dealing with benchmark metrics. *)

  type t
  (** Represents a metric. *)

  val make :
    metric:string ->
    config:string ->
    ?units:string ->
    ?trend:[< Trend.t ] ->
    ?description:string ->
    [< `Float of float ] ->
    t
  (** [make ~metric ~config value] constructs a metric with given
      specification. *)
end

module Unit_of_rate : sig
  (** Dealing with units of rate. *)

  type t =
    [ `_1  (** 1/s *)
    | `k  (** 10{^ 3}/s or k/s *)
    | `M  (** 10{^ 6}/s or M/s *)
    | `G  (** 10{^ 9}/s or G/s *) ]
  (** Represents a unit of rate, i.e. how many per second. *)

  val to_divisor : [< t ] -> float
  (** [to_divisor t] converts the unit of rate [t] to a divisor. *)

  val to_mnemonic : [< t ] -> string
  (** [to_mnemonic t] returns a human readable mnemonic for the unit of rate
      [t]. *)
end

module Unit_of_time : sig
  (** Dealing with units of time. *)

  type t =
    [ `s  (** seconds *)
    | `ms  (** milliseconds *)
    | `mus  (** microseconds *)
    | `ns  (** nanoseconds *) ]
  (** Represents a unit of time. *)

  val to_multiplier : [< t ] -> float
  (** [to_multiplier t] converts the unit of time [t] to a multiplier. *)

  val to_mnemonic : [< t ] -> string
  (** [to_mnemonic t] returns a human readable mnemonic for the unit of time
      [t]. *)
end

module Times : sig
  (** Recording timings of benchmarks running on multiple domains in parallel
      and producing metrics from the recorded timings. *)

  type t
  (** Represents a record of elapsed times of multiple runs of a benchmark
      running on multiple domains. *)

  val record :
    budgetf:float ->
    n_domains:int ->
    ?ensure_multi_domain:bool ->
    ?domain_local_await:[< `Busy_wait | `Neglect > `Busy_wait ] ->
    ?n_warmups:int ->
    ?n_runs_min:int ->
    ?n_runs_max:int ->
    ?before:(unit -> unit) ->
    init:(int -> 's) ->
    ?wrap:(int -> 's -> (unit -> unit) -> unit) ->
    work:(int -> 's -> unit) ->
    ?after:(unit -> unit) ->
    unit ->
    t
  (** [record ~budgetf ~n_domains ~init ~work ()] essentially repeatedly runs
      [let x = init i in wrap i x (fun () -> .. work i x ..)] on specified
      number of domains, [i ∊ [0, n_domains-1]], and records the times that
      calls of [work] take.  The calls of [work] are synchronized to start as
      simultaneously as possible.

      Optional arguments:

      - [~ensure_multi_domain]: Whether to run an extra busy untimed domain when
        [n_domains] is [1].  Doing so prevents the OCaml runtime from using
        specialized runtime implementations.  Defaults to [true].

      - [~domain_local_await]: Specifies whether and how to configure
        {{:https://github.com/ocaml-multicore/domain-local-await/}domain-local-await}
        or DLA.  [`Neglect] does not reconfigure DLA.  [`Busy_wait] configures
        DLA to use a busy-wait implementation, which prevents domains from going
        to sleep.  Defaults to [`Busy_wait].

      - [~n_warmups]: Specifies the number of warmup runs to perform before the
        actual measurements.  Defaults to [3].

      - [~n_runs_min]: Specifies the minimum number of timed runs.  The upper
        bound is determined dynamically based on [budgetf]. Defaults to [7].

      - [~n_runs_max]: Specifies the maximum number of timed runs.  Defaults to
        [1023].

      - [~before]: Specifies an action to run on one domain before [init].

      - [~after]: Specifies an action to run on one domain after [work]. *)

  val to_thruput_metrics :
    n:int ->
    singular:string ->
    ?plural:string ->
    config:string ->
    ?unit_of_time:Unit_of_time.t ->
    ?unit_of_rate:Unit_of_rate.t ->
    t ->
    Metric.t list
  (** [to_thruput_metrics ~n ~singular ~config times] produces a pair of metrics
      from the recorded [times] where one metric is for the time a single
      operation takes and the other is the thruput of operations over all
      domains.

      Optional arguments:

      - [~plural]: Plural for the operation.  Defaults to [singular ^ "s"].

      - [~unit_of_time]: Unit of time for the duration of a single operation.
        Defaults to [`ns].

      - [~unit_of_rate]: Unit of rate for the number of operations per second.
        Defaults to [`M]. *)
end

module Suite : sig
  (** Dealing with benchmark suites. *)

  type t = budgetf:float -> Metric.t list
  (** Represents a benchmark suite, i.e. a function that produces a list of
      metric outputs for
      {{:https://github.com/ocurrent/current-bench}current-bench}. *)
end

module Cmd : sig
  (** Command line interface for a benchmark executable. *)

  type output =
    [ `JSON
      (** [`JSON] gives the JSON output for
          {{:https://github.com/ocurrent/current-bench}current-bench}. *)
    | `Brief  (** [`Brief] gives concise human readable output. *)
    | `Diff of string
      (** [`Diff "path.json"] gives concise human readable diff against results
          stored in specified [path.json] file. *)
    ]
  (** Specifies the output format. *)

  val run :
    benchmarks:(string * Suite.t) list ->
    ?budgetf:float ->
    ?filters:string list ->
    ?debug:bool ->
    ?output:output ->
    ?argv:string array ->
    ?flush:bool ->
    ?randomize:bool ->
    unit ->
    unit
  (** [run ~benchmarks ()] interprets command line arguments and runs the
      benchmarks suites based on the arguments.

      Optional arguments:

      - [~budgetf]: A budget (usually) in seconds passed to each benchmark
        suite.  This defaults to a small number so that a benchmark suite can be
        used as a test.

      - [~filters]: A list of regular expressions to match names of benchmark
        suites.  If any regular expression matches the name of benchmark, then
        that benchmark will be run.  Defaults to [[]].

      - [~debug]: Print progress information to help debugging.  Defaults to
        [false].

      - [~output]: Output mode.  Defaults to [`JSON].

      - [~argv]: Array of command line arguments.  Defaults to [Sys.argv].

      - [~flush]: Whether to flush the standard output after writing it.
        Defaults to [true].

      - [~randomize]: Whether to randomize the order of suites or not.  Defaults
        to [true].

      Command line arguments take precedence over the optional arguments.  In
      other words, you can specify the optional arguments to give defaults for
      the benchmark executable. *)
end

module Countdown : sig
  (** Scalable low-level countdown. *)

  type t
  (** Represents a countdown counter. *)

  val create : n_domains:int -> unit -> t
  (** [create ~n_domains ()] returns a new countdown counter with initial value
      of [0]. *)

  val non_atomic_set : t -> int -> unit
  (** [non_atomic_set countdown count] sets the [count] of the [countdown].

      ⚠️ This operation is not atomic.  However, it is safe to call
      [non_atomic_set] with the same [countdown] and [count] in parallel,
      because the [countdown] will be initialized deterministically. *)

  val get : t -> int
  (** [get countdown] returns the count of the [countdown]. *)

  val alloc : t -> domain_index:int -> batch:int -> int
  (** [alloc countdown ~domain_index ~batch] tries to reduce the count of the
      [countdown] by at most [batch] (which must be positive) and returns the
      number by which the count was reduced or [0] in case the count was already
      [0]. *)
end

module Util : sig
  (** Utilities for creating benchmarks.

      ⚠️ In the future we expect to regroup these utilities under different
      modules and deprecate them in this module. *)

  val iter_factor : int
  (** A multiplier depending various factors such as whether we are running on a
      32- or 64-bit machine (1x/10x), bytecode or native (1x/10x), and whether
      we are running on single-core or multicore OCaml (1x/10x). *)

  val alloc : ?batch:int -> int Atomic.t -> int
  (** [alloc ~batch n] tries to decrement the specified atomic variable [n] by
      at most the optional amount [~batch] and not beyond [n] having value [0].
      Returns the amount by which [n] was decremented, which is [0] only in case
      [n] is [0]. *)

  val cross : 'a list -> 'b list -> ('a * 'b) list
  (** [cross xs ys] returns a list formed by pairing each element of [xs] with
      each element of [ys].

      For example:
      {[
        # Util.cross [1; 2; 3] ["a"; "b"]
        - : (int * string) list =
        [(1, "a"); (1, "b"); (2, "a"); (2, "b"); (3, "a"); (3, "b")]
      ]} *)

  module Bits : sig
    (** A minimalistic bitset data structure. *)

    type t
    (** Represents a bitset. *)

    val create : unit -> t
    (** [create ()] returns a new zero length bitset. *)

    val push : t -> bool -> unit
    (** [push bs b] adds the bit [b] to the end of the bitset [bs]. *)

    val iter : (bool -> unit) -> t -> unit
    (** [iter action bs] calls the [action] for each bit in the bitset [bs]. *)
  end

  val generate_push_and_pop_sequence : ?state:Random.State.t -> int -> Bits.t
  (** [generate_push_and_pop_sequence n] generates a bitset where each [true]
      bit represents a "push" operation and each [false] bit represents a
      "try_pop" operation.  Performing the operations on an initially empty
      dispenser leaves the dispenser empty.  The sequence may include "try_pop"
      operations at points where the dispenser will be empty. *)
end