Analog of Promise.any() for Multicore OCaml

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?

1 Like

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.

2 Likes

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
4 Likes

I think domainslib also has a parallel_find function that should do this

1 Like

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).

1 Like