Share
Embed
Multicore OCaml - What's coming in 2021
KC Sivaramakrishnan
December 08, 2020
Transcript
101 slides
-
Multicore OCam l What’s coming in 2021 “KC” Sivaramakrishnan and
Anil Madhavapeddy OCam -
• Adds native support for concurrency and parallelism to OCaml
Multicore OCaml -
• Adds native support for concurrency and parallelism to OCaml
Multicore OCaml Overlapped execution A B A C B Time -
• Adds native support for concurrency and parallelism to OCaml
Multicore OCaml Overlapped execution A B A C B Time Simultaneous execution A B C Time -
• Adds native support for concurrency and parallelism to OCaml
Multicore OCaml Overlapped execution A B A C B Time Simultaneous execution A B C Time Effect Handlers -
• Adds native support for concurrency and parallelism to OCaml
Multicore OCaml Overlapped execution A B A C B Time Simultaneous execution A B C Time Effect Handlers Domains -
Challenges • Millions of lines of legacy cod e ✦
Written without concurrency and parallelism in min d ✦ Cost of refactoring sequential code itself is prohibitive -
Challenges • Millions of lines of legacy cod e ✦
Written without concurrency and parallelism in min d ✦ Cost of refactoring sequential code itself is prohibitive • Low-latency and predictable performanc e ✦ Great for applications that require ~10ms latency -
Challenges • Millions of lines of legacy cod e ✦
Written without concurrency and parallelism in min d ✦ Cost of refactoring sequential code itself is prohibitive • Low-latency and predictable performanc e ✦ Great for applications that require ~10ms latency • Excellent compatibility with debugging and pro f i ling tool s ✦ gdb, lldb, perf, libunwind, etc. -
Challenges • Millions of lines of legacy cod e ✦
Written without concurrency and parallelism in min d ✦ Cost of refactoring sequential code itself is prohibitive • Low-latency and predictable performanc e ✦ Great for applications that require ~10ms latency • Excellent compatibility with debugging and pro f i ling tool s ✦ gdb, lldb, perf, libunwind, etc. Backwards compatibility before scalability -
Desiderata • Feature backwards compatibilit y ✦ Do not break
existing code • Performance backwards compatibilit y ✦ Existing programs run just as fast using just the same memory -
Desiderata • Feature backwards compatibilit y ✦ Do not break
existing code • Performance backwards compatibilit y ✦ Existing programs run just as fast using just the same memory • GC Latency before multicore scalability -
Desiderata • Feature backwards compatibilit y ✦ Do not break
existing code • Performance backwards compatibilit y ✦ Existing programs run just as fast using just the same memory • GC Latency before multicore scalability • Compatibility with program inspection tools -
Desiderata • Feature backwards compatibilit y ✦ Do not break
existing code • Performance backwards compatibilit y ✦ Existing programs run just as fast using just the same memory • GC Latency before multicore scalability • Compatibility with program inspection tools • Performant concurrent and parallel programming abstractions -
Rest of the talk • Domains for shared memory parallelis
m • Effect handlers for concurrent programming -
Domains for Parallelism • A unit of parallelism • Heavyweight
— maps onto a OS threa d ✦ Recommended to have 1 domain per core -
Domains for Parallelism • A unit of parallelism • Heavyweight
— maps onto a OS threa d ✦ Recommended to have 1 domain per core • Low-level domain AP I ✦ Spawn & join, wait & notif y ✦ Domain-local storag e ✦ Atomic memory operation s ✤ Dolan et al, “Bounding Data Races in Space and Time”, PLDI’18 -
Domains for Parallelism • A unit of parallelism • Heavyweight
— maps onto a OS threa d ✦ Recommended to have 1 domain per core • Low-level domain AP I ✦ Spawn & join, wait & notif y ✦ Domain-local storag e ✦ Atomic memory operation s ✤ Dolan et al, “Bounding Data Races in Space and Time”, PLDI’18 • No restrictions on sharing objects between domain s ✦ But how does it work? -
Incremental and non-moving Stock OCaml GC • A generational, non-moving,
incremental, mark-and-sweep GC Minor Heap Major Heap • Small (2 MB default ) • Bump pointer allocatio n • Survivors copied to major heap -
Incremental and non-moving Stock OCaml GC • A generational, non-moving,
incremental, mark-and-sweep GC Minor Heap Major Heap • Small (2 MB default ) • Bump pointer allocatio n • Survivors copied to major heap Mutator Start of major cycle Idle -
Incremental and non-moving Stock OCaml GC • A generational, non-moving,
incremental, mark-and-sweep GC Minor Heap Major Heap • Small (2 MB default ) • Bump pointer allocatio n • Survivors copied to major heap Mutator Start of major cycle Idle Mark Roots mark roots -
Mark mark main Incremental and non-moving Stock OCaml GC •
A generational, non-moving, incremental, mark-and-sweep GC Minor Heap Major Heap • Small (2 MB default ) • Bump pointer allocatio n • Survivors copied to major heap Mutator Start of major cycle Idle Mark Roots mark roots -
Mark mark main Sweep sweep Incremental and non-moving Stock OCaml
GC • A generational, non-moving, incremental, mark-and-sweep GC Minor Heap Major Heap • Small (2 MB default ) • Bump pointer allocatio n • Survivors copied to major heap Mutator Start of major cycle Idle Mark Roots mark roots -
Mark mark main Sweep sweep Incremental and non-moving Stock OCaml
GC • A generational, non-moving, incremental, mark-and-sweep GC Minor Heap Major Heap • Small (2 MB default ) • Bump pointer allocatio n • Survivors copied to major heap End of major cycle Mutator Start of major cycle Idle Mark Roots mark roots -
Mark mark main Sweep sweep Incremental and non-moving Stock OCaml
GC • A generational, non-moving, incremental, mark-and-sweep GC Minor Heap Major Heap • Small (2 MB default ) • Bump pointer allocatio n • Survivors copied to major heap End of major cycle Mutator Start of major cycle Idle Mark Roots mark roots • Fast allocations -
Mark mark main Sweep sweep Incremental and non-moving Stock OCaml
GC • A generational, non-moving, incremental, mark-and-sweep GC Minor Heap Major Heap • Small (2 MB default ) • Bump pointer allocatio n • Survivors copied to major heap End of major cycle Mutator Start of major cycle Idle Mark Roots mark roots • Fast allocations • Max GC latency < 10 ms, 99th percentile latency < 1 ms -
Free Multicore OCaml GC Major Heap Dom 0 Dom 0
Dom 1 Dom 0 Dom 1 Domain 0 allocation pointer Domain 1 allocation pointer Minor Heap -
Free Multicore OCaml GC • Stop-the-world parallel minor collection for
minor hea p ✦ 2 global barriers / minor g c ✦ On 24 cores, ~10 ms pauses Major Heap Dom 0 Dom 0 Dom 1 Dom 0 Dom 1 Domain 0 allocation pointer Domain 1 allocation pointer Minor Heap -
Multicore OCaml GC • Mostly-concurrent mark-and-sweep for major collectio n
✦ All the marking and sweeping work done without synchronizatio n ✦ 3 barriers per cycle (worst case) to agree end of GC phase s ✤ 2 barriers for the two kinds of f i nalisers in OCam l ✦ ~5 ms pauses on 24 cores Sweep Mark Mark Roots Mutator Sweep Mark Mark Roots Start of major cycle End of major cycle mark and sweep phases may overlap Domain 0 Domain 1 -
Sequential performance coq irmin menhir alt-ergo • ~1% faster than
stock (geomean of normalised running times ) ✦ Difference under measurement noise mostl y ✦ Outliers due to difference in allocators -
Domainslib for parallel programming • Domain API exposed by the
compiler is too low-level -
Domainslib for parallel programming • Domain API exposed by the
compiler is too low-level • Domainslib - https://github.com/ocaml-multicore/domainslib Domain 0 Domain N … Task Pool Async/Await Parallel for Domainslib -
Domainslib for parallel programming • Domain API exposed by the
compiler is too low-level • Domainslib - https://github.com/ocaml-multicore/domainslib Domain 0 Domain N … Task Pool Async/Await Parallel for Domainslib Let’s look at examples! -
Recursive Fibonacci - Sequential let rec fib n = if
n < 2 then 1 else fib (n-1) + fib (n-2) -
Recursive Fibonacci - Parallel let fib n = let pool
= T.setup_pool ~num_domains:(num_domains - 1) in let res = fib_par pool n in T.teardown_pool pool; res module T = Domainslib.Task -
Recursive Fibonacci - Parallel let fib n = let pool
= T.setup_pool ~num_domains:(num_domains - 1) in let res = fib_par pool n in T.teardown_pool pool; res let rec fib_par pool n = if n <= 40 then fib_seq n else let a = T.async pool (fun _ -> fib_par pool (n-1)) in let b = T.async pool (fun _ -> fib_par pool (n-2)) in T.await pool a + T.await pool b module T = Domainslib.Task -
Recursive Fibonacci - Parallel let rec fib_seq n = if
n < 2 then 1 else fib_seq (n-1) + fib_seq (n-2) let fib n = let pool = T.setup_pool ~num_domains:(num_domains - 1) in let res = fib_par pool n in T.teardown_pool pool; res let rec fib_par pool n = if n <= 40 then fib_seq n else let a = T.async pool (fun _ -> fib_par pool (n-1)) in let b = T.async pool (fun _ -> fib_par pool (n-2)) in T.await pool a + T.await pool b module T = Domainslib.Task -
Performance: f i b(48) Cores Time (Seconds) Vs Serial Vs
Self 1 37.787 0.98 1 2 19.034 1.94 1.99 4 9.723 3.8 3.89 8 5.023 7.36 7.52 16 2.914 12.68 12.97 24 2.201 16.79 17.17 -
Conway’s Game of Life let next () = ... for
x = 0 to board_size - 1 do for y = 0 to board_size - 1 do next_board.(x).(y) <- next_cell cur_board x y done done; ... -
Conway’s Game of Life let next () = ... for
x = 0 to board_size - 1 do for y = 0 to board_size - 1 do next_board.(x).(y) <- next_cell cur_board x y done done; ... let next () = ... T.parallel_for pool ~start:0 ~finish:(board_size - 1) ~body:(fun x -> for y = 0 to board_size - 1 do next_board.(x).(y) <- next_cell cur_board x y done); ... -
Performance: Game of Life Cores Time (Seconds) Vs Serial Vs
Self 1 24.326 1 1 2 12.290 1.980 1.98 4 6.260 3.890 3.89 8 3.238 7.51 7.51 16 1.726 14.09 14.09 24 1.212 20.07 20.07 Board size = 1024, Iterations = 512 -
Parallelism is not Concurrency Parallelism is a performance hack whereas
concurrency is a program structuring mechanism -
Parallelism is not Concurrency • Lwt and Async - concurrent
programming libraries in OCam l ✦ Callback-oriented programming with nicer syntax Parallelism is a performance hack whereas concurrency is a program structuring mechanism -
Parallelism is not Concurrency • Lwt and Async - concurrent
programming libraries in OCam l ✦ Callback-oriented programming with nicer syntax • Suffers many pitfalls of callback-oriented programmin g ✦ No backtraces, exceptions can’t be used, monadic syntax Parallelism is a performance hack whereas concurrency is a program structuring mechanism -
Parallelism is not Concurrency • Lwt and Async - concurrent
programming libraries in OCam l ✦ Callback-oriented programming with nicer syntax • Suffers many pitfalls of callback-oriented programmin g ✦ No backtraces, exceptions can’t be used, monadic syntax • Go (goroutines) and GHC Haskell (threads) have better abstractions — lightweight threads Parallelism is a performance hack whereas concurrency is a program structuring mechanism -
Parallelism is not Concurrency • Lwt and Async - concurrent
programming libraries in OCam l ✦ Callback-oriented programming with nicer syntax • Suffers many pitfalls of callback-oriented programmin g ✦ No backtraces, exceptions can’t be used, monadic syntax • Go (goroutines) and GHC Haskell (threads) have better abstractions — lightweight threads Parallelism is a performance hack whereas concurrency is a program structuring mechanism Should we add lightweight threads to OCaml? -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines • Effect declaration separate from interpretation (c.f. exceptions) -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines • Effect declaration separate from interpretation (c.f. exceptions) effect E : string let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines • Effect declaration separate from interpretation (c.f. exceptions) effect E : string let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " effect declaration -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines • Effect declaration separate from interpretation (c.f. exceptions) effect E : string let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " computation effect declaration -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines • Effect declaration separate from interpretation (c.f. exceptions) effect E : string let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " computation handler effect declaration -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines • Effect declaration separate from interpretation (c.f. exceptions) effect E : string let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " computation handler suspends current computation effect declaration -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines • Effect declaration separate from interpretation (c.f. exceptions) effect E : string let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " computation handler delimited continuation suspends current computation effect declaration -
Effect Handlers • A mechanism for programming with user-de f
i ned effects • Modular basis of non-local control- f l ow mechanism s ✦ Exceptions, generators, lightweight threads, promises, asynchronous IO, coroutines • Effect declaration separate from interpretation (c.f. exceptions) effect E : string let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " computation handler delimited continuation suspends current computation resume suspended computation effect declaration -
Stepping through the example effect E : string let comp
() = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp -
Stepping through the example effect E : string let comp
() = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp -
comp Stepping through the example effect E : string let
comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp parent Fiber: A piece of stack + effect handler -
comp comp Stepping through the example effect E : string
let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp parent 0 -
comp comp Stepping through the example effect E : string
let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k 0 -
comp comp Stepping through the example effect E : string
let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k 0 -
comp comp Stepping through the example effect E : string
let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k 0 -
comp comp Stepping through the example effect E : string
let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k 0 1 -
comp comp Stepping through the example effect E : string
let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k 0 1 -
comp comp Stepping through the example effect E : string
let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k parent 0 1 -
comp comp Stepping through the example effect E : string
let comp () = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k parent 0 1 2 -
Stepping through the example effect E : string let comp
() = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k 0 1 2 3 -
Stepping through the example effect E : string let comp
() = print_string "0 "; print_string (perform E); print_string "3 " let main () = try comp () with effect E k -> print_string "1 "; continue k "2 "; print_string “4 " pc main sp k 0 1 2 3 4 -
Lightweight Threading effect Fork : (unit -> unit) -> unit
effect Yield : unit -
Lightweight Threading effect Fork : (unit -> unit) -> unit
effect Yield : unit let run main = ... (* assume queue of continuations *) let run_next () = match dequeue () with | Some k -> continue k () | None -> () in let rec spawn f = match f () with | () -> run_next () | effect Yield k -> enqueue k; run_next () | effect (Fork f) k -> enqueue k; spawn f in spawn main -
Lightweight Threading effect Fork : (unit -> unit) -> unit
effect Yield : unit let run main = ... (* assume queue of continuations *) let run_next () = match dequeue () with | Some k -> continue k () | None -> () in let rec spawn f = match f () with | () -> run_next () | effect Yield k -> enqueue k; run_next () | effect (Fork f) k -> enqueue k; spawn f in spawn main let fork f = perform (Fork f) let yield () = perform Yield -
Lightweight threading let main () = fork (fun _ ->
print_endline "1.a"; yield (); print_endline "1.b"); fork (fun _ -> print_endline "2.a"; yield (); print_endline “2.b") ;; run main -
Lightweight threading let main () = fork (fun _ ->
print_endline "1.a"; yield (); print_endline "1.b"); fork (fun _ -> print_endline "2.a"; yield (); print_endline “2.b") ;; run main 1.a 2.a 1.b 2.b -
Lightweight threading let main () = fork (fun _ ->
print_endline "1.a"; yield (); print_endline "1.b"); fork (fun _ -> print_endline "2.a"; yield (); print_endline “2.b") ;; run main 1.a 2.a 1.b 2.b • Direct-style (no monads) • User-code need not be aware of effects -
Generators • Generators — non-continuous traversal of data structure by
yielding value s ✦ Primitives in JavaScript and Pytho n ✦ Can be derived automatically from iterator using effect handlers -
Generators • Generators — non-continuous traversal of data structure by
yielding value s ✦ Primitives in JavaScript and Pytho n ✦ Can be derived automatically from iterator using effect handlers • Task — traverse a complete binary-tree of depth 2 5 ✦ 226 stack switches -
Generators • Generators — non-continuous traversal of data structure by
yielding value s ✦ Primitives in JavaScript and Pytho n ✦ Can be derived automatically from iterator using effect handlers • Task — traverse a complete binary-tree of depth 2 5 ✦ 226 stack switches • Iterator — idiomatic recursive traversal -
Generators • Generators — non-continuous traversal of data structure by
yielding value s ✦ Primitives in JavaScript and Pytho n ✦ Can be derived automatically from iterator using effect handlers • Task — traverse a complete binary-tree of depth 2 5 ✦ 226 stack switches • Iterator — idiomatic recursive traversal • Generato r ✦ Hand-written generator (hw-generator ) ✤ CPS translation + defunctionalization to remove intermediate closure allocatio n ✦ Generator using effect handlers (eh-generator) -
Performance: Generators Variant Time (milliseconds) Iterator (baseline) 202 hw-generator 837
(3.76x) eh-generator 1879 (9.30x) Multicore OCaml -
Performance: Generators Variant Time (milliseconds) Iterator (baseline) 202 hw-generator 837
(3.76x) eh-generator 1879 (9.30x) Multicore OCaml Variant Time (milliseconds) Iterator (baseline) 492 generator 43842 (89.1x) nodejs 14.07 -
Performance: WebServer • Effect handlers for asynchronous I/O in direct-styl
e ✦ https://github.com/kayceesrk/ocaml-aeio/ • Variant s ✦ Go + net/http (GOMAXPROCS=1 ) ✦ OCaml + http/af + Lwt (explicit callbacks ) ✦ OCaml + http/af + Effect handlers (MC ) • Performance measured using wrk2 -
Performance: WebServer • Effect handlers for asynchronous I/O in direct-styl
e ✦ https://github.com/kayceesrk/ocaml-aeio/ • Variant s ✦ Go + net/http (GOMAXPROCS=1 ) ✦ OCaml + http/af + Lwt (explicit callbacks ) ✦ OCaml + http/af + Effect handlers (MC ) • Performance measured using wrk2 -
Performance: WebServer • Effect handlers for asynchronous I/O in direct-styl
e ✦ https://github.com/kayceesrk/ocaml-aeio/ • Variant s ✦ Go + net/http (GOMAXPROCS=1 ) ✦ OCaml + http/af + Lwt (explicit callbacks ) ✦ OCaml + http/af + Effect handlers (MC ) • Performance measured using wrk2 • Direct style (no monadic syntax) -
Upstreaming Plan 1. Domains-only multicore to be upstreamed f i
rst 2. Runtime support for effect handler s • No effect syntax but all the compiler and runtime bits in -
Upstreaming Plan 1. Domains-only multicore to be upstreamed f i
rst 2. Runtime support for effect handler s • No effect syntax but all the compiler and runtime bits in 3. Effect syste m a. Track user-de f i ned effects in the typ e b. Track ambinet effects (ref, IO) in the typ e c. OCaml becomes a pure language (in the Haskell sense). -
Upstreaming Plan 1. Domains-only multicore to be upstreamed f i
rst 2. Runtime support for effect handler s • No effect syntax but all the compiler and runtime bits in 3. Effect syste m a. Track user-de f i ned effects in the typ e b. Track ambinet effects (ref, IO) in the typ e c. OCaml becomes a pure language (in the Haskell sense). let foo () = print_string "hello, world" val foo : unit -[ io ]-> unit Syntax is still in the works -
Multicore OCaml + Tezos • Thanks to Tezos Foundation for
funding Multicore OCaml development! -
Multicore OCaml + Tezos • Thanks to Tezos Foundation for
funding Multicore OCaml development! • Multicore + Tezo s ✦ Parallel Lwt preemptive tasks ✦ Direct-style asynchronous IO librar y ✤ Bridge the gap between Async and Lw t ✦ Parallelising Irmin (storage layer of Tezos) -
Multicore OCaml + Tezos • Thanks to Tezos Foundation for
funding Multicore OCaml development! • Multicore + Tezo s ✦ Parallel Lwt preemptive tasks ✦ Direct-style asynchronous IO librar y ✤ Bridge the gap between Async and Lw t ✦ Parallelising Irmin (storage layer of Tezos) • An end-to-end Multicore Tezos demonstrator (mid-2021) -
Thanks! • Multicore OCaml — https://github.com/ocaml-multicore/ocaml- multicore • Effects Examples
— https://github.com/ocaml-multicore/effects- examples • Sivaramakrishnan et al, “Retro f i tting Parallelism onto OCaml", ICFP 2020 • Dolan et al, “Concurrent System Programming with Effect Handlers”, TFP 2017 $ opam switch create 4.10.0+multicore \ --packages=ocaml-variants.4.10.0+multicore \ --repositories=multicore=git+https://github.com/ocaml-multicore/multicore-opam.git,default Install Multicore OCaml