Complexity/Time Constraints, predicability, compiler-existance and programming language design.

Complexity:

- JIT is only faster if it often enough recognizes the right things to JIT in the right way.

- At the same time the JIT compilers share resources with the running program limiting how complex the analytics and optimizations it can do can become. This is not just a problem wrt. to hurting the main applications performance due to reducing available resources but also e.g. matters for energy usage.

- Worse there is often real time time bound on how long a JIT compiler can take no matter how much resources it uses or responsiveness will suffer to much.

- Similar if no additional tricks are used (which often add painful complexity) the optimizations have to be done every time the program is started again and again, hurting auto scaling or ephemeral programs.

- There is also the problem that workload/data specific optimizations (which gives JIT the additional edge) rely on some data/workloads always having a specific format. If this invariants change they might become slow or might even to fallback to other code (which implies the invariant also needed to be checked). One exception is if you hint to the compiler that certain invariants (e.g. function pointers) won't change (like in the OP article).

Predictability:

- the performance of AOT focused language code is often more predictable making it often easier to optimize

- in JIT languages you rely more on the compiler being able to detect the right patterns, this isn't always easy and even with AOT languages this sometimes isn't easy even through the compiler can do much more work to analyze the context

compiler-existance:

- Many AOT language can take advantage of LLVM/GCC so a lot of work has gone into optimizing them before they even started.

- And while you can use LLVM to JIT it is often not viable as it's a rather large dependency with a lot of different versions which itself is optimized for throughput and not latency and is also tuned for a use-case which is allowed to take a while and consume quite a bit of resources.

Language Design:

- Features AOT languages are often designed with their AOT nature in mind, many JIT/Interpreted languages are not. Sometimes they outright have features which can impede JIT optimizations or at least make them more complicated/harder to find/less fast.

- If you throw enough money at a badly for JIT designed language you probably still get good results, e.g. see JavaScript. But this isn't a story of success, it's a story of dynamic/politics leading to an absurd wast of human resources (dev hours) across many companies burning millions if not trillions over the years to work around language limitations.

So any hypothetical language perfectly taking advantage to JIT to become "the fastest" would probably be much closer to a classical AOT language then e.g. JavaScript (as in less dynamic more strictly structured, maybe GC but maybe not or at least more restricted to reduce GC abuse possibilities, no or very limited runtime reflection, etc.).

To top this of some of the benefits of JIT can be brought to AOT languages. For example you can compile a binary with LLVM in a way so that it collects statistics which then can be used to fine tune optimizations (better decide if a path is cold or which loops to unroll how much etc.). Drawback is setting it up in you deployment can add quite a bit of complexity. Another example are tools JIT some very few predetermined points, like de-visualizing/inlining virtual function calls or for one-time initialized global remove the "is initialized checks".

Which means at the cost of more complex deployments doing some profiling to find some problematic vcalls and similar you can get many (but not all) the optimizations JIT languages have while having most of the benefits of AOT language. Through it only makes sense if your company/product reached a certain non-small size. Or when there is more modern tooling around to make such things easier to setup.