From d7bbb65c48be26a287bb9aca3327f228e165f2b8 Mon Sep 17 00:00:00 2001 From: Gabriel Scherer Date: Sat, 30 Jan 2021 22:05:54 +0100 Subject: [PATCH] benchmark various implementations of Bench.to_seq discussed in #1006 Results on my machine: enumeration (89.71 us) is 77.5% faster than batseq (399.39 us) which is 3.8% faster than too strict (415.29 us) which is probably (alpha=40.06%) same speed as simple (416.74 us) Saving times to times.flat This suggests that using an enumeration indeed provides a large performance advantage over the simple approach. --- Makefile | 1 + benchsuite/bench_set_to_seq.ml | 80 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 benchsuite/bench_set_to_seq.ml diff --git a/Makefile b/Makefile index f44f5bfdb..30aaa988c 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,7 @@ BENCH_TARGETS += benchsuite/lines_of.native BENCH_TARGETS += benchsuite/bitset.native BENCH_TARGETS += benchsuite/bench_map.native BENCH_TARGETS += benchsuite/bench_nreplace.native +BENCH_TARGETS += benchsuite/bench_set_to_seq.native TEST_TARGET = test-byte ifeq ($(BATTERIES_NATIVE_SHLIB), yes) diff --git a/benchsuite/bench_set_to_seq.ml b/benchsuite/bench_set_to_seq.ml new file mode 100644 index 000000000..4bda8a5c2 --- /dev/null +++ b/benchsuite/bench_set_to_seq.ml @@ -0,0 +1,80 @@ +(* cd .. && ocamlbuild -use-ocamlfind benchsuite/bench_set_to_seq.native && _build/benchsuite/bench_set_to_seq.native *) + +(* The purpose of this test is to compare different implementation of + BatSet.to_seq. *) + +(* the type BatSet.t is abstract, + we break the abstraction boundary locally to implement our versions outside the module. *) +type 'a set = + | Empty + | Node of 'a set * 'a * 'a set * int +external hide : 'a set -> 'a BatSet.t = "%identity" +external reveal : 'a BatSet.t -> 'a set = "%identity" + + +module TooStrict = struct + let rec to_seq m = + match m with + | Empty -> BatSeq.nil + | Node(l, v, r, _) -> + BatSeq.append (to_seq l) (fun () -> BatSeq.Cons (v, to_seq r)) + + let to_seq s = to_seq (reveal s) +end + +module Simple = struct + let rec to_seq m = + fun () -> + match m with + | Empty -> BatSeq.Nil + | Node(l, v, r, _) -> + BatSeq.append (to_seq l) (fun () -> BatSeq.Cons (v, to_seq r)) () + + let to_seq s = to_seq (reveal s) +end + +module Enumeration = struct + type 'a iter = E | C of 'a * 'a set * 'a iter + + let rec cons_iter s t = match s with + | Empty -> t + | Node (l, e, r, _) -> cons_iter l (C (e, r, t)) + + let to_seq s = + let rec to_seq iter () = + match iter with + | E -> BatSeq.Nil + | C (e, r, t) -> + BatSeq.Cons (e, to_seq (cons_iter r t)) + in + to_seq (cons_iter s E) + + let to_seq s = to_seq (reveal s) +end + +let test_input = + let s = ref BatSet.empty in + for i = 0 to 9999 do + s := BatSet.add i !s; + done; + !s + +let test to_seq = + test_input + |> to_seq + |> BatSeq.length + +let () = + assert (test TooStrict.to_seq = test BatSet.to_seq); + assert (test Simple.to_seq = test BatSet.to_seq); + assert (test Enumeration.to_seq = test BatSet.to_seq); + () + +let () = + let repeat f n = for i = 1 to n do ignore (f ()) done in + Bench.bench_n [ + "too strict", repeat (fun () -> test TooStrict.to_seq); + "simple", repeat (fun () -> test Simple.to_seq); + "enumeration", repeat (fun () -> test Enumeration.to_seq); + "batseq", repeat (fun () -> test BatSet.to_seq); + ] |> Bench.run_outputs