moonpool/src/fork_join.mli
2023-07-16 23:36:34 -04:00

109 lines
3.8 KiB
OCaml

(** Fork-join primitives.
{b NOTE} These are only available on OCaml 5.0 and above.
@since 0.3 *)
[@@@ifge 5.0]
val both : (unit -> 'a) -> (unit -> 'b) -> 'a * 'b
(** [both f g] runs [f()] and [g()], potentially in parallel,
and returns their result when both are done.
If any of [f()] and [g()] fails, then the whole computation fails.
This must be run from within the pool: for example, inside {!Pool.run}
or inside a {!Fut.spawn} computation.
This is because it relies on an effect handler to be installed.
@since 0.3
{b NOTE} this is only available on OCaml 5. *)
val both_ignore : (unit -> _) -> (unit -> _) -> unit
(** Same as [both f g |> ignore].
@since 0.3
{b NOTE} this is only available on OCaml 5. *)
val for_ : ?chunk_size:int -> int -> (int -> int -> unit) -> unit
(** [for_ n f] is the parallel version of [for i=0 to n-1 do f i done].
[f] is called with parameters [low] and [high] and must use them like so:
{[ for j = low to high do (* … actual work *) done ]}.
If [chunk_size=1] then [low=high] and the loop is not actually needed.
@param chunk_size controls the granularity of parallelism.
The default chunk size is not specified.
See {!all_array} or {!all_list} for more details.
Example:
{[
let total_sum = Atomic.make 0
let() = for_ ~chunk_size:5 100
(fun low high ->
(* iterate on the range sequentially. The range should have 5 items or less. *)
let local_sum = ref 0 in
for j=low to high do
local_sum := !local_sum + j
done;
ignore (Atomic.fetch_and_add total_sum !local_sum : int)))
let() = assert (Atomic.get total_sum = 4950)
]}
Note how we still compute a local sum sequentially in [(fun low high -> …)],
before combining it wholesale into [total_sum]. When the chunk size is large,
this can have a dramatic impact on the synchronization overhead.
When [chunk_size] is not provided, the library will attempt to guess a value
that keeps all cores busy but runs as few tasks as possible to reduce
the synchronization overhead.
Use [~chunk_size:1] if you explicitly want to
run each iteration of the loop in its own task.
@since 0.3
{b NOTE} this is only available on OCaml 5. *)
val all_array : ?chunk_size:int -> (unit -> 'a) array -> 'a array
(** [all_array fs] runs all functions in [fs] in tasks, and waits for
all the results.
@param chunk_size if equal to [n], groups items by [n] to be run in
a single task. Default is [1].
@since 0.3
{b NOTE} this is only available on OCaml 5. *)
val all_list : ?chunk_size:int -> (unit -> 'a) list -> 'a list
(** [all_list fs] runs all functions in [fs] in tasks, and waits for
all the results.
@param chunk_size if equal to [n], groups items by [n] to be run in
a single task. Default is not specified.
This parameter is available since 0.3.
@since 0.3
{b NOTE} this is only available on OCaml 5. *)
val all_init : ?chunk_size:int -> int -> (int -> 'a) -> 'a list
(** [all_init n f] runs functions [f 0], [f 1], … [f (n-1)] in tasks, and waits for
all the results.
@param chunk_size if equal to [n], groups items by [n] to be run in
a single task. Default is not specified.
This parameter is available since 0.3.
@since 0.3
{b NOTE} this is only available on OCaml 5. *)
val map_array : ?chunk_size:int -> ('a -> 'b) -> 'a array -> 'b array
(** [map_array f arr] is like [Array.map f arr], but runs in parallel.
@since 0.3
{b NOTE} this is only available on OCaml 5. *)
val map_list : ?chunk_size:int -> ('a -> 'b) -> 'a list -> 'b list
(** [map_list f l] is like [List.map f l], but runs in parallel.
@since 0.3
{b NOTE} this is only available on OCaml 5. *)
[@@@endif]