Chapter 7 · 11 July – 1 August

Slow programs, and what they pointed at

For its first month, Raku++ got faster because some small program felt slow. The example programs, Mandelbrot, N-Queens, Life and Brainfuck, were timed against Rakudo, and each slow one pointed at a single engine fault. In late July the work turned to profiles, and a benchmark gate began to decide whether a release could ship.

16.0 → 1.81 sN-Queens at N=10, once next stopped being a C++ exception
0.29 → 0.01 sBrainfuck, once an array read stopped copying the value
−17.6%fib in v1.5.1, from a profile-ranked campaign
−18.3%$a OP $b in a loop, from node specialization (v1.7.0)
The turnThe rule that came out of July: ask the profile who allocates. The wins came from removing work and allocations, not from making structures smaller. A flag-packing change that took a value from 376 to 360 bytes ran 2.5% slower, and was reverted.

What the slow programs pointed at

Decided once

v1.5.0 (29 July) found fib walking its own body about 2.7 million times, through an allocating callback, to decide whether any declaration needed hoisting. The answer never changes, so it is now decided once per block and stored on the tree node. fib went from 911 to 831 ms. That became a pattern: a question whose answer depends only on the program is answered once and remembered on the node. When the engine went parallel, those answers became atomics that are decided once (the parallel chapter).

A campaign ranked by the profile

v1.5.1 changed no behaviour at all; Roast was identical byte for byte. Five candidates were ranked from a profile. Three landed, one was measured and dropped, and one was measured and never attempted.

fib: 903.3 → 744.1 ms. Slot-indexed variables were billed as the biggest architectural win of all, but measured a ceiling of about 4% and were set aside. Three weeks later, with the rest of the frame work done, they took loopsum down 34% (the next speed chapter).

Shapes

v1.7.0 (1 August) taught the two hottest evaluators to recognise four syntactic shapes: $var OP literal, literal OP $var, $var OP $var, and @arr[$var] or @arr[literal]. The verdict is recorded on the node, and the path skips what the general case must do. $a OP $b −18.3%, $a OP 1 −17.7%, fib −11.7%, @a[$i] −8.7%.

Measured and not done