@matklad So, after fully going through Martin Thompson’s talk and reading @vitalyd’s posts again, I think I just managed to put my finger on what I dislike about runtime sophistication such as concurrent GC and JIT compilation from the point of view of performance optimization.
While these technologies start out from good intentions (e.g. by liberating blocks of memory in a grouped fashion, one can amortize overhead with respect to the one-by-one approach of manual memory management, RAII and reference counting), and can have some strong performance benefits (like the always-on PGO that is discussed in the talk), making them fast requires, on the language runtime side, building a software complexity monster that is not too far away from the hardware complexity that it is supposed to make accessible.
As a result, you have something that will get your application running with decent performance in an impressively small amount of time, but as a counterpart will face a much larger complexity wall when you try to improve the performance later on, because now you need to grasp not only the complexity of your code and the hardware that it’s running on, but also the huge complexity of your language runtime.
This is why I’m interested in exploring the Julia + Rust combo for scientific computing. I think it naturally maps the sociological ecosystem that we have there, where to oversimplify we have a heterogeneous population composed of a large amount of scientists with relatively weak software training and a small amount of software engineers and computing staff with relatively weak science training.
It’s good to keep the scientists focused on the problem space, and to save them from the need of thinking about the machine 90% of the time, and for this I think Julia comes better armed than its Python / MATLAB / R competitors because the focus on a fast implementation means that people won’t need to jump through the horrible linear algebra contortions that are the hallmark of “slow interpreter with a fast BLAS” ecosystems. Let’s keep linear algebra language features for linear algebra, and write a loop when we truly need a loop, life is just better that way.
At the same time, when the performance limit of current dynamic language technology is reached (and, let’s be honest, it will always be reached in nontrivial ways by naive code), I would much rather optimize code written in a language whose implementation has an easy mental model, than something of the level of complexity of Julia’s implementation. Similarly, when the code gets large, I think the code organization and compile-time error detection of a correctness-focused statically typed language like Rust are too good to ignore. So the “Start with idiomatic Julia, and partially or fully move to Rust if the code gets too large (say, ~10kLOC) or the performance too low” strategy has a lot of appeal to me.
When I find the time to explore this strategy in more depth, I will see how well it works out in practice.