-
Notifications
You must be signed in to change notification settings - Fork 30k
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
querystring: avoid conflicts with Object.prototype and __proto__ #5722
Conversation
I finally had some time today to do some perf testing around this. I compared the changes in this PR against current master and both the current node benchmarks and benchmark.js-based benchmarks agree, there is a perf regression for each benchmark input anywhere from 1% to 20% (most are towards the higher end though). On the other hand, if we take the existing code and merely add a check for A third option that had some success was instead creating |
Benchmarks they told me to do showed improvements, not regressions, for most common cases where there are not so many pairs. Only in the pairs case there's obviously some regression because of the double I'd like to see/know/try these benchmarks you are talking about but I'd also would like to raise a concern about targeting The bug this PR is trying to solve is any accessor that could be set for whatever obtrusive, polyfill, or valid reason, in the Targeting However, all tests I've seen about Have you benchmarked that too? |
I'm using the benchmarks from I'm not sure what you're saying about |
Actually, this is the for (var i = 0, l = 1000, o = {}; i < l; o[parseInt(Math.random() * l)] = i++);
var keys = Object.keys(o);
var i = 0, r1 = 0;
console.time('the `in` operator');
while (i < l) {
// String(i++) implicit
// but to have a fair bench ...
if (String(i++) in o) r1++;
}
console.timeEnd('the `in` operator');
var i = 0, r2 = 0;
console.time('the indexOf() operation');
while (i < l) {
if (keys.indexOf(String(i++)) !== -1) r2++;
}
console.timeEnd('the indexOf() operation');
console.assert(r1 === r2, 'should have same result, instead: ' + r1 + ' & ' + r2); And not a single run showed The average is $ node bench.js
the `in` operator: 0.857ms
the indexOf() operation: 4.249ms I'm really curious to see your benchmarks because things don't look right to me. |
@WebReflection The benchmarks are here. It's not always enough to test differences like that in isolation. |
Yeah, sorry for the overlap, I've received your notification after I've sent the comment but pelase double check my very simple bench that compares Trying to answer your other concerns:
That's the point of creating TL;DR ... every discussion I've seen (recently) about Regardless the |
I'm sorry @mscdex but I think you are reading benchmarks upside down. Both me and @evanlucas can confirm my PR is on average up to 22% faster than current master: only the last benchmark is 3% up to 14% slower. Would you mind reconsidering your opinion about this PR? Because I think you are reading the bench in the wrong way. To sum it up: the first 6 benchmarks are always faster with this PR, only the last bench, which is the worst case, is slower for explained reasons ( the |
@WebReflection Typically the binary with the new changes is supposed to be first and the old binary is second on the command line (after the I am pretty sure the benchmark results on my end are not the "wrong way" as I have been running node.js benchmarks for quite some time. I have even double checked that the binaries are correct by verifying the output of |
so you are saying that this comment is showing that this PR is slower, while I am saying all tests but last are negative because the PR is faster, right? Then I don't understand why the only test that has many pairs and will involve both That makes absolutely no logic sense to me, but of course I've no idea what V8 would do there to make a double check faster than a single one. If you are sure 100% this PR is slower (it shouldn't, logically speaking) I can try using If I've got the test completely wrong though, apologies for pushing twice this PR and thinking you read it wrong. (but I'm going to investigate with some V8 core person about this 'cause again, it doesn't feel right to me) |
FWIW I just benchmarked |
@WebReflection your current PR is failing the linter. Please update and force push |
Dho ..it's a diff from previous PR , weird it's failing but I'll force push later. About |
@WebReflection I'm only benchmarking with the |
@mscdex if that's your test you should really go for the |
Currently, the literal returned object is swallowing all methods and properties inherited from the Object.prototype. This includes the `__proto__` special case and this PR aim is to fix all these inherited properties at once, using the `in` operator before checking the array of known keys.
@thealphanerd I've push forced after a commit --amend ... I hope it's OK now |
so @mscdex with this test: var r1, r2, i, l = 1000;
i = 0;
console.time('Object.create(null)');
while (i++ < l) r1 = Object.create(null);
console.timeEnd('Object.create(null)');
i = 0;
console.time('{__proto__:null}');
while (i++ < l) r2 = {__proto__:null};
console.timeEnd('{__proto__:null}');
console.assert(Object.keys(r1).join(',') === Object.keys(r2).join(','), 'should have same keys'); I have these results: [webreflection@archibold test]$ node bench.js
Object.create(null): 0.945ms
{__proto__:null}: 0.180ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.916ms
{__proto__:null}: 0.174ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.927ms
{__proto__:null}: 0.177ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.944ms
{__proto__:null}: 0.177ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.914ms
{__proto__:null}: 0.175ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.960ms
{__proto__:null}: 0.181ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.933ms
{__proto__:null}: 0.176ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.948ms
{__proto__:null}: 0.176ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.916ms
{__proto__:null}: 0.173ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.935ms
{__proto__:null}: 0.177ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.936ms
{__proto__:null}: 0.202ms
[webreflection@archibold test]$ node bench.js
Object.create(null): 0.951ms
{__proto__:null}: 0.178ms As unreliable as it is, it looks reasonable to me. |
@WebReflection Like I said before, you cannot always rely on simple benchmarks in isolation like that. You need to test the changes (e.g. using the existing benchmarks) in core itself to get more realistic measurements. From my benchmarking (using the existing benchmarks) comparing the two, Also, using |
well, if isolation isn't good, what makes you think the artificial benchmark tells the truth? At this point I am hoping for some V8 expert to tell me what's going on because I can isolate all fast paths but apparently putting them together is slower? Again, nonsense to my understanding on how V8 would optimize. That being said the So what are you proposing to fix the bug #5642 in a broader way? |
Alternative fix: #6044 |
Recommend closing this in favor of #6044 |
Closing now that #6055 has landed. |
Currently, the literal returned object is swallowing all
methods and properties inherited from the Object.prototype.
This includes the
__proto__
special case and this PRaim is to fix all these inherited properties at once,
using the
in
operator before checking the array ofknown keys.
This PR has been previously described and tested in #5650
and it has been made from scratch to avoid confusion.
The benchmark shows overall 4 out of 5 tests are 2% up to 22%
faster and one test, the worst case scenario with many pairs,
slower between 3% and 14% than before.