Skip to content

Commit 3801d9e

Browse files
committed
Add ?verbose flag to test scheduler and add a test of fairness
1 parent 56b1a02 commit 3801d9e

File tree

5 files changed

+97
-19
lines changed

5 files changed

+97
-19
lines changed

example/guards.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ let () =
9797
likely use e.g. the multififo or the random scheduler to compute with
9898
guarded case statements. *)
9999
let max_domains = Picos_domain.recommended_domain_count () in
100-
Test_scheduler.run ~max_domains @@ fun () ->
100+
Test_scheduler.run ~verbose:true ~max_domains @@ fun () ->
101101
Run.all
102102
[
103103
(fun () -> assert (42 = guard1 ()));

test/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@
241241
backoff
242242
domain_shims
243243
picos
244+
picos_domain
244245
picos_std.finally
245246
picos_std.structured
246247
picos_std.sync

test/lib/test_scheduler/test_scheduler.ocaml4.ml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,22 @@ let init () =
2323
propagate ()
2424
end
2525

26-
let run_fiber ?max_domains:_ ?allow_lwt:_ ?avoid_threads:_ ?fatal_exn_handler
27-
fiber main =
26+
let explain () = Printf.printf "Testing with scheduler: threads\n%!"
27+
28+
let run_fiber ?(verbose = false) ?max_domains:_ ?allow_lwt:_ ?avoid_threads:_
29+
?fatal_exn_handler fiber main =
2830
init ();
29-
Picos_mux_thread.run_fiber ?fatal_exn_handler fiber main
31+
if verbose then explain ();
32+
try Picos_mux_thread.run_fiber ?fatal_exn_handler fiber main
33+
with exn ->
34+
if not verbose then explain ();
35+
raise exn
3036

31-
let run ?max_domains ?allow_lwt ?avoid_threads ?fatal_exn_handler
37+
let run ?verbose ?max_domains ?allow_lwt ?avoid_threads ?fatal_exn_handler
3238
?(forbid = false) main =
3339
let computation = Computation.create ~mode:`LIFO () in
3440
let fiber = Fiber.create ~forbid computation in
3541
let main _ = Computation.capture computation main () in
36-
run_fiber ?max_domains ?allow_lwt ?avoid_threads ?fatal_exn_handler fiber main;
42+
run_fiber ?verbose ?max_domains ?allow_lwt ?avoid_threads ?fatal_exn_handler
43+
fiber main;
3744
Computation.await computation

test/lib/test_scheduler/test_scheduler.ocaml5.ml

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,28 @@ let init () =
2626
propagate ()
2727
end
2828

29-
let rec run_fiber ?(max_domains = 1) ?(allow_lwt = true)
29+
let explain scheduler ~quota ~n_domains =
30+
let quota =
31+
match scheduler with
32+
| `Fifos | `Multififos -> Printf.sprintf " ~quota:%d" quota
33+
| `Randos | `Thread | `Lwt -> ""
34+
in
35+
let n_domains =
36+
match scheduler with
37+
| `Multififos | `Randos -> Printf.sprintf " ~n_domains:%d" n_domains
38+
| `Fifos | `Thread | `Lwt -> ""
39+
in
40+
let scheduler =
41+
match scheduler with
42+
| `Fifos -> "fifos"
43+
| `Multififos -> "multififos"
44+
| `Randos -> "randos"
45+
| `Thread -> "thread"
46+
| `Lwt -> "lwt"
47+
in
48+
Printf.printf "Testing with scheduler: %s%s%s\n%!" scheduler quota n_domains
49+
50+
let rec run_fiber ?(verbose = false) ?(max_domains = 1) ?(allow_lwt = true)
3051
?(avoid_threads = false) ?fatal_exn_handler fiber main =
3152
init ();
3253
let scheduler =
@@ -80,26 +101,21 @@ let rec run_fiber ?(max_domains = 1) ?(allow_lwt = true)
80101
(fun () -> Picos_mux_thread.run_fiber ?fatal_exn_handler fiber main)
81102
with
82103
| None ->
83-
run_fiber ~max_domains ~allow_lwt ~avoid_threads ?fatal_exn_handler fiber
84-
main
104+
run_fiber ~verbose ~max_domains ~allow_lwt ~avoid_threads
105+
?fatal_exn_handler fiber main
85106
| Some run -> begin
107+
if verbose then explain scheduler ~quota ~n_domains;
86108
try run ()
87109
with exn ->
88-
Printf.printf "Test_scheduler: %s ~quota:%d ~n_domains:%d\n%!"
89-
(match scheduler with
90-
| `Fifos -> "fifos"
91-
| `Multififos -> "multififos"
92-
| `Randos -> "randos"
93-
| `Thread -> "thread"
94-
| `Lwt -> "lwt")
95-
quota n_domains;
110+
if not verbose then explain scheduler ~quota ~n_domains;
96111
raise exn
97112
end
98113

99-
let run ?max_domains ?allow_lwt ?avoid_threads ?fatal_exn_handler
114+
let run ?verbose ?max_domains ?allow_lwt ?avoid_threads ?fatal_exn_handler
100115
?(forbid = false) main =
101116
let computation = Computation.create ~mode:`LIFO () in
102117
let fiber = Fiber.create ~forbid computation in
103118
let main _ = Computation.capture computation main () in
104-
run_fiber ?max_domains ?allow_lwt ?avoid_threads ?fatal_exn_handler fiber main;
119+
run_fiber ?verbose ?max_domains ?allow_lwt ?avoid_threads ?fatal_exn_handler
120+
fiber main;
105121
Computation.await computation

test/test_schedulers.ml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,60 @@ let test_cross_scheduler_wakeup () =
181181
end
182182
done
183183

184+
let () =
185+
let max_domains = Random.int (Picos_domain.recommended_domain_count ()) + 1 in
186+
Test_scheduler.run ~verbose:true ~max_domains @@ fun () ->
187+
let n_fibers = 100 in
188+
let min_yields_per_fiber = 10_000 in
189+
let yields = Float.Array.make n_fibers 0.0 in
190+
begin
191+
let countdown = Atomic.make n_fibers in
192+
Flock.join_after @@ fun () ->
193+
for i = 0 to n_fibers - 1 do
194+
Flock.fork @@ fun () ->
195+
let counter = ref 0 in
196+
try
197+
while true do
198+
Control.yield ();
199+
incr counter;
200+
if !counter = min_yields_per_fiber then
201+
if 1 = Atomic.fetch_and_add countdown (-1) then Flock.terminate ()
202+
done
203+
with Control.Terminate ->
204+
Float.Array.set yields i (float !counter /. float min_yields_per_fiber)
205+
done
206+
end;
207+
let mean_of xs =
208+
Float.Array.fold_left ( +. ) 0.0 xs /. Float.of_int (Float.Array.length xs)
209+
in
210+
let sd_of xs ~mean =
211+
Float.sqrt
212+
(mean_of
213+
(Float.Array.map
214+
(fun v ->
215+
let d = v -. mean in
216+
d *. d)
217+
xs))
218+
in
219+
let median_of xs =
220+
Float.Array.sort Float.compare xs;
221+
let n = Float.Array.length xs in
222+
if n land 1 = 0 then
223+
(Float.Array.get xs ((n asr 1) - 1) +. Float.Array.get xs (n asr 1))
224+
/. 2.0
225+
else Float.Array.get xs (n asr 1)
226+
in
227+
let mean = mean_of yields in
228+
let median = median_of yields in
229+
let sd = sd_of yields ~mean in
230+
Printf.printf
231+
"Fairness of %d fibers performing at least %d yields:\n\
232+
\ sd: %f -- ideally 0\n\
233+
\ mean: %f -- ideally 1\n\
234+
\ median: %f -- ideally 1\n\
235+
%!"
236+
n_fibers min_yields_per_fiber sd mean median
237+
184238
let () =
185239
[
186240
("Trivial main returns", [ Alcotest.test_case "" `Quick test_returns ]);

0 commit comments

Comments
 (0)