Running scua app.scua now compiles hot loops and
functions to native code as they warm up. You no longer pass
--jit=on. A program that spends its time in loops gets
several times faster, and one that does not runs as it did before. The
compiled and interpreted tiers are checked against each other byte for
byte, so the output, the faults and the limits are the same whichever
ran your code.
--jit=off still runs the interpreter only, and it is
staying. Use it when you want to rule the JIT out of a measurement or a
bug report.
#Loops that stay compiled now
Several loop shapes that used to drop back to the slower tier stay on the fast one in 0.25.0.
A loop that builds small records or arrays.
{ x = i, y = i * 2 } in a physics step,
[id, score] pushed onto a list. Up to four fields or
elements are built inside the compiled loop, the same objects the
interpreter would make, collected at the same points.
A loop that builds short strings. `item_{i}`,
"id:" .. id, a whole line like `{name}: {i}`.
Pieces up to 16 bytes and integers below one hundred million are built
inline. A string that grows as the loop runs,
s = s .. piece, still takes the runtime call it always did.
A builder is the tool for that job.
A loop that counts things in a dictionary:
for w in words do
if counts[w] == nil then counts[w] = 1 else counts[w] = counts[w] + 1 end
end
This used to leave the fast tier at the first table access. It compiles now, and the three accesses share one lookup.
A loop that carries a string, a record or a list node from one
iteration to the next. cur = cur.next compiles like a
numeric loop, and the while cur != nil test folds away
inside it.
On Intel and AMD, a loop that calls out and uses a handful of constants no longer runs out of registers. Shapes that compiled on Apple silicon but fell back on x86-64 now compile on both.
Our wordcount benchmark does most of the above in one
loop. Under the JIT it takes less than half the time it took in 0.24.0
on both Apple silicon and x86-64, measured with the setup in
bench/lang/RESULTS.md on 12 September. The interpreter
gains too, by a sixth on Apple silicon and a fifth on x86-64.
#Large tables look up faster
A table with more than sixteen keys, or with non-string keys, is a
hash table underneath. Keys that differ only in a trailing character,
w1, w2, up to w999, used to pile
into runs, so a lookup walked past its neighbours before reaching its
own entry. The slot is now picked by a multiply that spreads such keys
out. Tables with a fixed set of string fields were already direct and do
not change.
One side effect: the order in which for k, v in table
visits a large table's entries changes. That order was never
specified.
#The JIT runs on Windows
On 64-bit Intel and AMD Windows, both tiers of the JIT now run. In earlier releases the flag was accepted and nothing happened. Windows on ARM still runs the interpreter, with the same output.
Asking for the JIT where it cannot run, on a build or a platform without one, now says so on stderr and carries on with the interpreter. Nothing is printed unless you asked for the JIT by name.
--jit-stats gains an opt-exits counter, the
number of times a compiled loop handed control back to the interpreter.
When a loop compiled but did not get faster, that is the number to
read.
#Re-save partitions from earlier releases
The hash table change moves where a table's entries sit inside a
saved blob. A blob written by an earlier release now fails to load with
a version error rather than answering nil for every key.
Re-save anything you want to keep. scua_format_version()
reports the new number.
#For embedders
The C API gains managed userdata, with external ownership and borrowing, dependency-aware destruction and redacted persistence. Checked access sessions expose packed numeric buffers for native reads and writes, and a single-use output API lets C fill a fresh SCUA array and commit its actual length. These interfaces are experimental.
There is also an opt-in dynamic FFI for source builds made with
-Dffi=true, on macOS and Linux, which calls a native
library's exported functions without a wrapper for each one. It gets a
post of its own. Native libraries and
FFI has the setup and the limits.
The full changelog has the detail.