Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34.2k
lib: use Object.create(null) directly#11930
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
Uh oh!
There was an error while loading. Please reload this page.
Conversation
TimothyGu commented Mar 19, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
cjihrig left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code LGTM. I worry that this will make debugging more difficult though.
joyeecheung commented Mar 20, 2017
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 |
TimothyGu commented Mar 20, 2017
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)} |
TimothyGu commented Mar 20, 2017
Can you elaborate? I doubt having a generic name like |
cjihrig commented Mar 20, 2017
I meant specifically in heap snapshots. |
joyeecheung commented Mar 20, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
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 |
mscdex commented Mar 20, 2017
What's up with the 'manyblankpairs' regression? |
TimothyGu commented Mar 20, 2017
@mscdex, it can be attributed to the initial cost of object creation: |
joyeecheung commented Mar 20, 2017
mscdex commented Mar 20, 2017
Is there any perf difference with |
lpinca commented Mar 20, 2017
|
jasnell left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changes LGTM but needs a rebase
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
TimothyGu commented Mar 23, 2017
@joyeecheung I added a commit that uses a dedicated class for |
TimothyGu commented Mar 24, 2017
Landed in d9b0e4c...cfc8422. |
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]>
mscdex commented Mar 25, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
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';varn=1e7;varinput={mode: 0o666,flags: 'r',start: 5,end: 100};functionStorageObject(){}StorageObject.prototype=Object.create(null);functioncopyObject(source){consttarget=newStorageObject();for(varkeyinsource)target[key]=source[key];returntarget;}functioncopyObject2(source){consttarget=Object.create(null);for(varkeyinsource)target[key]=source[key];returntarget;}varr;console.time('copyObject');for(vari=0;i<n;++i){r=copyObject(input);r.mode>>>=0;}console.timeEnd('copyObject');console.time('copyObject2');for(vari=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 |
mscdex commented Mar 25, 2017
I should add that I just tried with V8 5.7 which was just merged into master and the results are similar. |
mscdex commented Mar 26, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
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 |
lpinca commented Mar 26, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
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 functionStorageObject(){}StorageObject.prototype=Object.create(null);I've described the behavior in #11868 (comment). @mscdex can you take a look? |
twoi commented Apr 1, 2019
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 |
mcollina commented Apr 1, 2019
@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. |
twoi commented Apr 1, 2019
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 |
BridgeAR commented Apr 1, 2019
@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 |
twoi commented Apr 2, 2019 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
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. |
BridgeAR commented Apr 2, 2019
twoi commented Apr 2, 2019
@BridgeAR I see. Makes sense. Thanks |


Since v8/v8@532c16e (included in v5.6), using
Object.create(null)directly is now faster than using a constructor.Refs: emberjs/ember.js#15001
Refs: https://crrev.com/532c16eca071df3ec8eed394dcebb932ef584ee6
/cc @nodejs/v8
querystring
map-bench
Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passesAffected core subsystem(s)
lib