Push is definitely. Unshift is too in theory, but it's difficult to make the optimization kick in. Also, you have to fix an array with spare room at the head, and that doesn't happen by itself.
The following program shows the optimization in action:
int main () { array a;
// This reallocates the array in every iteration. a = allocate (100000 - 10000); werror ("%O\n", gauge { for (int i = 0; i < 10000; i++) a = ({17}) + lambda () {array b = a; a = 0; return b;}(); });
// This does not reallocate the array in every iteration. a = allocate (100000); a = a[10000..]; werror ("%O\n", gauge { for (int i = 0; i < 10000; i++) a = ({17}) + lambda () {array b = a; a = 0; return b;}(); }); }
I get 30.82 sec in the first loop and 0.02 in the second with a 7.6. There are two things worth noting here:
o The line
a = ({17}) + lambda () {array b = a; a = 0; return b;}();
is essentially the same as "a = ({17}) + a", but the difference is that the array in a only exists on stack when `+ is executed. If you write
a = ({17}) + a;
you'll get two refs to the array: One in a and another on the stack in the call to `+. The optimizer has to play safe in that case and therefore can't change the array destructively.
o The lines
a = allocate (100000); a = a[10000..];
can't be combined to
a = allocate (100000)[10000..];
in 7.6 since the constant optimizer will produce an array with 90000 elements and no room before the head. This appears to work better in 7.7.