Optimise substr assignment in void context
authorFather Chrysostomos <sprout@cpan.org>
Sat, 26 Nov 2011 07:04:22 +0000 (23:04 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Sat, 26 Nov 2011 22:33:47 +0000 (14:33 -0800)
commit24fcb59fcc2fac06ec8b42aeedb3a16987d87db4
treec975ed9172a79e92712e3dcba8a279bb6972bbe1
parenta74fb2cdc8f2121774cc6d2b5e9ddd01a96db467
Optimise substr assignment in void context

In void context we can optimise

    substr($foo, $bar, $baz) = $replacement;

to something like

    substr($foo, $bar, $baz, $replacement);

except that the execution order must be preserved.  So what we actu-
ally do is

    substr($replacement, $foo, $bar, $baz);

with a flag to indicate that the replacement comes first.  This means
we can also optimise assignment to two-argument substr the same way.

Although optimisations are not supposed to change behaviour,
this one does.

• It stops substr assignment from calling get-magic twice, which means
  the optimisation makes things less buggy than usual.
• It causes the uninitialized warning (for an undefined first argu-
  ment) to mention the substr operator, as it did before the previous
  commit, rather than the assignment operator.  I think that sort of
  detail is minor enough.

I had to make the warning about clobbering references apply whenever
substr does a replacement, and not only when used as an lvalue.  So
four-argument substr now emits that warning.  I would consider that a
bug fix, too.

Also, if the numeric arguments to four-argument substr and the
replacement string are undefined, the order of the uninitialized warn-
ings is slightly different, but is consistent regardless of whether
the optimisation is in effect.

I believe this will make 95% of substr assignments run faster.  So
there is less incentive to use what I consider the less readable form
(the four-argument form, which is not self-documenting).

Since I like naïve benchmarks, here are Before and After:

$ time ./miniperl -le 'do{$x="hello"; substr ($x,0,0) = 34;0}for 1..1000000'

real 0m2.391s
user 0m2.381s
sys 0m0.005s
$ time ./miniperl -le 'do{$x="hello"; substr ($x,0,0) = 34;0}for 1..1000000'

real 0m0.936s
user 0m0.927s
sys 0m0.005s
dist/B-Deparse/Deparse.pm
dist/B-Deparse/t/deparse.t
ext/B/B/Concise.pm
ext/B/t/concise-xs.t
op.c
op.h
pp.c
t/lib/warnings/9uninit