Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: refactor primordials.uncurryThis #36221

Closed
wants to merge 1 commit into from

Conversation

aduh95
Copy link
Contributor

@aduh95 aduh95 commented Nov 21, 2020

This is done to avoid creating an array and gain performance.

Refs: https://v8.dev/blog/v8-release-80#optimizing-higher-order-builtins

cc @ExE-Boss @mscdex

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • commit message follows commit guidelines

@aduh95 aduh95 added the needs-benchmark-ci PR that need a benchmark CI run. label Nov 21, 2020
@targos
Copy link
Member

targos commented Nov 22, 2020

Is there a core library that's already using a lot of uncurried methods? This is to know which benchmarks to run.

@aduh95
Copy link
Contributor Author

aduh95 commented Nov 22, 2020

Good point, I'm not sure. Let me pull the commits from my other PRs to see if it improves the perf for buffers and fs.

EDIT: Benchmark-CI: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/700/console

@aduh95
Copy link
Contributor Author

aduh95 commented Nov 22, 2020

It seems to have indeed an overall positive impact on performance.

Perf regressions

With this PR:

                                                                                                                                           confidence improvement accuracy (*)    (**)   (***)
buffers/buffer-concat.js n=800000 withTotalLength=1 pieceSize=1 pieces=4                                                                            *     -2.59 %       ±2.18%  ±2.91%  ±3.79%
buffers/buffer-concat.js n=800000 withTotalLength=1 pieceSize=256 pieces=16                                                                         *     -5.97 %       ±4.53%  ±6.03%  ±7.85%
buffers/buffer-copy.js n=6000000 partial='true' bytes=128                                                                                          **     -3.85 %       ±2.86%  ±3.81%  ±4.97%
buffers/buffer-copy.js n=6000000 partial='true' bytes=8                                                                                           ***     -4.88 %       ±2.72%  ±3.64%  ±4.77%
buffers/buffer-fill.js n=20000 size=65536 type='fill(0)'                                                                                           **     -6.89 %       ±4.86%  ±6.47%  ±8.43%
buffers/buffer-fill.js n=20000 size=65536 type='fill(100)'                                                                                         **     -7.51 %       ±4.99%  ±6.64%  ±8.65%
buffers/buffer-fill.js n=20000 size=65536 type='fill(400)'                                                                                          *     -6.23 %       ±5.17%  ±6.89%  ±9.00%
buffers/buffer-fill.js n=20000 size=65536 type='fill("t")'                                                                                          *     -4.66 %       ±3.98%  ±5.30%  ±6.90%
buffers/buffer-fill.js n=20000 size=65536 type='fill("t", "utf8")'                                                                                ***     -7.38 %       ±3.78%  ±5.03%  ±6.56%
buffers/buffer-fill.js n=20000 size=8192 type='fill("")'                                                                                          ***     -7.65 %       ±4.05%  ±5.40%  ±7.06%
buffers/buffer-fill.js n=20000 size=8192 type='fill(0)'                                                                                           ***    -15.52 %       ±5.99%  ±7.96% ±10.36%
buffers/buffer-fill.js n=20000 size=8192 type='fill(100)'                                                                                         ***    -20.12 %       ±5.72%  ±7.67% ±10.10%
buffers/buffer-fill.js n=20000 size=8192 type='fill(400)'                                                                                         ***    -16.90 %       ±5.48%  ±7.29%  ±9.50%
buffers/buffer-fill.js n=20000 size=8192 type='fill("t", 0)'                                                                                      ***    -12.71 %       ±5.22%  ±6.94%  ±9.04%
buffers/buffer-fill.js n=20000 size=8192 type='fill("t", 0, "utf8")'                                                                                *     -7.39 %       ±6.26%  ±8.33% ±10.84%
buffers/buffer-from.js n=800000 len=2048 source='string-base64'                                                                                     *    -13.01 %      ±10.73% ±14.30% ±18.66%
buffers/buffer-indexof.js n=50000 type='string' encoding='utf8' search='Ou est ma chatte?'                                                          *     -5.47 %       ±5.07%  ±6.82%  ±9.02%
buffers/buffer-read-float.js n=1000000 value='big' endian='LE' type='Double'                                                                      ***    -16.94 %       ±4.76%  ±6.35%  ±8.31%
buffers/buffer-read-float.js n=1000000 value='inf' endian='LE' type='Double'                                                                      ***    -20.86 %       ±3.63%  ±4.84%  ±6.31%
buffers/buffer-read-float.js n=1000000 value='nan' endian='LE' type='Double'                                                                      ***    -17.51 %       ±4.13%  ±5.50%  ±7.17%
buffers/buffer-read-float.js n=1000000 value='small' endian='LE' type='Double'                                                                    ***    -18.08 %       ±4.27%  ±5.69%  ±7.40%
buffers/buffer-read-float.js n=1000000 value='zero' endian='LE' type='Double'                                                                     ***    -18.22 %       ±4.12%  ±5.49%  ±7.14%
buffers/buffer-read-with-byteLength.js byteLength=6 n=1000000 type='IntBE' buffer='fast'                                                            *     -4.85 %       ±4.28%  ±5.71%  ±7.44%
buffers/buffer-swap.js n=1000000 len=1024 method='swap16' aligned='true'                                                                            *     -2.12 %       ±2.03%  ±2.70%  ±3.51%
buffers/buffer-tostring.js n=1000000 len=1024 args=1 encoding='UCS-2'                                                                               *     -2.04 %       ±1.79%  ±2.38%  ±3.10%
buffers/buffer-tostring.js n=1000000 len=1 args=0 encoding='latin1'                                                                                 *     -4.28 %       ±3.69%  ±4.92%  ±6.41%
buffers/buffer-tostring.js n=1000000 len=1 args=3 encoding='ascii'                                                                                 **     -2.67 %       ±1.68%  ±2.23%  ±2.90%
buffers/buffer-write.js n=1000000 type='UInt16BE' buffer='fast'                                                                                     *     -4.26 %       ±3.82%  ±5.09%  ±6.64%
buffers/buffer-write-string.js n=1000000 len=2048 args='offset+length' encoding='ascii'                                                            **     -3.50 %       ±2.35%  ±3.14%  ±4.13%
buffers/buffer-write-string.js n=1000000 len=2048 args='offset+length' encoding='utf16le'                                                         ***     -1.38 %       ±0.76%  ±1.02%  ±1.32%

