Perry uses NaN-boxing to preserve TypeScript's dynamic type system at runtime, the same approach as JavaScriptCore. The PERF_ROADMAP is honest about the cost: 1.86x behind Zig on image convolution, with 1.24 billion wasted instructions traced specifically to NaN-box unboxing. You cannot get C-level performance without dropping TypeScript semantics, and dropping them means you are no longer compiling TypeScript.
I think you mean you can't get that performance without monomorphization. When you know the types you can...
...wait, I went and looked up that file.
"The Three Optimizations That Would Close the Gap"
You're presenting the data from there in an extremely misleading way! They in no way need to drop any Typescript semantics to go faster.
Typescript is a dynamic language. Without changing the language, there is fundamentaly no way to resolve at compile time decisions that can be made only at runtime (ie, they are data driven). Monomorphization helps pin down (some) dynamic types but the fundamental problem remains.
You can compile two versions, it's not a big deal. "Fundamentally no way" means you're trying to solve math proofs instead of going for practical speedups.
But this case I think they do know the number type for sure, they're just failing to optimize around that fact.
Why don't JITs preserve previous work across runs of the same code?
If you encounter code with the same hash as last time, load up the previously generated binary and run that... or is that already happening?
Julia?
That's a JIT. Yes, you can do all sorts of optimizations in a JIT, because you do it at runtime using runtime information, and always keeping an escape hatch, so the static code bails when invoked with data it was not compiled to handle. This kind of hatch is used here with <any> wrapping.
JIT is a technique to accelerate dynamic languages at runtime to near machine performance while keeping dynamic ergonomics; but it can't transcend the AOT / runtime wall.
Julia sits somewhere between jit and aot, there is no interpreter part, if something is to be exectued, it's compiled to machine code.
The point is that you can have dynamic language that executes natively.
You can also compile whole program aot in Julia.
You're right. The typed buffer locals optimization keeps TypeScript semantics intact by exploiting the existing Buffer/Uint8Array type annotation to skip the NaN-unbox. It's not dropping types, it's using them. The floor I described applies to any-typed paths where static type info isn't available. For well-typed TypeScript, the roadmap shows the gap closes without semantic changes.
Put the LLM down and talk to humans as a human.