Closed Bug 1370195 Opened 7 years ago Closed 7 years ago

Try to use only one array allocation when calling bound function with many arguments

Categories

(Core :: JavaScript Engine, enhancement)

enhancement
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla55
Tracking Status
firefox55 --- fixed

People

(Reporter: anba, Assigned: anba)

Details

Attachments

(1 file)

We're currently allocating two arrays when invoking bound functions with many parameters (one array to copy the call arguments and another array to merge the bound with the call arguments).
Attached patch bug1370195.patchSplinter Review
|boundArgs| is an empty array for bind_bindFunction0, so we don't actually need to call bind_invokeFunctionN to merge the bound with the call arguments and instead we can directly call FUN_APPLY for the [[Call]] case resp. we only need to map the arguments to an array for [[Construct]].

This improves this µ-benchmark from 525ms to 80ms:

    var r = 0;
    var bf1 = function(a,b,c,d,e,f,g,h,){ return a+b+c+d+e+f+g+h }.bind();
    var bf2 = function(a,b,c,d,e,f,g,h,){ return a+b+c+d+e+f+g+h }.bind();
    var bf3 = function(a,b,c,d,e,f,g,h,){ return a+b+c+d+e+f+g+h }.bind();
    var bf4 = function(a,b,c,d,e,f,g,h,){ return a+b+c+d+e+f+g+h }.bind();
    var bf5 = function(a,b,c,d,e,f,g,h,){ return a+b+c+d+e+f+g+h }.bind();
    var t = Date.now();
    for (var i = 0; i < 1000000; ++i) {
        r += bf1(1,2,3,4,5,6,7,8,)
        r += bf2(1,2,3,4,5,6,7,8,)
        r += bf3(1,2,3,4,5,6,7,8,)
        r += bf4(1,2,3,4,5,6,7,8,)
        r += bf5(1,2,3,4,5,6,7,8,)
    }
    print(Date.now() - t);


To avoid two array allocations for the other binder functions, I've added a nested combiner function expression, so we only need to create a single array when merging the bound with the call arguments. 

I've tried various approaches and this one seems to give a performance improvement when many arguments are present without regressing the performance numbers when only few arguments are used. 

For example when the combiner function was either directly written inline in the bound() function code or when a function declaration was used, I saw a noticeable performance regression (~25% in µ-benchmarks similar to the one above) when invoking or constructing the bound function. The lazily created function expression approach didn't lead to these performance regressions - at least for µ-benchmarks!

This µ-benchmark improves from 645ms to 380ms:

    var r = 0;
    var bf1 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i+j }.bind(null, 1);
    var bf2 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i+j }.bind(null, 1);
    var bf3 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i+j }.bind(null, 1);
    var bf4 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i+j }.bind(null, 1);
    var bf5 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i+j }.bind(null, 1);
    var t = Date.now();
    for (var i = 0; i < 1000000; ++i) {
        r += bf1(2,3,4,5,6,7,8,9,10)
        r += bf2(2,3,4,5,6,7,8,9,10)
        r += bf3(2,3,4,5,6,7,8,9,10)
        r += bf4(2,3,4,5,6,7,8,9,10)
        r += bf5(2,3,4,5,6,7,8,9,10)
    }
    print(Date.now() - t);


And this one improves from 545ms to 400ms:

    var r = 0;
    var bf1 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i }.bind(null, 1,2,3);
    var bf2 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i }.bind(null, 1,2,3,4);
    var bf3 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i }.bind(null, 1,2,3,4,5);
    var bf4 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i }.bind(null, 1,2,3,4,5,6);
    var bf5 = function(a,b,c,d,e,f,g,h,i,j){ return a+b+c+d+e+f+g+h+i }.bind(null, 1,2,3,4,5,6,7);
    var t = Date.now();
    for (var i = 0; i < 1000000; ++i) {
        r += bf1(4,5,6,7,8,9,)
        r += bf2(5,6,7,8,9,)
        r += bf3(6,7,8,9,)
        r += bf4(7,8,9,)
        r += bf5(8,9,)
    }
    print(Date.now() - t);




Do you think this approach works out in practice outside of µ-benchmarks?
Attachment #8874410 - Flags: feedback?(till)
Comment on attachment 8874410 [details] [diff] [review]
bug1370195.patch

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

Great results, thanks! I do think that the approach should improve real-world .bind()-happy code, yes. It seems likely that the lazily allocated combiner function is the best solution because it'll not penalize the cases where it's not needed, but will enable inlining the combiner with ideal type information in those cases where it is needed.

Does this need more work for some reason? If not, feel free to treat the f=me as an r=me and land away.
Attachment #8874410 - Flags: feedback?(till) → feedback+
(In reply to Till Schneidereit [:till] from comment #2)
> Comment on attachment 8874410 [details] [diff] [review]
> bug1370195.patch
> 
> Review of attachment 8874410 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> Great results, thanks! I do think that the approach should improve
> real-world .bind()-happy code, yes. It seems likely that the lazily
> allocated combiner function is the best solution because it'll not penalize
> the cases where it's not needed, but will enable inlining the combiner with
> ideal type information in those cases where it is needed.
> 
> Does this need more work for some reason? If not, feel free to treat the
> f=me as an r=me and land away.

No, it doesn't need any further work. I f? instead of r? just in case there's some gotcha which would make this patch a non-starter.
Comment on attachment 8874410 [details] [diff] [review]
bug1370195.patch

Adding r+ flag per comment #2.
Attachment #8874410 - Flags: review+
Pushed by ryanvm@gmail.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/dbedc1b9a3cc
Use fewer array allocations when invoking bound functions with many arguments. r=till
Keywords: checkin-needed
https://hg.mozilla.org/mozilla-central/rev/dbedc1b9a3cc
Status: ASSIGNED → RESOLVED
Closed: 7 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla55
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: