RT #127855] Slowdown in m//g on COW strings
authorDavid Mitchell <davem@iabyn.com>
Mon, 2 May 2016 13:46:49 +0000 (14:46 +0100)
committerRicardo Signes <rjbs@cpan.org>
Tue, 3 May 2016 15:23:03 +0000 (11:23 -0400)
Better fix for this issue. The previous couple of commits revert an
earlier fix for this, which basically modified SvGROW or a particular
one of its callers to add 1 to the requested length to ensure that there
was space for any future COW reference count (which is stored in  spare
byte off the end of the string if SvCUR + null byte < SvLEN).

It turns out that sv_grow() already does a +1 over-allocation (added by me
with v5.19.0-442-gcbcb2a1), *except* that I made it skip the +1 if the
request size seemed to be a large power of two: with the idea being that
if someone had requested an exact big power of two then they were probably
doing something with buffers, and wouldn't want an 0x10000001 byte buffer
when they requested 0x10000000 bytes.  This was me basically being
conservative. However, I was probably too conservative: the simple test I
added just checked that bottom 8 bits were zero and if so, assumed that it
was a big buffer request. So as a side effect of this over-simple test,
something like 0x13d57f00 wouldn't be incremented to 0x13d57f01.

This is what was discovered in the original report in the the RT ticket:
strings allocated with lengths whose lower 8 bits were zero wouldn't have
space for a COW refcount, so would become much slower on things like

    while $g =~ m/0/g.

This commit improves the test so it only skips the +1 on lengths that
are an exact power of 2 and are greater than  0xfff.

sv.c

diff --git a/sv.c b/sv.c
index 0200679..decc47c 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -1573,7 +1573,9 @@ Perl_sv_grow(pTHX_ SV *const sv, STRLEN newlen)
      * Only increment if the allocation isn't MEM_SIZE_MAX,
      * otherwise it will wrap to 0.
      */
-    if (newlen & 0xff && newlen != MEM_SIZE_MAX)
+    if (   (newlen < 0x1000 || (newlen & (newlen - 1)))
+        && newlen != MEM_SIZE_MAX
+    )
         newlen++;
 #endif