
A deep dive into Multicore OCaml garbage collector - melqdusy
http://kcsrk.info/multicore/gc/2017/07/06/multicore-ocaml-gc/
======
rixed
I never managed to understand why so many people were apparently longering for
multicore support. I just can't believe many are impatiently waiting for this
feature to start using the ocaml language for some projects requiring
multicore support, and I became impatient myself just to see what those
projects are.

In recent years where servers are to scale to many machines I found myself
using less and less kennel threads. Also it seams GUIs are mostly web theses
days. Most remaining fields I can imagine where you would want kennel threads,
ocaml would not fit anyway because of GC and lack of control over memory
layout.

So I really wonder...

~~~
lobster_johnson
In Go you can just do "go doWork()" and the scheduler runs the function as a
coroutine on some kernel thread, thus allowing your program to make use of all
cores.

The ability to just throw goroutines at the scheduler changes how you approach
writing programs. For example, say you want to process an input that consists
of lots of individual records. You can run the input stream in one goroutine,
then spawn a bunch of worker goroutines that each receives records on a
channel for processing. Since Go uses real threads and doesn't have a global
lock, you can get nearly linear improvement in throughout here.

You wouldn't do this in, say, Ruby or Python, where the concurrency situation
is about the same as in OCaml. Ruby has threads, like OCaml, but their
slowness means they're not really usable in the same way as goroutines.

Parallelizing apps by forking child processes and communicating input and
results via pipes is something I've done a lot in Ruby, and it's a really
awkward, heavy-handed concurrency model.

~~~
JacobiX
In OCaml there is Lwt. It provides very light-weight cooperative threads. The
context switches are very fast and composing cooperative threads allows the
writing of highly asynchronous applications. For instance, in the versions of
Go prior to 1.5 goroutines used to run on a single core. And as of 1.5 the
variable GOMAXPROCS can be set to the number of cores. EDIT:clarify the
difference between concurrency and parallelism.

