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

util: use Object.create(null) for dictionary object #3791

Closed
wants to merge 10 commits into from
Closed

util: use Object.create(null) for dictionary object #3791

wants to merge 10 commits into from

Conversation

JungMinu
Copy link
Member

Fixes #3788
arrayToHash() needs to use Object.create(null) for its dictionary object.

Fixes #3788
`arrayToHash()` needs to use `Object.create(null)` for its dictionary object.
@evanlucas
Copy link
Contributor

Could use a test

@mscdex mscdex added the util Issues and PRs related to the built-in util module. label Nov 12, 2015
@Fishrock123
Copy link
Contributor

Note: Object.create(null) is pretty slow, relatively speaking. cc @trevnorris?

@Fishrock123
Copy link
Contributor

See also: #728

@Fishrock123
Copy link
Contributor

Probably better would be to special-case __proto__, unless someone has evidence that Object.create(null) is actually faster these days?

@@ -159,7 +159,7 @@ function stylizeNoColor(str, styleType) {


function arrayToHash(array) {
var hash = {};
var hash = Object.create(null);

array.forEach(function(val, idx) {
hash[val] = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

You'd check if val was __proto__ here and discard it if it was, I think. cc @bnoordhuis

add if val is  `__proto__`, use `Object.create(null);`

array.forEach(function(val, idx) {

Copy link
Contributor

Choose a reason for hiding this comment

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

Why creating a new hash in each iteration?

Copy link
Member Author

Choose a reason for hiding this comment

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

@pmq20 Sorry, it was a mistake

@Fishrock123
Copy link
Contributor

@JungMinu please run ./configure && make -j8 test.

@pmq20
Copy link
Contributor

pmq20 commented Nov 12, 2015

Also rebasing multiple commits into one in a single MR is more preferable.

if array contains __proto__, use Object.create(null)
@YurySolovyov
Copy link

also make sure you follow coding style (spaces, braces, etc.)

Beautify codes
hash = Object.create(null);
} else {
hash = {};
}

array.forEach(function(val, idx) {

Choose a reason for hiding this comment

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

if idx is not used, why pass it?

Copy link
Member Author

Choose a reason for hiding this comment

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

@YuriSolovyov Hmm, It was there before I made a PR,
I speculate that it could be used

Choose a reason for hiding this comment

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

it is not used anywhere in the scope of the function, IMO it is safe to drop it

@trevnorris
Copy link
Contributor

@Fishrock123 Object.create(null) is "slow" in terms of operations we were performing for Buffers (back when it was relevant). Though the implementation has improved and the overhead in this use case is negligible at best. And honestly the O(n) lookup of __proto__ could be slower depending on the array, since the common case would be that __proto__ doesn't exist.

@JungMinu Sorry for the confusion here. Let's simply set var hash = Object.create(null);.

util: use Object.create(null) for dictionary object
@Fishrock123
Copy link
Contributor

Fair enough. My bad.

@jasnell
Copy link
Member

jasnell commented Nov 12, 2015

LGTM

@trevnorris
Copy link
Contributor

if CI is happy, LGTM

@cjihrig
Copy link
Contributor

cjihrig commented Nov 12, 2015

LGTM. Good to know that we may be able to use this in other place in the code too.


array.forEach(function(val, idx) {
array.forEach(function(val) {

Choose a reason for hiding this comment

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

Would we want to continue to use arrow functions for inline anons?

array.forEach(val => hash[val] = true);

and/or convert this into a single reduce expression?

function arrayToHash(array) {
  return array.reduce((acc, val) => {
    acc[val] = true;
    return acc;
  }, Object.create(null));
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Using reduce() is a bit misleading since nothing is actually being reduced, and I don't think turning it into a single expression is helpful in this case.

As for using arrow functions, personally only think they must be used to prevent var self = this; from happening, but also wouldn't be opposed if it were used. IOW, if it does change then cool, but it's not a blocker.

Choose a reason for hiding this comment

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

Ok cool. Yeah I guess I agree, the naming of Array.prototype.reduce is definitely a bit awkward here. Maybe the general arrow function stance is also worth a larger style/convention conversation elsewhere? Thanks for the input!

Copy link
Contributor

Choose a reason for hiding this comment

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

Ironically @JungMinu has already started some of this :) #3622

Hopefully we'll be able to finish conversion of other applicable cases, but it's not high priority. Don't think the CTC would feel the need to make a stance "official" but if the rest of the code is following some convention then we can point to that as an example going forward.

@Fishrock123
Copy link
Contributor

CI: https://ci.nodejs.org/job/node-test-pull-request/710/

(Continuous Integration test run)

@@ -159,9 +159,9 @@ function stylizeNoColor(str, styleType) {


function arrayToHash(array) {
var hash = {};
var hash = Object.create(null);;
Copy link
Contributor

Choose a reason for hiding this comment

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

This adds a redundant semicolon.

@jasnell
Copy link
Member

jasnell commented Nov 13, 2015

CI is red but failures look unrelated.
LGTM

@jasnell
Copy link
Member

jasnell commented Nov 13, 2015

@JungMinu .. can I ask you to please squash the commits?

@thefourtheye
Copy link
Contributor

I just used this script to compare the performance.

'use strict';

const times = 10e6;

console.time('Object.create');
for (var i = 0; i < times; i += 1) {
  let obj = Object.create(null);
}
console.timeEnd('Object.create');

console.time('Object Literal');
for (var i = 0; i < times; i += 1) {
  let obj = {};
}
console.timeEnd('Object Literal');

And it looks like Object.create is 10 times slower than object literal on my Ubuntu 14.04, 2 core, 4GB RAM machine. But, this will actually be a very small number. Only if this is not a problem, we could have fixed #2350 :(

@JungMinu
Copy link
Member Author

@jasnell Sure, I will squash the commits soon :)

@jasnell
Copy link
Member

jasnell commented Nov 15, 2015

Thank you!
On Nov 14, 2015 6:56 PM, "Minwoo Jung" [email protected] wrote:

@jasnell https://github.com/jasnell Sure, I will squash the commits
soon :)


Reply to this email directly or view it on GitHub
#3791 (comment).

@trevnorris
Copy link
Contributor

@thefourtheye If you look at the IR generated by the optimizing compiler you'll find much of the second call has been optimized out completely. Also, if you look at actual execution time you'll see that the savings is in the low double digit nanoseconds. The call is sufficiently fast for how it's used here.

@thefourtheye
Copy link
Contributor

@trevnorris Yup, the difference is not pronounced much. Probably, I'll have a shot at #2350 again.

@JungMinu
Copy link
Member Author

@jasnell @trevnorris Thanks a lot 😄

@@ -159,9 +159,9 @@ function stylizeNoColor(str, styleType) {


function arrayToHash(array) {
var hash = {};
var hash = Object.create(null);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not a Set instead?

@JungMinu JungMinu closed this Nov 15, 2015
@JungMinu JungMinu deleted the patch-18 branch November 15, 2015 04:43
@JungMinu JungMinu restored the patch-18 branch November 15, 2015 04:44
@JungMinu JungMinu reopened this Nov 15, 2015
@JungMinu
Copy link
Member Author

@jasnell @trevnorris Sorry, because of the conflicts in my branch, I submitted a new PR, #3831

@JungMinu JungMinu closed this Nov 15, 2015
jasnell pushed a commit that referenced this pull request Nov 16, 2015
Fixes #3788
`arrayToHash()` needs to use `Object.create(null)` for its dictionary object.

Refs: #3791
PR-URL: #3831
Reviewed-By: Trevor Norris <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Fishrock123 pushed a commit that referenced this pull request Nov 17, 2015
Fixes #3788
`arrayToHash()` needs to use `Object.create(null)` for its dictionary object.

Refs: #3791
PR-URL: #3831
Reviewed-By: Trevor Norris <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
@WebReflection
Copy link
Contributor

has anyone tested var hash = {__proto__: null} performance ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
util Issues and PRs related to the built-in util module.
Projects
None yet
Development

Successfully merging this pull request may close these issues.