Closed Bug 1366696 Opened 7 years ago Closed 7 years ago

Small performance improvements for RegExp.prototype.@@split and RegExp.prototype.@@replace

Categories

(Core :: JavaScript: Standard Library, enhancement)

enhancement
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla55
Tracking Status
firefox55 --- fixed

People

(Reporter: anba, Assigned: anba)

References

(Blocks 1 open bug)

Details

Attachments

(4 files, 1 obsolete file)

There are some small performance improvements possible for RegExp.prototype.@@split and RegExp.prototype.@@replace, which improved octane-regexp by 5-6% in local tests.
Assignee: nobody → andrebargull
Status: NEW → ASSIGNED
We don't need to create a new RegExp when the input RegExp for RegExp.prototype.@@split doesn't have the sticky flag set. This saves a call into the runtime and an allocation. 

Drive-by fix: Changed the default limit to 2^32-1, per the current spec.

Improves the following µ-benchmark from 1200ms to 600ms for me:

    var r = /a/g;
    var s = "aaaaa";
    var q = 0;
    var t = Date.now();
    for (var i = 0; i < 1000000; ++i) {
        q += s.split(r).length;
    }
    print(Date.now() - t, q);
Attachment #8870010 - Flags: review?(till)
Two changes:
- Adds a fast path for small input strings when no complex replacement is necessary, similar to the existing optimization for global RegExps.

Improves this µ-benchmark from 390 ms to 250 ms:

    var r = /b/;
    var s = "aabaa";
    var q = 0;
    var t = Date.now();
    for (var i = 0; i < 2000000; ++i) {
        q += s.replace(r, "").length;
    }
    print(Date.now() - t, q);

This case is hit multiple times in octane-regexp.


- Adds a separate replacement function for functional replacements. This saves us an array allocation when the number of captures is <= 4.

Improves this benchmark from 1300 ms to 1200 ms

    var r = /a/g;
    var s = "aaaaa";
    var rp = () => "";
    var q = 0;
    var t = Date.now();
    for (var i = 0; i < 1000000; ++i) {
        q += s.replace(r, rp).length;
    }
    print(Date.now() - t, q);


Drive-by compliance fix: Use `undefined` instead of `null` when calling the replacer function in RegExpGetComplexReplacement.
Attachment #8870013 - Flags: review?(till)
js::regexp_clone is not inlinable, so it's faster for us to check the RegExp pattern and flags in every iteration than calling into the runtime. (The RegExp was cloned for `defined(SUBSTITUTION)`, but that should have been `defined(ELEMBASE)` -> bug 1343363.)

Improves this µ-benchmark from 1300 ms (or 1200 with part 2) to 570 ms:

    var r = /a/g;
    var s = "aaaaa";
    var rp = () => "";
    var q = 0;
    var t = Date.now();
    for (var i = 0; i < 1000000; ++i) {
        q += s.replace(r, rp).length;
    }
    print(Date.now() - t, q);


Drive-by changes: Use MOZ_ALWAYS_TRUE for infallible ToInt32 calls in RegExp.cpp and fix the arity for the RegExp selfhosting helpers in SelfHosting.cpp.
Attachment #8870016 - Flags: review?(till)
And another drive-by change:
I noticed there's some unnecessary rooting in SelfHosting.cpp for return values, which we should remove because it isn't really necessary and may or may not save a few nanoseconds. ;-)
Attachment #8870018 - Flags: review?(till)
I probably should have mentioned, that js::regexp_construct_raw_flags and js::regexp_clone both showed up in bug 1365361, which was the main motivation to reduce calls to these two functions.
Blocks: 1365361
Comment on attachment 8870010 [details] [diff] [review]
bug1366696-part1-split-sticky.patch

Review of attachment 8870010 [details] [diff] [review]:
-----------------------------------------------------------------

Very nice.

::: js/src/builtin/RegExp.js
@@ +762,5 @@
>  
>      // Step 13.
>      var lim;
>      if (limit === undefined)
> +        lim = 0xffffffff;

Nit: please introduce a constant and use it here.
Attachment #8870010 - Flags: review?(till) → review+
Comment on attachment 8870013 [details] [diff] [review]
bug1366696-part2-local-replace.patch

Review of attachment 8870013 [details] [diff] [review]:
-----------------------------------------------------------------

r=me
Attachment #8870013 - Flags: review?(till) → review+
Comment on attachment 8870016 [details] [diff] [review]
bug1366696-part3-global-replace.patch

Review of attachment 8870016 [details] [diff] [review]:
-----------------------------------------------------------------

r=me
Attachment #8870016 - Flags: review?(till) → review+
Comment on attachment 8870018 [details] [diff] [review]
bug1366696-part4-sh-rooting.patch

Review of attachment 8870018 [details] [diff] [review]:
-----------------------------------------------------------------

Gotta save those nanoseconds ;)
Attachment #8870018 - Flags: review?(till) → review+
Updated per review comments. Carrying r+.
Attachment #8870010 - Attachment is obsolete: true
Attachment #8870556 - Flags: review+
Pushed by ryanvm@gmail.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/3fe3c5c6e251
Part 1: Only create new splitter if sticky flag is present in input regexp. r=till
https://hg.mozilla.org/integration/mozilla-inbound/rev/82fdb7ec638e
Part 2: Improve performance of RegExp.prototype.@@replace with non-global regexp. r=till
https://hg.mozilla.org/integration/mozilla-inbound/rev/a8bf1340824b
Part 3: Improve performance of RegExp.prototype.@@replace with global regexp. r=till
https://hg.mozilla.org/integration/mozilla-inbound/rev/afcf9a101dfa
Part 4: Remove unnecessary rooting in SelfHosting.cpp for return values. r=till
Keywords: checkin-needed
(In reply to André Bargull from comment #0)
> There are some small performance improvements possible for
> RegExp.prototype.@@split and RegExp.prototype.@@replace, which improved
> octane-regexp by 5-6% in local tests.

Seems to be at least 7% on the Mac slaves on AWFY? Very nice.
(In reply to Jan de Mooij [:jandem] from comment #15)
> (In reply to André Bargull from comment #0)
> > There are some small performance improvements possible for
> > RegExp.prototype.@@split and RegExp.prototype.@@replace, which improved
> > octane-regexp by 5-6% in local tests.
> 
> Seems to be at least 7% on the Mac slaves on AWFY? Very nice.

\o/

Any next improvements are probably harder to achieve. For example the String.prototype.replace calls in Octane-RegExp spend quite some time concatenating strings (http://searchfox.org/mozilla-central/rev/2933592c4a01b634ab53315ce2d0e43fccb82181/js/src/builtin/RegExpGlobalReplaceOpt.h.js#107-109,123-127). And since we don't have something like a StringBuilder for self-hosted code, we're creating lots of dependent and rope strings here.
Unfortunately it looks like this also caused a 1100% (!) slowdown in sixspeed-templatestring-es6, and some less pronounced slowdowns in the other template strings tests:

https://arewefastyet.com/#machine=29&view=single&suite=six-speed&subtest=templatestring-es6

I have no idea what might be going on, but it seems like some light profiling should make it obvious.
Flags: needinfo?(andrebargull)
I've investigated the slowdown with local awfy-builds and it seems to start with https://hg.mozilla.org/integration/mozilla-inbound/rev/882d55c60444 (bug 1365782), which seems much more likely than the changes from this bug. Setting NI for :tcampbell to verify it.
Flags: needinfo?(andrebargull) → needinfo?(tcampbell)
This is quite likely my bug, I'll reopen Bug 1365782 and investigate.
Flags: needinfo?(tcampbell)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: