-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
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
[utils] Add small optimization: composeClasses #41488
Conversation
Netlify deploy previewhttps://deploy-preview-41488--material-ui.netlify.app/ @material-ui/core: parsed: +0.06% , gzip: +0.07% Bundle size reportDetails of bundle changes (Toolpad) |
8b9aac1
to
bf36500
Compare
Hey, @romgrk! Thanks for working on this 😊 How urgent is this improvement for the X team? I'm asking because we're releasing the last v5 version next week (except for urgent fixes), after which we'll start working on the If the X team needs this right away, we'll have to get it reviewed and approved by Monday, which might be too close. If this can wait, we can point the PR to the Besides that, I'm curious: would you expect to avoid |
Not urgent, it can wait.
No, as I mentionned performant code is often a tradeoff for readable code. What we do in the datagrid is we avoid expensive code patterns at specific locations, and use more readable patterns where the performance requirements are not as high. For example, anything that runs during scroll events (very tight runtime budget), at the cell level (scales with the number of cells on screen) or during filtering (scales with the number of total rows) is a good candidate for optimization.
In this case it's clear that allocating + filling + joining the array is slower, but it depends on the case. In V8 (the most frequent engine by far) this case would probably fall into the ConsString representation where Sidenote, one unfortunate performance characteric I noted on V8 though is that |
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.
What we do in the datagrid is we avoid expensive code patterns at specific locations, and use more readable patterns where the performance requirements are not as high. [...] The best option imho is to benchmark first, and optimize when we see something expensive in the profiling results.
This makes sense, we might want to adopt this as well @mnajdova
}, | ||
); | ||
} | ||
output[slotName] = result; |
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.
Would output[slotName] = result.trim()
(to remove the trailing space) defeat the optimization's purpose?
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.
It would, the V8 ConsString
I linked above is efficient because it doesn't need to copy+write the string to perform concatenation, it can simply use linked-list (or linked-tree) operations. Trimming it however requires to 1. turn the string into an actual array of bytes, and 2. create a trimmed copy of those bytes. Considering that this is for classnames, it feels very acceptable to leave additional whitespaces in.
You can see the performance cost here.
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.
Fair enough
The code looks good to me. Before approving, I would like to ask @mui/core if anyone can foresee an issue with adding the extra space after the classes. We can move forward if nothing comes up in a couple of days. The only one I can think of is that hardcoded tests in users' tests might break if they match the class names string without a space or have snapshots that include the strings. We could add this to https://github.com/mui/material-ui/blob/next/docs/data/material/migration/migration-v5/migration-v5.md as a breaking change to be cautious; what do you think, @romgrk? |
Yes, that would be possible. Tbh I'm not sure it's worth it at the moment, and I think I can get rid of some of the costs more efficiently without breaking changes once I apply the style API improvement. I'll close it for now, I'll get back to this later. |
I have been doing some more benchmarking on the datagrid and I see that
composeClasses
makes up around 1.5% of the CPU time during scroll events, but also in general during any rendering-heavy code (e.g. initial mount). I have run the benchmarks with just MUI core components (e.g. 40 buttons, 40 text fields) and the results are the same, which makes sense as we use the same patterns in both codebases.`composeClasses` profile
I have applied different optimizations to the original implementation, and was able to get it to run about 3x faster. By using this implementation, we'd reduce our rendering runtime in general by around 1%, which is an interesting result for optimizing just one function.
optimization rounds results (benchmark code)
The obvious downside is that performance-optimized code is less readable. The table above presents the optimization improvements for each successive step, if the code proposed here is too exotic, you could also propose which optimizations make sense to include. The most impactful by far is avoiding accumulating strings in an array.