forked from ocaml-batteries-team/batteries-included
/
bench_map.ml
292 lines (231 loc) · 7.78 KB
/
bench_map.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
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
(* cd .. && ocamlbuild benchsuite/bench_map.native && _build/benchsuite/bench_map.native *)
(* The purpose of this test is to compare different implementation of
the Map associative data structure. *)
let total_length = 500_000
let (-|) = BatPervasives.(-|)
let (<|) = BatPervasives.(<|)
module MapBench (M : sig val input_length : int end) = struct
let input_length = M.input_length
let nb_iter =
max 10 (total_length / input_length)
let () = Printf.printf "%d iterations\n" nb_iter
let random_key () = Random.int input_length
let random_value () = Random.int input_length
let random_inputs random_elt () =
BatList.init input_length (fun _ -> random_elt ())
let make_samples input tests () = Bench.bench_funs tests input
(* we don't use BatInt to ensure that the same comparison function
is used (PMap use Pervasives.compare by default), in order to
have comparable performance results. *)
module StdMap = BatMap.Make(struct type t = int let compare = compare end)
module Map = BatMap
let same_elts stdmap pmap =
BatList.of_enum (StdMap.enum stdmap)
= BatList.of_enum (Map.enum pmap)
(* A benchmark for key insertion *)
let create_std_map input =
List.fold_left
(fun t (k, v) -> StdMap.add k v t)
StdMap.empty input
let create_poly_map input =
List.fold_left
(fun t (k, v) -> Map.add k v t)
Map.empty input
let create_input =
let keys = random_inputs random_key () in
let values = random_inputs random_value () in
BatList.combine keys values
let std_created_map = create_std_map create_input
let poly_created_map = create_poly_map create_input
let () =
assert (same_elts std_created_map poly_created_map)
let samples_create = make_samples create_input
[ "stdmap create", ignore -| create_std_map;
"pmap create", ignore -| create_poly_map ]
(* A benchmark for fast import *)
let import_std_map input =
StdMap.of_enum (BatList.enum input)
let import_poly_map input =
Map.of_enum (BatList.enum input)
let import_input = create_input
let () =
let std_imported_map = import_std_map import_input in
assert (same_elts std_imported_map poly_created_map);
let poly_imported_map = import_poly_map import_input in
assert (same_elts std_created_map poly_imported_map);
()
let samples_import = make_samples import_input
[ "stdmap import", ignore -| import_std_map;
"pmap import", ignore -| import_poly_map ]
(* A benchmark for key lookup *)
let lookup_input =
random_inputs random_key ()
let lookup_std_map input =
List.iter
(fun k -> ignore (StdMap.mem k std_created_map))
input
let lookup_poly_map input =
List.iter
(fun k -> ignore (Map.mem k poly_created_map))
input
let samples_lookup = make_samples lookup_input
[ "stdmap lookup", lookup_std_map;
"pmap lookup", lookup_poly_map ]
(* A benchmark for key removal *)
let remove_input =
random_inputs random_key ()
let remove_std_map input =
List.fold_left
(fun t k -> StdMap.remove k t)
std_created_map input
let remove_poly_map input =
List.fold_left
(fun t k -> Map.remove k t)
poly_created_map input
let () =
assert (same_elts
(remove_std_map remove_input)
(remove_poly_map remove_input))
let samples_remove = make_samples remove_input
[ "stdmap remove", ignore -| remove_std_map;
"pmap remove", ignore -| remove_poly_map ]
(* A benchmark for merging *)
let random_pairlist () =
BatList.combine
(random_inputs random_key ())
(random_inputs random_value ())
let p1 = random_pairlist ()
let p2 = random_pairlist ()
let merge_fun k a b =
if k mod 2 = 0 then None else Some ()
let merge_std_map =
let m1 = StdMap.of_enum (BatList.enum p1) in
let m2 = StdMap.of_enum (BatList.enum p2) in
fun () ->
StdMap.merge merge_fun m1 m2
let merge_poly_map =
let m1 = Map.of_enum (BatList.enum p1) in
let m2 = Map.of_enum (BatList.enum p2) in
fun () ->
Map.merge merge_fun m1 m2
let samples_merge = make_samples () [
"stdmap merge", ignore -| merge_std_map;
"pmap merge", ignore -| merge_poly_map;
]
(* compare fold-based and merge-based union, diff, intersect *)
let pmap_union (m1, m2) = Map.union m1 m2
let fold_union (m1, m2) =
Map.foldi Map.add m1 m2
let merge_union (m1, m2) =
let merge_fun k a b = if a <> None then a else b in
Map.merge merge_fun m1 m2
let union_input =
let m1 = Map.of_enum (BatList.enum p1) in
let m2 = Map.of_enum (BatList.enum p2) in
m1, m2
let () =
let li m = BatList.of_enum (Map.enum m) in
let test impl_union =
li (pmap_union union_input) = li (impl_union union_input) in
assert (test fold_union);
assert (test merge_union);
()
let samples_union = make_samples union_input [
"pmap union", ignore -| pmap_union;
"fold-based union", ignore -| fold_union;
"merge-based union", ignore -| merge_union;
]
let pmap_diff (m1, m2) =
Map.diff m1 m2
let fold_diff (m1, m2) =
Map.foldi (fun k _ acc -> Map.remove k acc) m2 m1
let merge_diff (m1, m2) =
let merge_fun k a b = if b <> None then None else a in
Map.merge merge_fun m1 m2
let diff_input =
let m1 = Map.of_enum (BatList.enum p1) in
let m2 = Map.of_enum (BatList.enum p2) in
m1, m2
let () =
let li m = BatList.of_enum (Map.enum m) in
let test impl_diff =
li (pmap_diff diff_input) = li (impl_diff diff_input) in
assert (test fold_diff);
assert (test merge_diff);
()
let samples_diff = make_samples diff_input [
"pmap diff", ignore -| pmap_diff;
"fold-based diff", ignore -| fold_diff;
"merge-based diff", ignore -| merge_diff;
]
let pmap_intersect f (m1, m2) =
Map.intersect f m1 m2
let filter_intersect f (m1, m2) =
let filter_fun k v1 =
match
try Some (Map.find k m2)
with Not_found -> None
with
| None -> None
| Some v2 -> Some (f v1 v2) in
Map.filter_map filter_fun m1
let merge_intersect f (m1, m2) =
let merge_fun k a b =
match a, b with
| Some v1, Some v2 -> Some (f v1 v2)
| None, _ | _, None -> None in
Map.merge merge_fun m1 m2
let intersect_input =
let m1 = Map.of_enum (BatList.enum p1) in
let m2 = Map.of_enum (BatList.enum p2) in
m1, m2
let () =
let li m = BatList.of_enum (Map.enum m) in
let test impl_intersect =
li (pmap_intersect (-) intersect_input)
= li (impl_intersect (-) intersect_input) in
assert (test filter_intersect);
assert (test merge_intersect);
()
let samples_intersect = make_samples intersect_input [
"pmap intersect", ignore -| pmap_intersect (-);
"filter-based intersect", ignore -| filter_intersect (-);
"merge-based intersect", ignore -| merge_intersect (-);
]
let () =
let create = samples_create () in
let import = samples_import () in
let lookup = samples_lookup () in
let remove = samples_remove () in
let merge = samples_merge () in
let union = samples_union () in
let diff = samples_diff () in
let intersect = samples_intersect () in
List.iter
(print_newline -| Bench.summarize)
[
create;
import;
lookup;
remove;
merge;
union;
diff;
intersect;
]
end
let big_length = 100_000
let small_length = 500
let () =
Printf.printf "Test with small maps (length = %d)\n%!" small_length;
let () =
let module M = MapBench(struct let input_length = small_length end) in
() in
print_newline ();
print_newline ();
Printf.printf "Test with big maps (length = %d)\n%!" big_length;
Bench.config.Bench.samples <- 100;
let () =
let module M = MapBench(struct let input_length = big_length end) in
() in
()