Skip to content

Commit 228dc65

Browse files
committed
Add a benchmark using work-stealing deque as a SPMC queue
This is a technically possible use case of a work-stealing deque and also a possible, although non-ideal, pattern for some programs running on a work-stealing scheduler.
1 parent c0b4b4a commit 228dc65

File tree

1 file changed

+80
-3
lines changed

1 file changed

+80
-3
lines changed

bench/bench_ws_deque.ml

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
open Multicore_bench
22
module Ws_deque = Saturn_lockfree.Work_stealing_deque.M
33

4-
let run_one ~budgetf ?(n_domains = 1) () =
4+
let run_as_scheduler ~budgetf ?(n_domains = 1) () =
55
let spawns =
66
Array.init n_domains @@ fun _ -> ref 0 |> Multicore_magic.copy_as_padded
77
in
@@ -85,6 +85,83 @@ let run_one ~budgetf ?(n_domains = 1) () =
8585
let n = Array.fold_left (fun n c -> n + !c) 0 spawns in
8686
Times.to_thruput_metrics ~n ~singular:"spawn" ~config times
8787

88+
let run_as_one_domain ~budgetf ?(n_msgs = 150 * Util.iter_factor) order =
89+
let t = Ws_deque.create () in
90+
91+
let op_lifo push =
92+
if push then Ws_deque.push t 101
93+
else match Ws_deque.pop t with _ -> () | exception Exit -> ()
94+
and op_fifo push =
95+
if push then Ws_deque.push t 101
96+
else match Ws_deque.steal t with _ -> () | exception Exit -> ()
97+
in
98+
99+
let init _ =
100+
assert (match Ws_deque.steal t with _ -> false | exception Exit -> true);
101+
Util.generate_push_and_pop_sequence n_msgs
102+
in
103+
let work _ bits =
104+
Util.Bits.iter (match order with `FIFO -> op_fifo | `LIFO -> op_lifo) bits
105+
in
106+
107+
let config =
108+
let label = match order with `FIFO -> "FIFO" | `LIFO -> "LIFO" in
109+
Printf.sprintf "one domain (%s)" label
110+
in
111+
Times.record ~budgetf ~n_domains:1 ~init ~work ()
112+
|> Times.to_thruput_metrics ~n:n_msgs ~singular:"message" ~config
113+
114+
let run_as_spmc ~budgetf ~n_thiefs () =
115+
let n_domains = n_thiefs + 1 in
116+
117+
let n_msgs = 70 * Util.iter_factor in
118+
119+
let t = Ws_deque.create () in
120+
121+
let n_msgs_to_steal = Atomic.make 0 |> Multicore_magic.copy_as_padded in
122+
123+
let init _ =
124+
assert (match Ws_deque.steal t with _ -> false | exception Exit -> true);
125+
Atomic.set n_msgs_to_steal n_msgs
126+
in
127+
let work i () =
128+
if i < n_thiefs then
129+
let rec work () =
130+
let n = Util.alloc n_msgs_to_steal in
131+
if 0 < n then
132+
let rec loop n =
133+
if 0 < n then
134+
match Ws_deque.steal t with
135+
| exception Exit ->
136+
Domain.cpu_relax ();
137+
loop n
138+
| _ -> loop (n - 1)
139+
else work ()
140+
in
141+
loop n
142+
in
143+
work ()
144+
else
145+
for i = 1 to n_msgs do
146+
Ws_deque.push t i
147+
done
148+
in
149+
150+
let config =
151+
Printf.sprintf "1 adder, %d taker%s" n_thiefs
152+
(if n_thiefs = 1 then "" else "s")
153+
in
154+
Times.record ~budgetf ~n_domains ~init ~work ()
155+
|> Times.to_thruput_metrics ~n:n_msgs ~singular:"message" ~config
156+
88157
let run_suite ~budgetf =
89-
[ 1; 2; 4; 8 ]
90-
|> List.concat_map @@ fun n_domains -> run_one ~budgetf ~n_domains ()
158+
List.concat
159+
[
160+
[ 1; 2; 4; 8 ]
161+
|> List.concat_map (fun n_domains ->
162+
run_as_scheduler ~budgetf ~n_domains ());
163+
[ 1; 2; 4 ]
164+
|> List.concat_map (fun n_thiefs -> run_as_spmc ~budgetf ~n_thiefs ());
165+
run_as_one_domain ~budgetf `FIFO;
166+
run_as_one_domain ~budgetf `LIFO;
167+
]

0 commit comments

Comments
 (0)