Without this PR:

                                                                                                                                           confidence improvement accuracy (*)    (**)   (***)
buffers/buffer-compare-instance-method.js n=1000000 args=5 size=16386                                                                               *     -2.65 %       ±2.32%  ±3.10%  ±4.06%
buffers/buffer-copy.js n=6000000 partial='false' bytes=32768                                                                                      ***    -20.43 %       ±5.03%  ±6.69%  ±8.70%
buffers/buffer-copy.js n=6000000 partial='false' bytes=8                                                                                          ***     -3.55 %       ±1.30%  ±1.73%  ±2.26%
buffers/buffer-copy.js n=6000000 partial='true' bytes=32768                                                                                       ***    -44.63 %       ±7.50%  ±9.98% ±13.00%
buffers/buffer-creation.js n=600000 len=1024 type='fast-alloc-fill'                                                                                **     -6.14 %       ±4.53%  ±6.03%  ±7.85%
buffers/buffer-fill.js n=20000 size=65536 type='fill(0)'                                                                                          ***     -8.14 %       ±3.66%  ±4.87%  ±6.34%
buffers/buffer-fill.js n=20000 size=65536 type='fill(100)'                                                                                        ***     -7.35 %       ±4.14%  ±5.51%  ±7.17%
buffers/buffer-fill.js n=20000 size=65536 type='fill(400)'                                                                                        ***     -8.21 %       ±3.65%  ±4.87%  ±6.35%
buffers/buffer-fill.js n=20000 size=65536 type='fill("t", 0)'                                                                                      **     -5.36 %       ±3.37%  ±4.49%  ±5.85%
buffers/buffer-fill.js n=20000 size=65536 type='fill("t", 0, "utf8")'                                                                             ***     -8.87 %       ±4.20%  ±5.59%  ±7.28%
buffers/buffer-fill.js n=20000 size=65536 type='fill("test")'                                                                                       *     -4.97 %       ±4.00%  ±5.33%  ±6.94%
buffers/buffer-fill.js n=20000 size=65536 type='fill("t", "utf8")'                                                                                 **     -6.09 %       ±3.58%  ±4.76%  ±6.20%
buffers/buffer-fill.js n=20000 size=8192 type='fill(0)'                                                                                           ***    -31.09 %       ±4.92%  ±6.57%  ±8.57%
buffers/buffer-fill.js n=20000 size=8192 type='fill(100)'                                                                                         ***    -28.59 %       ±4.76%  ±6.33%  ±8.24%
buffers/buffer-fill.js n=20000 size=8192 type='fill(400)'                                                                                         ***    -30.78 %       ±3.69%  ±4.91%  ±6.39%
buffers/buffer-fill.js n=20000 size=8192 type='fill("t")'                                                                                          **     -6.52 %       ±4.19%  ±5.60%  ±7.32%
buffers/buffer-fill.js n=20000 size=8192 type='fill("t", 0, "utf8")'                                                                              ***     -9.97 %       ±4.98%  ±6.63%  ±8.63%
buffers/buffer-fill.js n=20000 size=8192 type='fill("t", "utf8")'                                                                                   *     -8.24 %       ±6.93%  ±9.23% ±12.05%
buffers/buffer-from.js n=800000 len=2048 source='arraybuffer-middle'                                                                                *     -4.75 %       ±4.31%  ±5.76%  ±7.55%
buffers/buffer-read.js n=1000000 type='BigUInt64BE' buffer='fast'                                                                                   *     -2.43 %       ±2.39%  ±3.19%  ±4.15%
buffers/buffer-swap.js n=1000000 len=1024 method='swap64' aligned='false'                                                                           *     -7.94 %       ±7.45% ±10.03% ±13.30%
buffers/buffer-swap.js n=1000000 len=768 method='swap32' aligned='true'                                                                             *     -5.01 %       ±3.83%  ±5.13%  ±6.75%
Perf improvments

With this PR:

                                                                                                                                           confidence improvement accuracy (*)    (**)   (***)
buffers/buffer-bytelength.js n=4000000 len=256 encoding='buffer'                                                                                    *      3.32 %       ±2.77%  ±3.69%  ±4.81%
buffers/buffer-compare-instance-method.js n=1000000 args=1 size=16                                                                                 **      7.64 %       ±4.70%  ±6.25%  ±8.14%
buffers/buffer-compare-instance-method.js n=1000000 args=2 size=16                                                                                  *      2.54 %       ±2.10%  ±2.80%  ±3.64%
buffers/buffer-compare-instance-method.js n=1000000 args=5 size=16                                                                                  *      2.14 %       ±1.80%  ±2.41%  ±3.14%
buffers/buffer-compare.js n=1000000 size=16                                                                                                       ***      6.25 %       ±2.70%  ±3.59%  ±4.68%
buffers/buffer-compare.js n=1000000 size=16386                                                                                                    ***      2.37 %       ±1.28%  ±1.70%  ±2.22%
buffers/buffer-compare-offset.js n=1000000 size=512 method='offset'                                                                                 *      1.12 %       ±1.02%  ±1.36%  ±1.77%
buffers/buffer-creation.js n=600000 len=4096 type='slow-allocUnsafe'                                                                                *      5.23 %       ±4.52%  ±6.01%  ±7.83%
buffers/buffer-equals.js n=1000000 difflen='false' size=0                                                                                         ***      9.71 %       ±5.37%  ±7.15%  ±9.31%
buffers/buffer-equals.js n=1000000 difflen='true' size=0                                                                                          ***     23.83 %       ±6.41%  ±8.52% ±11.09%
buffers/buffer-equals.js n=1000000 difflen='true' size=16386                                                                                      ***     24.96 %       ±4.06%  ±5.40%  ±7.03%
buffers/buffer-equals.js n=1000000 difflen='true' size=512                                                                                        ***     18.33 %       ±5.90%  ±7.86% ±10.26%
buffers/buffer-fill.js n=20000 size=65536 type='fill(Buffer.alloc(1), 0)'                                                                          **      6.02 %       ±4.45%  ±5.92%  ±7.71%
buffers/buffer-from.js n=800000 len=100 source='uint8array'                                                                                         *      7.55 %       ±5.77%  ±7.71% ±10.12%
buffers/buffer-from.js n=800000 len=2048 source='object'                                                                                            *      5.34 %       ±5.17%  ±6.88%  ±8.96%
buffers/buffer-indexof.js n=50000 type='buffer' encoding='ucs2' search='@'                                                                         **      6.76 %       ±4.10%  ±5.46%  ±7.10%
buffers/buffer-indexof.js n=50000 type='buffer' encoding='ucs2' search='Alice'                                                                     **      5.55 %       ±3.99%  ±5.31%  ±6.93%
buffers/buffer-indexof.js n=50000 type='buffer' encoding='ucs2' search='Gryphon'                                                                    *      2.71 %       ±2.24%  ±2.98%  ±3.89%
buffers/buffer-indexof.js n=50000 type='buffer' encoding='ucs2' search='--l'                                                                       **      4.78 %       ±3.27%  ±4.35%  ±5.67%
buffers/buffer-indexof.js n=50000 type='buffer' encoding='ucs2' search='SQ'                                                                       ***      4.78 %       ±2.37%  ±3.16%  ±4.12%
buffers/buffer-indexof.js n=50000 type='buffer' encoding='utf8' search='@'                                                                         **      5.96 %       ±4.23%  ±5.63%  ±7.33%
buffers/buffer-indexof.js n=50000 type='buffer' encoding='utf8' search='Gryphon'                                                                  ***      4.60 %       ±1.79%  ±2.38%  ±3.10%
buffers/buffer-indexof.js n=50000 type='buffer' encoding='utf8' search='SQ'                                                                         *      4.52 %       ±3.43%  ±4.56%  ±5.95%
buffers/buffer-indexof.js n=50000 type='string' encoding='ucs2' search='Ou est ma chatte?'                                                          *      4.47 %       ±3.86%  ±5.14%  ±6.70%
buffers/buffer-read-with-byteLength.js byteLength=5 n=1000000 type='UIntBE' buffer='fast'                                                          **      5.36 %       ±3.46%  ±4.61%  ±6.01%
buffers/buffer-swap.js n=1000000 len=64 method='swap64' aligned='true'                                                                              *      0.29 %       ±0.26%  ±0.34%  ±0.44%
buffers/buffer-tojson.js len=0 n=10000                                                                                                            ***     12.64 %       ±3.95%  ±5.25%  ±6.84%

Without this PR:

                                                                                                                                           confidence improvement accuracy (*)    (**)   (***)
buffers/buffer-bytelength.js n=4000000 len=256 encoding='buffer'                                                                                    *      2.39 %       ±2.25%  ±3.00%  ±3.91%
buffers/buffer-copy.js n=6000000 partial='true' bytes=128                                                                                         ***      5.00 %       ±1.80%  ±2.41%  ±3.17%
buffers/buffer-copy.js n=6000000 partial='true' bytes=8                                                                                           ***      2.72 %       ±0.96%  ±1.28%  ±1.66%
buffers/buffer-creation.js n=600000 len=10 type='slow-allocUnsafe'                                                                                  *     11.32 %       ±8.69% ±11.63% ±15.28%
buffers/buffer-from.js n=800000 len=2048 source='arraybuffer'                                                                                      **      8.74 %       ±5.63%  ±7.49%  ±9.76%
buffers/buffer-indexof.js n=50000 type='string' encoding='utf8' search='@'                                                                          *      6.45 %       ±5.45%  ±7.29%  ±9.58%
buffers/buffer-tostring.js n=1000000 len=1024 args=3 encoding='hex'                                                                                **      1.94 %       ±1.29%  ±1.72%  ±2.25%
buffers/buffer-tostring.js n=1000000 len=1024 args=3 encoding='latin1'                                                                              *      1.51 %       ±1.33%  ±1.77%  ±2.32%
buffers/buffer-write-string.js n=1000000 len=2048 args='offset' encoding='latin1'                                                                   *      3.03 %       ±2.33%  ±3.11%  ±4.07%

@aduh95 aduh95 marked this pull request as ready for review November 22, 2020 18:55
@aduh95 aduh95 added request-ci Add this label to start a Jenkins CI on a PR. and removed needs-benchmark-ci PR that need a benchmark CI run. labels Nov 22, 2020
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Nov 22, 2020
@nodejs-github-bot
Copy link
Collaborator

}

const { bind, call } = Function.prototype;
const uncurryThis = call.bind(bind, call);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not that this isn't a very cool application, but wouldn't this be a bit clearer?

const uncurryThis = (f) => Function.prototype.call.bind(f)

Copy link
Contributor Author

@aduh95 aduh95 Nov 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that Function.prototype.bind can be modified by the user. I could add a comment explaining it though. Do you think that would be enough?

Suggested change
const uncurryThis = call.bind(bind, call);
// The following line is equivalent to `const uncurryThis = f => call.bind(f);`.
const uncurryThis = call.bind(bind, call);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah I guess once you factor it out you just end up with call.bind(bind, call). A comment would be good then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, PTAL.

This is done to avoid creating an array and gain performance.

Co-authored-by: ExE Boss <[email protected]>
@mscdex
Copy link
Contributor

mscdex commented Nov 22, 2020

It seems to have helped a little bit, but there are still significant regressions when the other primordial changes are added on top. For example the fill(n) Buffer regressions in #36166 were:

