Hi all,
I wanted to ring a friendly alarm about some corner case of OCaml’s memory usage since the 5.x switch which is now about 3 years old and about to be on its 5th release since.
This is a graph of the memory usage for a real-use case of our application. Left is with OCaml 4.14.2 and right with ocaml 5.3:
I just completed some early testing with the latest alpha release of OCaml 5.4 and, sadly, the problem is still there.
The application in question, liquidsoap is perhaps an outlier in the OCaml community in that it operates on a very short (~0.02s) loop that creates a lot of data (raw audio/video) on each iteration and for which most of the data can be discarded after each loop but some of it has to be kept for audio/video buffers and such.
It could be argued that this kind of application was never meant to be written in OCaml but, alas, it’s been using the compiler for quite a while without any problems. It’s also a scripting language and OCaml’s tools on that end are awesome for it.
Clearly, though, this kind of use is overwhelming the new OCaml 5.x GC as it does not seem to be able to perform as well as the 4.14.x one in a case like that.
Similar problems have been reported through the compiler’s bugtracker:
- Regression with default GC settings between `4.14.2` and `5.1.1` · Issue #13123 · ocaml/ocaml · GitHub
- Goblint performance regression 4.14 vs 5.3 · Issue #13733 · ocaml/ocaml · GitHub
- Regression with default GC settings between `4.14.2` and `5.1.1` · Issue #13123 · ocaml/ocaml · GitHub
Please don’t consider this post as a criticism of the OCaml developpers, the pace and scope of change has been amazing and the attention to making the compiler better is real.
However, at this point, this is becoming an existential issue for us. If we keep being stuck with OCaml 4.14 for our production use, sooner rather than later, we will be unable to use other projects and libraries from the OCaml ecosystem.
My questions are:
- Are we the only application in the ecosystem seeing these kind of regression?
- Are there any indication that the GC can be brought up-to par on this specific case?
- If not, are there any interest in opening up the GC’s API to help application mitigate their use-case?
- If not, should we consider other tooling/variants e.g. OxCaml’s stack allocations
For #1, I think that it would be really important to figure out. If we are the only case then this is, indeed, that the application is an outlier. However, I have a hard time thinking that we are the only one in this case and, if so, then this becomes a potential problem for the adoption of the compiler.
For #2 I imagine that it’s hard to say if there isn’t a clear reproducible test. However, I already submitted one for ocaml/ocaml#13123 and it is still pending
For #3, my naive idea would be that, since the application is generating short term data on a regular basis, perhaps more control over minor stack allocations and their cleanup could help?
For instance being able to allocate new data on a specific minor stack and cleanup that minor stack only after running one loop?
It’s hard to say if that would make sense but I feel like this is similar to what OxCaml is doing with their stack allocations
For #4, implementing such a drastic change impacts all our APIs.
Even things like List.fold_left signature have to be adapted to the multiple local use-case.
And, overall, the feature seems to be designed for small functions.
In a case like us, with multiple layers of abstractions and some cases where local memory needs to be promoted to the heap for long-term storage (for instance in a temporary audio buffer), the tools there do not seem immediately obvious to use for it and/or with a real risk of being stuck with an exploratory compiler if these never make it into the mainstream compiler.
Thanks for y’all insight!