~~~
lobster_johnson
Lwt looks nice, but also seems a lot more intrusive to me than, say, Go. It's
based on promises (so it's very explicit), and if I remember correctly, in
order to do any I/O you have to use the Lwt mechanisms instead of the standard
ones (which block).

How does Lwt interact with third-party libs that aren't written to use Lwt? I
know from Ruby that libevent-type stuff such as Eventmachine doesn't combine
all that well with non-async code.

Go made a good call in making all I/O blocking. That means code is easy to
read and write. It comes with downsides, such as that you _have_ to use
goroutines, but for most developers it's a more productive concurrency model.
(Unfortunately there's no alternative; you can build a blocking system out of
a non-blocking one with no overhead, but the opposite is not true. When I
first started with Go I was surprised that you can't "select{}" on a file
descriptor or socket, or indeed any custom implementation, in the same way
that "range" is magical and only works on built-in types.)

~~~
kcsrk
With algebraic effect handlers (the concurrency story of multicore OCaml), you
can write your I/O code in direct-style, but retain the advantage of non-
blocking I/O. As such, you have the advantage of goroutines, but this is also
an opt-in. The asynchronous I/O is implemented as a library and is not baked
into the language, and hence you can write your own I/O library. For more
details, see our recent draft paper:
[http://kcsrk.info/papers/system_effects_may_17.pdf](http://kcsrk.info/papers/system_effects_may_17.pdf).

------
axaxs
OCaml has always intrigued me as something to learn, but it seems I always
read equally as many reasons that it's not good, not ready, or why I should
try Haskell instead. Does anyone have experience to say yay/nay on worthwhile
learning, know of any companies using it heavily, or know of any large cleany
written codebases from which to study?

~~~
flunhat
IMO the best part about OCaml is that it's just so _fun_ to write code in. The
type system is a million times more expressive and elegant than anything the
world of Java/C# offers and more rigorous to boot. And unlike Haskell, you can
cheat a little and write imperative code if you really need to. The library
ecosystem could be better with OCaml though, but this is a chicken-egg problem
and not an indictment of the language itself.

~~~
vog
_> you can cheat a little and write imperative code if you really need to_

I'm not sure if this is cheating.

Yes, this can be misused to introduce side-effects where there should be none.

However, the strict evaluation by default (and lazy evaluation only when
explicitly marked) has a huge benefit that you can not only reason easily
about correctness (as in Haskell), but also reason easily about performance.
The latter you can't do in Haskell without huge assumptions about the
optimizer. In that regard, Haskell is similar to SQL: For small SQL queries it
is pretty clear how it will be executed, but for large SQL queries it isn't
clear at all which route the query planner will choose.

Having said that, reasoning about performance is even better in Rust, which is
heavily inspired by OCaml but allows to to reason about memory access, closing
the gap that you can reason about OCaml's performance only while ignoring the
GC.

~~~
mercurial
> Having said that, reasoning about performance is even better in Rust, which
> is heavily inspired by OCaml but allows to to reason about memory access,
> closing the gap that you can reason about OCaml's performance only while
> ignoring the GC.

Rust is a really different topic. It's an imperative low-level language with
functional trappings, not the reverse. First off, "reasoning about memory
access" really means "think about what part of code owns which data", which is
non-trivial, especially considering the various ways for sharing data in Rust.

Secondly, trying to do idiomatic OCaml in Rust is a terrible idea. Idiomatic
OCaml is all about immutability and recursion. Immutability means allocation,
and idiomatic Rust is pretty much all about avoiding allocations. And
recursion is also a bad idea, especially considering Rust does not have TCO.
It's not a dig at Rust, which is an exceedingly interesting language of its
own, but Rust and OCaml have completely different philosophies.

~~~
saosebastiao
Idiomatic rust avoids _heap_ allocations, but it very much encourages
immutable stack allocation...something that OCaml also tries to encourage.

Rust started out _a lot_ like OCaml, and gradually moved away from it whenever
they needed to make a compromise to improve its position as a systems
language. You start to see it with big things like how the pattern matcher
works, but you also notice small things like idiomatic capitalization
(lower_snake_case for functions, UpperCamelCase for types). The original
compiler was even written in OCaml. The language borrowed a lot from OCaml and
the ML subculture is still really strong on the core development team. That
shows through enough to persuade ML-inclined engineers to use it even when
they don't need a systems language. I'd say there's a really strong reason the
comparison still exists even if they've moved apart over time.

~~~
rbehrends
> Idiomatic rust avoids heap allocations, but it very much encourages
> immutable stack allocation...something that OCaml also tries to encourage.

I'm not sure I'm following you here; one of the more common criticisms of
OCaml is generally that it (unavoidably) puts too many values on the heap. I
mean, even floats get boxed by default, unless the compiler can avoid that.

~~~
CyberDildonics
> even floats get boxed by default, unless the compiler can avoid that.

Yikes, that seems like it would destroy performance.

~~~
rbehrends
It's one of OCaml's biggest weaknesses, but it's also not as bad as it sounds.

One, records and arrays of floats don't suffer an additional level of
indirection.

Two, the compiler is actually pretty good at unboxing floats these days.
Arguments of non-inlined functions are generally the major reason for boxing
to happen (because of ABI requirements). Inner loops should generally be free
of unnecessary boxing.

Three, even when boxing happens, keep in mind that OCaml uses a very fast bump
allocator (which is also inlined). This means that heap allocation of short-
lived values is more like alloca() than malloc().

OCaml is still not the language that you'd want to write performance-sensitive
numeric code in, but it's generally fine when floating point math is only one
aspect of your code or when you're using it as a way to interface with
C/Fortran libraries (such as with Owl [1]), sort of like how Python uses
NumPy.

[1] [https://github.com/ryanrhymes/owl](https://github.com/ryanrhymes/owl)

------
amelius
I'm guessing a lot of other languages could benefit from this. So what I'd
like to see is a runtime environment and garbage collector decoupled from the
actual language. Something like the LLVM project, except focused on the
runtime system (and different variants) rather than code generation.

~~~
kcsrk
There is already one! See Malfunction [0,1]. There is an Idris and Agda
backend for malfunction.

[0]:
[https://github.com/stedolan/malfunction](https://github.com/stedolan/malfunction)

[1]:
[https://www.cl.cam.ac.uk/~sd601/papers/malfunction.pdf](https://www.cl.cam.ac.uk/~sd601/papers/malfunction.pdf)

------
lobster_johnson
So what's the status of Multicore OCaml? Will it be merged into the mainline
any time soon?

