File: bench.ml

package info (click to toggle)
ocaml-visitors 20200210-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,896 kB
  • sloc: ml: 4,077; makefile: 44; sh: 18
file content (136 lines) | stat: -rw-r--r-- 3,277 bytes parent folder | download | duplicates (2)
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
module Bench = Core_bench.Bench
module Command = Core.Command

let run tests =
  let tests =
    List.map (fun (name, test) -> Bench.Test.create ~name test) tests
  in
  Command.run (Bench.make_command tests)

type expr =
  | EConst of (int[@opaque])
  | EAdd of expr * expr
  | EList of expr list
  [@@deriving visitors { variety = "iter"; concrete = true },
              visitors { variety = "map"; concrete = true },
              visitors { variety = "reduce"; ancestors = ["VisitorsRuntime.addition_monoid"]; concrete = true }]

let iter : expr -> unit =
  new iter # visit_expr ()

let rec native_iter env e =
  match e with
  | EConst _ ->
      ()
  | EAdd (e1, e2) ->
      native_iter env e1;
      native_iter env e2
  | EList es ->
      List.iter (native_iter env) es

class size = object
  inherit [_] reduce as super
  method! visit_expr () e =
    1 + super # visit_expr () e
end

let size : expr -> int =
  new size # visit_expr ()

let rec native_size env e =
  match e with
  | EConst _ ->
      1
  | EAdd (e1, e2) ->
      native_size env e1 +
      native_size env e2
  | EList es ->
      List.fold_left (fun accu e -> accu + native_size env e) 0 es

let rec native_size_noenv e =
  match e with
  | EConst _ ->
      1
  | EAdd (e1, e2) ->
      native_size_noenv e1 +
      native_size_noenv e2
  | EList es ->
      List.fold_left (fun accu e -> accu + native_size_noenv e) 0 es

let rec native_size_noenv_accu accu e =
  match e with
  | EConst _ ->
      accu + 1
  | EAdd (e1, e2) ->
      let accu = native_size_noenv_accu accu e1 in
      native_size_noenv_accu accu e2
  | EList es ->
      List.fold_left native_size_noenv_accu accu es

let split n =
  assert (n >= 0);
  let n1 = Random.int (n + 1) in
  let n2 = n - n1 in
  assert (0 <= n1 && n1 <= n);
  assert (0 <= n2 && n2 <= n);
  n1, n2

let rec generate n =
  assert (n >= 0);
  if n = 0 then
    EConst (Random.int 100)
  else
    match Random.int 2 with
    | 0 ->
        let n1, n2 = split (n - 1) in
        EAdd (generate n1, generate n2)
    | 1 ->
        let n1, n2 = split (n - 1) in
        EList [ generate n1; generate n2 ]
    | _ ->
        assert false

let rec list_init i n f =
  if i = n then
    []
  else
    let x = f i in
    x :: list_init (i + 1) n f

let list_init n f =
  list_init 0 n f

let samples =
  list_init 100 (fun _ -> generate 100)

let test f () =
  List.iter (fun e -> ignore (f e)) samples

let () =
  run [
    "iter", test iter;
    "native_iter", test (native_iter ());
    ]

let () =
  run [
    "size", test size;
    "native_size", test (native_size ());
(*
    "native_size_noenv", test native_size_noenv;
    "native_size_noenv_accu", test (native_size_noenv_accu 0);
 *)
  ]

(* with hoisting, applied to self#visit_expr:
   iter allocates no memory, and is 46% slower than native_iter (which allocates).
   without hoisting:
   iter allocates memory and is 65% slower than native_iter
   So, in this case, hoisting helps a little.

   with hoisting, applied to self#visit_expr:
   size allocates no memory, and is 105% slower than native_size (which allocates).
   without hoisting:
   size allocates memory (same amount as native_size) and is 86% slower than native_size
   So, in this case, hoisting seems counter-productive.
 *)