-
Notifications
You must be signed in to change notification settings - Fork 4k
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
perf(lodash): remove use of _.omit (and optimize lodash) #864
Comments
@levithomason - Two questions regarding performance:
|
I used a short hack: import ReactPerf from 'react-addons-perf'
window.perf = ReactPerf Then, on the console: perf.start()
// do stuff you want to get perf on
perf.stop()
perf.printExclusive() We could totally capture these results and do something automated I'm sure. Hadn't thought of that! |
I'll admit I'd be interested in hearing @jdalton 's thoughts on the perf and bundle size issues. |
Hi @markerikson! Using webpack and babel plugins That said, From a perf standpoint Lodash is a collection of general purpose utilities. It's certainly the case that more specialized and context aware implementations can be faster. I'd say specialize where needed and defer to a utility lib in most other cases where you don't feel like the maintenance burden. |
Dropping |
From my initial reply:
It's easier to pick than to omit. A pick is also likely at least 4x faster than your current omit. |
@jdalton I very much appreciate the extra context and deeper insight. I would love to go with some solution similar to your last suggestion:
Any other info or resources you have regarding lodash perf would also be greatly appreciated. |
I'm not familiar with your usage but if you give me a list of methods and scenarios I can review them. |
Looks like we're using 32 methods at present:
The usage scenarios are wide ranging but the majority of these methods are called at some point during the component render cycle. I don't think other usages would pose much of an issue since they are likely to be called very infrequently, or just once. |
@levithomason Cool. With lodash-webpack-plugin the size is ~6kB min+gzip. Of those the most expensive are |
This is awesome, we can certainly live with 4-6kB, even something many times this size is no issue IMO. I had not heard of the webpack plugin prior to your first response either. I'll drop that in and make the updates you've noted and see where we're at. Not sure when I'll get to this, but it won't be long. Thanks much! |
The |
Yep, that one has been on the list to replace with |
I think even |
Checking their source it does appear so. We only need a simple shallow strict equality check on object values. I'll likely implement this as well 👍 |
Important note: Semantic-UI-React 0.60.9 had a major perf issue with how Lodash was being used to omit props. This was resolved in 0.60.10, so future installs should be okay. References: Semantic-Org/Semantic-UI-React#860 Semantic-Org/Semantic-UI-React#864
I was previously on 0.60.9. Unfortunately, that apparently had a huge performance issue, due to the way Lodash was being used to omit props that components didn't handle themselves. That resulted in increasingly slow performance to switch tabs as I added more components per tab panel (and not that many components, either!). The perf issue was fixed in 0.60.10, naturally. References: Semantic-Org/Semantic-UI-React#860 Semantic-Org/Semantic-UI-React#864
_.omit
Per #860, we've improved the render times of all components by ~12,000 times with a 4 line vanilla
_.omit
replacement.The issue here was that omit makes copies of its object arguments, the latter argument appears to be deep copied with circular reference checks. That second argument happened to be
props
, which of course includes children, which of course include children, ...omg.Every component uses
getUnhandledProps
, on every render, so every update caused every component to make a deep copy of its props and children's props all the way down the render tree util it reached the end of the tree. Then, repeat this for every node in the tree. It is obvious why Grid and Grid column were top offenders, they are mostly likely to contain the most deeply nested children.We have no need to ever make a full copy of objects like this. We ought to replace all uses of
_.omit
and other methods that copy objects and replace them with our own simple util.Lodash must go :(
This would be a massive undertaking but we should, unfortunately, consider completely replacing lodash if we're serious about performance. This could be done with a shim module that replaces one method at a time (e.g.
lib/lodash
) and uses the lodash method if it has no replacement.What? Why would you?
It's cherry-picked by
babel-plugin-lodash
, so it the footprint is small, right?Webpack ^ this alone and you get this bundle:
83.6 kB
All of this code is lodash. Its internal util dependency tree is massive, even when cherry-picking methods. This one method ends up taking what can be done in 4 LOC and turns it into an 84kb module with 96 dependencies. This is utterly insane.
Isn't it really performant, sometimes faster than native?
Sure, however, as shown above and in #860, we achieved >12,000 times performance gain with a 4 line vanilla JS method.
How large is our cherry-picked lodash dependency?
It's 30% of the uncompressed library size, 278kb. When Semantic-UI-React is minified and propTypes are striped, it is only
288K
itself.The text was updated successfully, but these errors were encountered: