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

Buffer.length became significantelly slower in 3.x #2463

Closed
matklad opened this issue Aug 20, 2015 · 15 comments
Closed

Buffer.length became significantelly slower in 3.x #2463

matklad opened this issue Aug 20, 2015 · 15 comments
Labels
buffer Issues and PRs related to the buffer subsystem. v8 engine Issues and PRs related to the V8 dependency.

Comments

@matklad
Copy link

matklad commented Aug 20, 2015

May be it's a know issue, but I was surprised to discover this behavior in my benchmarks.

Consider this two snippets of code:

for (var i = 0; i < buffer.length; i++) {
    ...
}

and

var length = buffer.length;
for (var i = 0; i < length; i++) {
    ...
}

under v2.5.0 their performance is indistinguishable, but under v3.0.0 or v3.1.0 the first version causes 1.5x slowdown of my whole benchmark.

@vkurchatkin vkurchatkin added the buffer Issues and PRs related to the buffer subsystem. label Aug 20, 2015
@Fishrock123
Copy link
Contributor

Probably because .length is now a part of the underlying Uint8Array implementation, rather than just a lazy value.

@vkurchatkin
Copy link
Contributor

It makes sense since in 3.0.0 it's a getter non-own property and previously it was just a simple own data property. Probably we can just attach it to an instance. /cc @trevnorris

@thefourtheye
Copy link
Contributor

I am not sure if this benchmark is correct, but it shows the difference

'use strict';

const common = require('../common');

const bench = common.createBenchmark(main, {
  n: [10e3, 10e4, 10e5, 10e6, 10e7, 10e8]
});

function main(conf) {
  const buffer = new Buffer(conf.n | 0);

  bench.start();
  for (var i = 0; i < buffer.length; i++);
  bench.end(n);
}

And this is the result I got with the latest master,

➜  io.js git:(master) ✗ ./iojs --version
v4.0.0-pre
➜  io.js git:(master) ✗ ./iojs benchmark/buffers/buffer-length.js
buffers/buffer-length.js n=10000:       27445910.97095
buffers/buffer-length.js n=100000:      56917366.79771
buffers/buffer-length.js n=1000000:    128949281.28184
buffers/buffer-length.js n=10000000:   151316669.89778
buffers/buffer-length.js n=100000000:  146552505.52810
buffers/buffer-length.js n=1000000000: 133754893.46407

And the following with v2.5.0,

➜  io.js git:(master) ✗ node --version
v2.5.0
➜  io.js git:(master) ✗ node benchmark/buffers/buffer-length.js 
buffers/buffer-length.js n=10000:        76998891.21597
buffers/buffer-length.js n=100000:       57674750.16740
buffers/buffer-length.js n=1000000:     375004593.80627
buffers/buffer-length.js n=10000000:    800677373.05761
buffers/buffer-length.js n=100000000:  1046371009.02718
buffers/buffer-length.js n=1000000000: 1129972622.92159

@trevnorris
Copy link
Contributor

@vkurchatkin What do you mean by "can just attach it to an instance"?

@matklad Yeah. Sorry about this. Thanks to everything W3C related, properties must be a getter.

@vkurchatkin
Copy link
Contributor

I mean just stick this.length = length in constructor. It is also good for backward compatibility

@trevnorris
Copy link
Contributor

@vkurchatkin we don't have a constructor anymore. It's now essentially var ua = new Uint8Array(n); ua.__proto__ = Buffer.prototype;

@trevnorris
Copy link
Contributor

Can this be closed? It's not a won't fix, but a can't fix. Part of what we inherited by needing to use typed arrays.

@thefourtheye
Copy link
Contributor

@trevnorris Can we maintain a length property and update it whenever the actual size of the buffer object is altered?

@trevnorris
Copy link
Contributor

@thefourtheye It's not a property. It's a getter on the typed array. And since we can only "inherit" the typed array by setting its __proto__ there's no way, that I'm aware of, we can override the default getter with our own value.

And if the size of the Buffer could be altered, then v8 will have to provide a new API that alerts us when that happens. Otherwise we'll have no idea when the array size changed.

@thefourtheye
Copy link
Contributor

we can override the default getter with our own value

Yup that's what I had in my mind.

@trevnorris
Copy link
Contributor

@thefourtheye Eh? The only way to do that from JS is to use Object.defineProperty(). That brings construction time of a 64KB Buffer from 3150 ns/op to 5170 ns/op. And using v8::Object::ForceSet() would change instantiation from 3170 ns/op to 3700 ns/op.

Either way we're taking a performance hit. Not to mention the fact that we'll have no way of knowing when the user runs ArrayBuffer.transfer() unless v8 gives us a hook.

I'm far more concerned about construction time then loop iteration time.

/cc @domenic I'm sure you can rule in from the ECMA side what could potentially happen if we override the .length getter?

@thefourtheye
Copy link
Contributor

I was thinking more like

if (arg < 0 || arg !== arg)
  arg = 0;
const buf = allocate(arg);
buf.length = arg;
return buf;

and maintaining there onwards.

@domenic
Copy link
Contributor

domenic commented Sep 3, 2015

@thefourtheye Doing buf.length = arg will throw in strict mode or be a no-op in sloppy mode, since buf.length is a property with a getter and no setter.

@trevnorris right, you could add a per-instance property that shadows the inherited property, using Object.defineProperty. Seems a bit silly.

The real fix here is just to get V8 to make this fast, like array.length is. Both are basically getters, although only one happens to be manifested as a JS getter.

@Fishrock123 Fishrock123 added the v8 engine Issues and PRs related to the V8 dependency. label Sep 3, 2015
@ChALkeR
Copy link
Member

ChALkeR commented Sep 10, 2015

This is fixed by #2801, it seems.

indutny added a commit that referenced this issue Sep 10, 2015
Original commit message:

    TypedArray accessor detection: consider entire prototype chain

    When looking up a special accessor for known TypedArray fields
    ("length", "byteLength", "byteOffset"), consider the entire
    prototype chain, not only the direct prototype.
    This allows subclasses of TypedArrays to benefit from fast
    specialized accesses.

    Review URL: https://codereview.chromium.org/1313493005

    Cr-Commit-Position: refs/heads/master@{#30678}

Benchmark results:

   buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
   ./node: 71607 node: 8702.3 ............ 722.85%

Improvement depends on the code, but generally brings us back to the
performance that we had before the v8 update (if not making it
faster).

Fixes: #2463
PR-URL: #2801
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
@bnoordhuis
Copy link
Member

Fixed by acb6779.

indutny added a commit that referenced this issue Sep 11, 2015
Original commit message:

    TypedArray accessor detection: consider entire prototype chain

    When looking up a special accessor for known TypedArray fields
    ("length", "byteLength", "byteOffset"), consider the entire
    prototype chain, not only the direct prototype.
    This allows subclasses of TypedArrays to benefit from fast
    specialized accesses.

    Review URL: https://codereview.chromium.org/1313493005

    Cr-Commit-Position: refs/heads/master@{#30678}

Benchmark results:

   buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
   ./node: 71607 node: 8702.3 ............ 722.85%

Improvement depends on the code, but generally brings us back to the
performance that we had before the v8 update (if not making it
faster).

Fixes: #2463
PR-URL: #2801
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 11, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 11, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 12, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 12, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 12, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 12, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 12, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 12, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 12, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Sep 12, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Dec 26, 2015
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
oleavr added a commit to frida/v8 that referenced this issue Aug 11, 2016
Cherry-picked from Node.js:

commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d
Author: Fedor Indutny <fedor@indutny.com>
Date:   Thu Sep 10 12:26:50 2015 -0700

    deps: cherry-pick 6da51b4 from v8's upstream

    Original commit message:

        TypedArray accessor detection: consider entire prototype chain

        When looking up a special accessor for known TypedArray fields
        ("length", "byteLength", "byteOffset"), consider the entire
        prototype chain, not only the direct prototype.
        This allows subclasses of TypedArrays to benefit from fast
        specialized accesses.

        Review URL: https://codereview.chromium.org/1313493005

        Cr-Commit-Position: refs/heads/master@{#30678}

    Benchmark results:

       buffers/buffer-iterate.js size=16386 type=slow method=for n=1000:
       ./node: 71607 node: 8702.3 ............ 722.85%

    Improvement depends on the code, but generally brings us back to the
    performance that we had before the v8 update (if not making it
    faster).

    Fixes: nodejs/node#2463
    PR-URL: nodejs/node#2801
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
    Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
buffer Issues and PRs related to the buffer subsystem. v8 engine Issues and PRs related to the V8 dependency.
Projects
None yet
Development

No branches or pull requests

8 participants