I would like to run a non-deterministic task in parallel, use the result of the first task that returns. Could anyone point me to how to do that in Multicore OCaml?
Rough sketch using GitHub - ocaml-multicore/eio: Effects-based direct-style IO for multicore OCaml : [EDIT: debugged and simplified]
(* app.ml *)
let num_domains = Domain.recommended_domain_count ()
let per_domain = 10
open Eio
let par ~new_domain f x =
let mailbox = Stream.create 1 in
let fiber _ () = Stream.add mailbox (f x) in
let domain _ () =
new_domain (fun () -> fiber |> List.init per_domain |> Fiber.any)
in
domain |> List.init num_domains |> Fiber.any;
Stream.take mailbox
(* Mock of CPU-intensive computation *)
let task clock () =
Time.sleep clock 10.;
1.
let () =
Eio_main.run @@ fun env ->
let dmgr = Stdenv.domain_mgr env in
let clock = Stdenv.clock env in
let res = par ~new_domain:(Domain_manager.run dmgr) (task clock) () in
traceln "Result: %f" res
Run with: time dune exec ./app.exe
. This should show a result like:
+Result: 1.000000
dune exec ./app.exe 0.08s user 0.02s system 0% cpu 10.402 total
Ie it stops almost as soon as the first fiber produces a result.
Although I could’ve sworn I saw a parallel reduce in Eio somewhere, I can’t seem to find it now.
If you use Miou, you can simply use Miou.call
to launch in parallel a task and Miou.await_first
to await the first resolved task and cancel others like:
let task () = ...
let () = Miou.run @@ fun () ->
let prms = List.init n (fun _ -> Miou.call task) in
match Miou.await_first prms with
| Ok value -> ...
| Error exn -> raise exn
I think domainslib
also has a parallel_find
function that should do this
I decided to go with Miou. My intuition was that n
would be the number of parallel called threads, but I see 16 threads on Activity Monitor when the code is running. I am inexperienced in working with parallel code, so I might be misunderstanding something. Is n
different from the number of spawned threads?
Caveat: I don’t know much about Miou (although it looks cool and simple), but n
seems to be the number of ‘tasks’, not threads. Tasks seem to be Miou’s equivalent of Eio’s fibers. Miou automatically starts a domain (ie thread) pool with a limited number of domains, then multiplexes the tasks between them: Miou (miou.Miou).
The reason for a limited number of domains is because domains are heavyweight and expensive. Tasks/fibers which run concurrently on each domain are lightweight and cheap. So we always want to run a small, fixed number of domains, and a large number of tasks/fibers multiplexed on top of them.
It’s similar to what my example above is doing explicitly (btw, I’ve updated and simplified my example).