GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||
GeezerVison is showing github.com — lib: use Object.create(null) directly by TimothyGu · Pull Request #11930 · nodejs/node Conversation
|
|
There seems to be some regressions in the event benchmarks... See numbersimprovement confidence p.value events/ee-add-remove.js n=250000 -43.29 % *** 4.520847e-30 events/ee-emit-multi-args.js n=2000000 1.57 % 2.325176e-01 events/ee-emit.js n=2000000 8.37 % ** 6.855389e-03 events/ee-listener-count-on-prototype.js n=50000000 -96.06 % *** 8.635026e-26 events/ee-listeners-many.js n=5000000 -17.51 % *** 3.849835e-27 events/ee-listeners.js n=5000000 -56.36 % *** 6.473337e-30 events/ee-once.js n=20000000 -51.46 % *** 4.847882e-33 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Interesting. The V8 optimization is mainly designed to accommodate map-like usage of objects, by automatically using so-called "slow" properties based on a hashmap instead of trying to find hidden classes. This makes it work especially well with cases like HTTP headers, FS cache, and query string parsing that don't have a fixed pattern of object properties. On the other hand, an instance of Take the benchmark with the worst regression, I believe having two or more events is a considerably more common case than just having one event, and therefore I think this change is still justified. benchmark diffdiff --git a/benchmark/events/ee-listener-count-on-prototype.js b/benchmark/events/ee-listener-count-on-prototype.js index dfe7e27..1ef0f09 100644 --- a/benchmark/events/ee-listener-count-on-prototype.js +++ b/benchmark/events/ee-listener-count-on-prototype.js @@ -9,12 +9,12 @@ function main(conf) { var ee = new EventEmitter(); - for (var k = 0; k < 10; k += 1) - ee.on('dummy', function() {}); + for (var k = 0; k < 2 * 10; k += 1) + ee.on(`dummy${k % 2}`, function() {}); bench.start(); for (var i = 0; i < n; i += 1) { - ee.listenerCount('dummy'); + ee.listenerCount(`dummy${i % 2}`); } bench.end(n); } |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Can you elaborate? I doubt having a generic name like |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I meant specifically in heap snapshots. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I think the benchmarks can add a few more configurations for different use cases. Note that the diff in #11930 (comment) also removes the loop invariant(not sure if V8 could perform LICM on that previously). Also +1 with @cjihrig, with |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
What's up with the 'manyblankpairs' regression? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@mscdex, it can be attributed to the initial cost of object creation: |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Before this PR ( After: I think the performance gain is probably worth it but wonder if @nodejs/v8 have any idea to work around this sacrafice |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Is there any perf difference with |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
changes LGTM but needs a rebase
Sorry, something went wrong.
There was an error while loading. Please reload this page.
The object is used as a structure, not as a map, which `StorageObject` was designed for.
After V8 5.6, using Object.create(null) directly is now faster than using a constructor. Refs: emberjs/ember.js#15001 Refs: https://crrev.com/532c16eca071df3ec8eed394dcebb932ef584ee6
6d1a545 to
9549e2d
Compare
|
@joyeecheung I added a commit that uses a dedicated class for |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Landed in d9b0e4c...cfc8422. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
PR-URL: nodejs#11930 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]>
The object is used as a structure, not as a map, which `StorageObject` was designed for. PR-URL: nodejs#11930 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]>
After V8 5.6, using Object.create(null) directly is now faster than using a constructor for map-like objects. PR-URL: nodejs#11930 Refs: emberjs/ember.js#15001 Refs: https://crrev.com/532c16eca071df3ec8eed394dcebb932ef584ee6 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]>
There was an error while loading. Please reload this page.
|
@TimothyGu I'm seeing contradicting results on master with some more simple, direct benchmarks: 'use strict'; var n = 1e7; var input = { mode: 0o666, flags: 'r', start: 5, end: 100 }; function StorageObject() {} StorageObject.prototype = Object.create(null); function copyObject(source) { const target = new StorageObject(); for (var key in source) target[key] = source[key]; return target; } function copyObject2(source) { const target = Object.create(null); for (var key in source) target[key] = source[key]; return target; } var r; console.time('copyObject'); for (var i = 0; i < n; ++i) { r = copyObject(input); r.mode >>>= 0; } console.timeEnd('copyObject'); console.time('copyObject2'); for (var i = 0; i < n; ++i) { r = copyObject2(input); r.mode >>>= 0; } console.timeEnd('copyObject2'); Results: With just the first two properties in With just the first property in |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I should add that I just tried with V8 5.7 which was just merged into master and the results are similar. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Ah, I just noticed that es/map-bench.js isn't actually benchmarking object creation, but instead property access. There is a much larger difference with object creation in I think we should revert the changes in this PR (except maybe the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I also noticed the huge difference with object creation time but this also "fixes" something weird which happens when using instances of function StorageObject() {} StorageObject.prototype = Object.create(null); I've described the behavior in #11868 (comment). @mscdex can you take a look? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
This is a breaking change. Maybe it's faster now, but it is not backwards compatible, e.g. it broke the following code: now throws |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@twoi I imagine you upgraded from a release to another, can you give us the version numbers? Was this a patch/minor release or a major release? Thanks. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
We upgraded from 0.8.15 to 11.9 - finally, because we are using a native module that (pre-napi) needed to ported / rebuilt on every Node.js version upgrade, which include all the vast v8 refactorings. But this change apparently made it into Node.js version 8.0.0. It's filed under |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@twoi this PR is semver-patch. The removal of the prototype was done in #6092 which is marked as semver-major (v6). Is there anything actionable you ask for? I highly recommend to never use |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@BridgeAR Thanks for the advice. Agree. I don't have anything actionable, just wanted to make aware of the fact that this innocent looking change can (and in fact did) break someone's code. Which, it seems, people noticed during testing: https://github.com/nodejs/node/pull/6092/files#r58880619 I am adding more testing myself, so this won't catch me unawares anymore... Case closed. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
The breaking change was done in #6092, not here. This PR is just some code refactoring. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@BridgeAR I see. Makes sense. Thanks |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.