buffers/buffer-fill.js n=20000 size=8192 type='fill(0)'             ***    -31.61 %       ±5.72%  ±7.65% ±10.04%
buffers/buffer-fill.js n=20000 size=8192 type='fill(100)'           ***    -31.92 %       ±3.21%  ±4.28%  ±5.59%
buffers/buffer-fill.js n=20000 size=8192 type='fill(400)'           ***    -32.13 %       ±4.27%  ±5.69%  ±7.43%

but with the current changes in this PR they're still:

buffers/buffer-fill.js n=20000 size=8192 type='fill(0)'             ***    -13.70 %       ±5.74%  ±7.65%  ±9.96%
buffers/buffer-fill.js n=20000 size=8192 type='fill(100)'           ***    -19.47 %       ±3.60%  ±4.81%  ±6.28%
buffers/buffer-fill.js n=20000 size=8192 type='fill(400)'           ***    -20.42 %       ±6.15%  ±8.19% ±10.68%

@aduh95
Copy link
Contributor Author

aduh95 commented Nov 22, 2020

@mscdex I'm confused, are you saying this PR is causing perf issue on buffers? Or did you generate the numbers above by including also the changes from #36166?

@mscdex
Copy link
Contributor

mscdex commented Nov 23, 2020

@aduh95 No, I had already ran all of the Buffer benchmarks with just the (original) changes in this PR and there were no real regressions. The first set of numbers I just posted here are not including the changes in this PR and are just copied from my comment on the separate Buffer primordial changes PR and the second set is with the changes in this PR.

So basically this shows that this PR does have a positive impact but it does not completely undo the regressions at least for the Buffer primordial PR. I did not benchmark the fs primordial PR before and after this PR.

@aduh95
Copy link
Contributor Author

aduh95 commented Nov 23, 2020

@mscdex OK, thanks for the clarification. So you are +1 (or +0 at least) for landing this, but your objection remains for #36166, right?
I don't know if you've seen, I got different benchmark results (see #36166 (comment)) after I added dd068f4. But that's a discsussion we should move to #36166.

@aduh95 aduh95 added author ready PRs that have at least one approval, no pending requests for changes, and a CI started. request-ci Add this label to start a Jenkins CI on a PR. labels Nov 24, 2020
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Nov 24, 2020
@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot
Copy link
Collaborator

Copy link
Member

@targos targos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM based on the benchmark results but I would really like someone from the V8 team to give us a long-term advice for this.

@aduh95
Copy link
Contributor Author

aduh95 commented Nov 24, 2020

LGTM based on the benchmark results but I would really like someone from the V8 team to give us a long-term advice for this.

FWIW, v8 themselves have moved away from using JS (https://github.com/v8/v8/blob/d6ead37d265d7215cf9c5f768f279e21bd170212/src/js/prologue.js#L152-L156) to C++ (https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/bootstrapper.cc#L4619-L4620) to implement uncurryThis. I don't know if that's something we could reuse in node, but that might improve perf even more.

@devsnek
Copy link
Member

devsnek commented Nov 24, 2020

We can't reuse it in node, and V8 has since removed that code entirely.

@aduh95 aduh95 added the commit-queue Add this label to land a pull request using GitHub Actions. label Nov 25, 2020
@github-actions github-actions bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label Nov 25, 2020
@github-actions
Copy link
Contributor

Landed in 4f0f2e7...05847a7

@github-actions github-actions bot closed this Nov 25, 2020
nodejs-github-bot pushed a commit that referenced this pull request Nov 25, 2020
This is done to avoid creating an array and gain performance.

Co-authored-by: ExE Boss <[email protected]>

PR-URL: #36221
Refs: https://v8.dev/blog/v8-release-80#optimizing-higher-order-builtins
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
@aduh95 aduh95 deleted the primordials-uncurryThis branch November 25, 2020 13:27
danielleadams pushed a commit that referenced this pull request Dec 7, 2020
This is done to avoid creating an array and gain performance.

Co-authored-by: ExE Boss <[email protected]>

PR-URL: #36221
Refs: https://v8.dev/blog/v8-release-80#optimizing-higher-order-builtins
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
@danielleadams danielleadams mentioned this pull request Dec 7, 2020
targos pushed a commit that referenced this pull request May 16, 2021
This is done to avoid creating an array and gain performance.

Co-authored-by: ExE Boss <[email protected]>

PR-URL: #36221
Refs: https://v8.dev/blog/v8-release-80#optimizing-higher-order-builtins
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
targos pushed a commit that referenced this pull request Jun 11, 2021
This is done to avoid creating an array and gain performance.

Co-authored-by: ExE Boss <[email protected]>

PR-URL: #36221
Refs: https://v8.dev/blog/v8-release-80#optimizing-higher-order-builtins
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants