It is indeed that it is hard to retrofit parallelism on to an existing language while trying to retain backwards compatibility _and_ performance.
Backwards compatibility is tricky because there's lots of C code using the C API.
Performance is hard because OCaml users are used to well performing code, with low and predictable pause times from the GC (<10ms).
The community is small and it seems like there wasn't appetite for maintaining two distinct runtimes with very different performance characteristics.
The current implementation for multicore GC (https://github.com/ocaml-multicore/ocaml-multicore) is reasonably close to upstream in performance on single threaded code and yet will scale up to multiple threads. It requires a change to the C API though.
There's a modified multicore GC (https://github.com/ctk21/ocaml-multicore/tree/stw_minor_gc) that doesn't require the C API change and we're currently writing up a paper that contrasts the two wit a fairly substantial amount of benchmarking (https://github.com/ocamllabs/sandmark).
Happy to answer any questions.
I would expect that low pause times are easy enough (or relatively easy considering the difficult domain of GC optimization), but keeping the allocations cheap is (one of) the key constraints, no?
In multicore's case it's about keeping low pause times while also keeping allocation cheap _and_ maintaining throughput.
I don't really understand why not. Can you expand on it? Doesn't the JVM for example do thread-local allocations in a conventional shared-memory parallel environment?
To avoid frequent synchronisation we could have gone with a (potentially generational) non-moving collector for the minor GC which would still have preserved the C API, probably allowed for very low pauses but would have made allocation more expensive.
I am very excited to use OCaml with multicore abilities but lately I realised that I have no clue how will the initial support even look like in terms of a programming API.
EDIT: Downvoters, I'm genuinely interested in hearing from you. Did I accidentally cause offense or am I mistaken?
Aonix, PTC, Aicas, IBM have been selling real time JVMs for embedded OSes and bare metal deployments for quite several years, before Azul came into the picture with their pause-less GC for multi-GB heaps (which still does minimal pauses anyway).
So recently is more the addition of Azul's ideas into Shenandoah, ZGC in OpenJDK, and Metronome in OpenJ9 as free beer GC for the Java community.
I don't know about Go GC but your comment about Haskell is very off.
> Not sure how Haskell's GC works, but I'm guessing it more or less depends on Haskell's immutability guarantees.
This is false. Just because you see immutability on the surface doesn't mean the GC still sees a lot of immutability.
Several reasons. The first is that even pure code can depend on interior mutability in a safe way using the ST monad, or in an unsafe way using the IO monad. (The GC can't just say, I give up when a user uses an unsafe function.) You might think that interior mutability is rarely used, but that'd still be wrong. Even something as commonplace as a hash map (from unordered-containers) uses interior mutability under the hood. Just read the source code and you'll see it. Here: http://hackage.haskell.org/package/unordered-containers-0.2.... Just grep for runST.
The bigger problem is laziness. How is laziness implemented? A value is represented as an unevaluated thunk, a closure to compute the value. When the value is needed, the closure is run and then replaced by the resulting data. This is mutability. And this is something that almost every Haskell program relies on.
I've explained this many times on HN. See eg https://news.ycombinator.com/item?id=21674749
ones = 1:ones powersOf2 = 0 : scanl (+) 1 (tail powersOf2)
fibonacci = 0 : scanl (+) 1 fibonacci
As I have already explained, immutable objects in Haskell often use laziness. And laziness requires mutability.Tangent: does GHC ever decide that it's cheaper to just potentially evaluate something twice than to use thunks?
That doesn't make it 'not a garbage collector' and it also doesn't make it 'not concurrent'.
> tuning may be required at the extremes
that is very rare, and if that is disqualifying for a concurrent GC, then Java is disqualified as a concurrent GC.
That seems like an over burdensome requirement given that we are swimming in cores. A 10% loss in single core performance vs 1.2x-Nx multicore performance increase would be an adequate tradeoff.
The only other option I can think of is having two different backends, one that is entirely single threaded and doesn't incur any multicore performance penalty.
As soon as you get parallel gc, MPMC queues, parallel sorting, set intersection, etc. Many programs will see about a 1.x speed up making the point moot.
Erlang went through this same transition.
If OCaml wants to remain somewhat viable it will need to be able to use more than one core. The most users of OCaml is always right now. This won't always be the case if they cannot go multicore.
Claiming a functional language is as performant as a systems language is a very big claim.
Ocaml is used in many performance and latency sensitive domains as a minimal amount of research would show you. Here's an example:
https://www.janestreet.com/tech-talks/building-an-exchange/
It's also got a reputation for pretty predictable performance characteristics, fast compilation and ease of writing correct and concise code, properties C++, for example, does not necessarily share. Part of this is because the compiler is comparatively straightforward in what it does (and same for standard library), with the flip side that you leave some performance on the table. But that might not be a bad trade-off given that ocaml generally seems to be within a factor of ~3x of C++ (which can probably easily baloon up for specific problems, given that ocaml has e.g. poor SIMD and machine size int support). So you probably wouldn't want to write AAA game engines, crypto libs or numerical kernels in it. But my guess is that a language with those characteristics (and much faster iteration times) translates to better real world performance for many other types of tasks.
Claiming "within ~3x of C++" answers your question. For the problems I work in (some of which you mention) even a 5% is a major deal. 300%? That’s just crazy talk and a non-starter.
That’s what performance means and why most language benchmarks are usually useless: people writing them have no clue of what are the requirements of many fields.
I assume you mean 105% and 300%? And what are the 105% that are a major deal – the runtime relative to what the hardware could theoretically do? Or 105% relative to what you can get out of C++?
Likewise, there is an endless amount of compilers in research written in OCaml.
Last time I checked, anything related to compiler toolchains used to be considered systems programming.
Anecdotally, I once worked at an Ocaml trading firm - where microseconds could matter! - and at least at the time, folks there weren't particularly interested in multicore. We have async/lwt already, and we spun up multiple processes for parallelism. A 10% performance drop for multicore support that we didn't need would have been unacceptable for us.
For things like servers, latency can matter to some extent because of the resource usage of in-flight requests. It is desirable to serve requests quickly to free up resources on the server and any clients too.
Similarly one will find plenty of apps which care about throughput too.
Right, but, to the parent's point, OCaml, with its HFT pedigree, has an unusual level of latency sensitivity in its userbase.
I can't speak for Jane Street, but, at the prop shop where I did my time, nobody found it all that peculiar to have major cross-company initiatives to, e.g., shave a couple bytes off a datagram format just to reduce some message's average latency by a fraction of a millisecond.
GUIs in Java are notoriously bad, and tend to only be used for internal tools (where good UX is not a priority), complex professional tools where functionality was a higher priority than UX, or open-source programs where the same thing is true. Another example is that Java is still an unpopular language for command-line tools that can be used from the shell, whereas OCaml is pretty popular in that use space.
Any reasonably popular language will have someone using it for any given thing, sure - I'm sure there are people out there writing webapp backends in C or numerical batch processes in Ruby - but the core constituency can differ a lot between different languages, and that can (and should!) affect that language's priorities.
And there is hardly any difference between using something like Swing Material UI widgets or the typical Android app, other than programmer skills.