A hot loop changes compiler without stopping, property writes skip the detour, and pure recursion remembers its answers
This release builds on Updraft, the compiler Floati wrote for itself. A long loop can now move between the interpreter, Gust and the optimizing compiler in the middle of a run, on one shared frame, so hot code reaches fast code sooner; and because a Gust compile costs about a microsecond per operation, Floati now compiles warm code much earlier, which makes eleven real pages about 1% faster. Compiled code writes object properties itself instead of calling a general helper, one array with a named property no longer slows down every other array on the page, and functions that only compute from their arguments remember the answers they have already worked out.
A loop changes compiler without stopping
Floati runs new code in its interpreter, compiles warm code with Gust, and moves code that stays hot to its optimizing compiler. Until now a function could only change compiler between calls: a long loop that started in the interpreter, or in Gust, stayed there until it finished. Because all three tiers keep a function's state in the same frame, a loop can now switch at the top of an iteration with nothing to translate: a loop in a function that is still warming up enters Gust there, and once it has run long enough to pay for the bigger compile, Gust hands the same frame to the optimizing compiler at a loop edge. Floati also prices the first compile at what a Gust compile really costs, about a microsecond per operation instead of the hundred the old code generator needed, so warm code is compiled far sooner.
Property writes without the detour
Every o.x = v in compiled code used to call a general helper, and that helper first looked up the global object by name and searched the object for a window marker before it even reached its cache. Compiled code now checks that the object has the layout this write saw last time and writes the value in place. Anything unusual (a frozen or sealed object, a read-only property, a setter, a proxy, the global object) still takes the helper, so every write means exactly what it did, and the helper itself now skips the two lookups. A second slowdown is gone too: as soon as one array anywhere on a page got a named property, as some libraries do, push, pop and the other array methods lost their fast path on every array for the rest of the session. Now only that array is treated differently.
Pure recursion remembers its answers
Some recursive functions compute their result from their arguments and nothing else: no writes, no objects, no random numbers. Floati's recursion tier can prove that, and for such a function it now remembers each result for the arguments it was computed for, within one run. Asking again for an answer it already has costs a lookup instead of a whole subtree of calls, so a naive fib(30) does 31 computations instead of 1.35 million. The answers are exactly the ones recomputing would give, and the call stack goes as deep as before, so a runaway recursion still stops where it did. The memory resets on every new call into the function, so a value it captures can change between runs, and a recursion that never repeats a call switches the memory off by itself after a few thousand calls. This is a different kind of speed from faster calls: recursion that does not repeat work, such as walking a tree, runs as before.
What changed 6 changes
Try it in your browser
Each check runs live in the browser you are reading with. Nothing is sent anywhere.