Multicore Parallelism

Spawn few domains (≈ number of cores); they're expensive.

36.1 Domains — units of parallelism (OS threads, one per core)

Spawn few domains (≈ number of cores); they're expensive. Use a pool for fine-grained tasks.

let fib_par n =
  let rec fib n = if n < 2 then n else fib (n - 1) + fib (n - 2) in
  let d1 = Domain.spawn (fun () -> fib (n - 1)) in
  let d2 = Domain.spawn (fun () -> fib (n - 2)) in
  Domain.join d1 + Domain.join d2

let cores = Domain.recommended_domain_count ()

36.2 Atomics

let counter = Atomic.make 0
let () =
  let ds = List.init 4 (fun _ -> Domain.spawn (fun () ->
    for _ = 1 to 100_000 do Atomic.incr counter done)) in
  List.iter Domain.join ds;
  Printf.printf "%d\n" (Atomic.get counter)    (* 400000 *)

Atomic.get, Atomic.set, Atomic.exchange, Atomic.compare_and_set, Atomic.fetch_and_add

36.3 Mutex, Condition, Semaphore

let m = Mutex.create ()
let shared = ref []
let add x = Mutex.protect m (fun () -> shared := x :: !shared)   (* 5.1+ *)

let c = Condition.create ()
(* Condition.wait c m; Condition.signal c; Condition.broadcast c *)
Semaphore.Counting.make 3, Semaphore.Binary.make true

36.4 Domain-local storage

let key = Domain.DLS.new_key (fun () -> Random.State.make_self_init ())
let rand () = Random.State.int (Domain.DLS.get key) 100

36.5 Domainslib — task pools, parallel for

module T = Domainslib.Task

let parallel_sum arr =
  let pool = T.setup_pool ~num_domains:(Domain.recommended_domain_count () - 1) () in
  let result = T.run pool (fun () ->
    T.parallel_for_reduce pool ( + ) 0 ~start:0 ~finish:(Array.length arr - 1)
      ~body:(fun i -> arr.(i))) in
  T.teardown_pool pool;
  result

let rec pfib pool n =
  if n < 20 then (let rec f n = if n < 2 then n else f (n-1) + f (n-2) in f n)
  else
    let a = T.async pool (fun () -> pfib pool (n - 1)) in
    let b = pfib pool (n - 2) in
    T.await pool a + b

36.6 Memory model & data races

OCaml 5 has a bounded data-race memory model: racy programs don't crash or exhibit undefined behaviour like C; races are local in effect. Still, protect shared mutable state with Atomic/Mutex, or use lock-free structures from Saturn (saturn library: queues, stacks, hashtables).

36.7 Threads (systhreads)

The threads library still exists for concurrency within a domain (with a per-domain lock): Thread.create, Thread.join. Use for blocking I/O compatibility, not CPU parallelism.

36.8 Tools

ThreadSanitizer (TSan) support: opam switch create 5.x+tsan detects data races.

Olly / runtime_events: observe GC and domain events.