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

isEqual performance optimization #2180

Closed
wants to merge 13 commits into from
Closed

Conversation

gaperton
Copy link

Optimized isEqual for speed on primitive types. Now it's multiple times faster than lodash in all browsers.

Performance of isEqual is critical for Backbone.Model.set performance.

http://jsperf.com/underscore-isequal/5

Integer comparison:
Chrome: 70x faster
Firefox: 50x faster
Safari: 140x faster

return eq(a, b);
if (a === b) return a !== 0 || 1 / a === 1 / b;
if (typeof a != 'object' && typeof b != 'object') return a != a && b != b;
return a != null && b != null && deepEq(a, b);
Copy link
Author

Choose a reason for hiding this comment

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

You might wonder why eq function is inlined here.

Answer is that it gives 25-30% more performance in firefox, and about 10-15% in Chrome.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this not implemented as eq(a, b) looks like a lot of redundant code

Copy link
Author

Choose a reason for hiding this comment

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

Cause it's 25-30% slower. I have implemented it in eq initially, and measured the result. Was not good in FF. Intentionally redundant code (3 lines), which makes firefox significantly faster.

@jridgewell
Copy link
Collaborator

I think this is getting more complicated than is needed. @megawac's original code is able to get the same performance with common usage without another function or duplicating all the checks already in eq(). master...jridgewell:isEqual

@gaperton
Copy link
Author

@jridgewell, in fact, it's quite opposite. This is an only line of additional logic this change adds, all other are optimizations of existing code for modern browser's JIT.

if (typeof a != 'object' && typeof b != 'object') return a != a && b != b;

And in fact, "original code" is 2-3 times slower than this _.isEqual in every use case in Chrome. It's slower in every browser except FF. Different browsers have different JITs, you can't optimize just for one.

http://jsperf.com/underscore-isequal/8

@gaperton
Copy link
Author

Hey. Looks like lodash guys are already testing the fix, improving performance to match this function. :)

http://jsperf.com/underscore-isequal/6

That's enough. You do whatever you would like to do. I'm switching to lodash. Bye.

@gaperton
Copy link
Author

Ok, that was my last commit. Now you have one of the fastest recursive deepEq implementations currently available. Unfortunately, I have no time to deal with that issue any more.

@jashkenas
Copy link
Owner

Should this be closed then?

if (a === b) return a !== 0 || 1 / a === 1 / b;
var typeA = typeof a;
return typeA != 'function' && typeA != 'object' && typeof b != 'object' ?
a != a && b != b : a != null && b != null && deepEq(a, b);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is going to fail _.isEqual(null, null).

Copy link
Author

Choose a reason for hiding this comment

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

Of course it's not. _.isEqual(null, null) will go through 1208, which in fact is the first line of the original eq function unchanged.

Sometimes it's so hard to find a valid reason to close PR, right? :) Then some bullshit in comments comes to help. And then, brilliant idea instantly comes to your head and you create your own similar PR. Good job :) Gratz :)

@jdalton
Copy link
Contributor

jdalton commented May 22, 2015

Yap. Micro-opt is micro-opt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants