-
-
Notifications
You must be signed in to change notification settings - Fork 929
Assorted Performance Improvements #3041
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
Conversation
The implementation adopts spread syntax, inspired by the approach mentioned in the comments. However, argument handling has been adjusted for better performance. Using spread syntax not only allows related comments to be deleted, but also slightly reduces bundle size and improves benchmarks by several percent. All existing tests pass successfully. Note that spread syntax does not seem to be supported by the existing internal bundler in some cases, so the bundler has also been updated to prevent incorrect suffix handling.
Since commit f9e5163, attrs is never null/undefined. However, `m.render()` takes into account the case where attrs is null/undefined, and it is better to use undefined from a performance point of view. Also, early return within `execSelector()` contributes to further performance improvement. Some tests are changed because of the null/undefined attrs. This commit may be breaking from this point of view, but the v2.0.4 era allows for attrs to be null/undefined and the impact will be quite small.
…rs are identical Since `compileSelector()` generates attrs objects containing only strings (no functions or nested objects). So, `updateAttrs()` can safely be skipped by checking the equality of the cached attrs objects themselves if the objects do not contain form attributes. If you mainly use the "static" attrs and do not use dynamic attrs, skipping `updateAttrs()` allows significant performance improvements. Also, the added checks are lightweight and there appears to be little or no performance degradation due to these checks. This commit itself is not a breaking change in the sense that it passes all existing tests. The checks in `m.render()` above and the form attribute checks in `compileSelector()` result in a minor increase in code volume, though.
This state.attrs.className != null check is redundant.
It seems that the current codebase (including generator, dom-for and spread syntax) already has little support for IE11 because it requires transpiling as well as polyfills.
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.
In addition to the other comments, I strongly would prefer existing behavior to remain intact, to minimize breakage. The tests shouldn't need changed for this PR.
As this results in a slight decrease in performance improvement, comments regarding potential performance improvements have also been added.
b150220
to
5f1ae33
Compare
This simplifies the processing within execSelector.
@dead-claudia Thank you for your kind comments. |
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.
One very minor nit and one request: could you run the perf numbers again?
Once those two happen, I'll approve it.
@dead-claudia Thank you for your comment.
|
@kfule What is the perf result for "rerender same tree (with selector-generated attrs)" for v2.3.4? |
@dead-claudia |
* hyperscriptVnode: use spread syntax The implementation adopts spread syntax, inspired by the approach mentioned in the comments. However, argument handling has been adjusted for better performance. Using spread syntax not only allows related comments to be deleted, but also slightly reduces bundle size and improves benchmarks by several percent. All existing tests pass successfully. Note that spread syntax does not seem to be supported by the existing internal bundler in some cases, so the bundler has also been updated to prevent incorrect suffix handling. * allow attrs to be undefined and execute early return in `execSelector()` Since commit f9e5163, attrs is never null/undefined. However, `m.render()` takes into account the case where attrs is null/undefined, and it is better to use undefined from a performance point of view. Also, early return within `execSelector()` contributes to further performance improvement. Some tests are changed because of the null/undefined attrs. This commit may be breaking from this point of view, but the v2.0.4 era allows for attrs to be null/undefined and the impact will be quite small. * use the cached attrs as-is and skip `updateAttrs()` if the static attrs are identical Since `compileSelector()` generates attrs objects containing only strings (no functions or nested objects). So, `updateAttrs()` can safely be skipped by checking the equality of the cached attrs objects themselves if the objects do not contain form attributes. If you mainly use the "static" attrs and do not use dynamic attrs, skipping `updateAttrs()` allows significant performance improvements. Also, the added checks are lightweight and there appears to be little or no performance degradation due to these checks. This commit itself is not a breaking change in the sense that it passes all existing tests. The checks in `m.render()` above and the form attribute checks in `compileSelector()` result in a minor increase in code volume, though. * refactor execSelector() This state.attrs.className != null check is redundant. * README: drop IE11 support It seems that the current codebase (including generator, dom-for and spread syntax) already has little support for IE11 because it requires transpiling as well as polyfills. * prevents existing tests from being changed As this results in a slight decrease in performance improvement, comments regarding potential performance improvements have also been added. * use a common empty attrs object This simplifies the processing within execSelector. * cachedAttrsIsStaticMap: use WeakMap to avoid potential memory leaks * hyperscriptVnode: add performance sensitivity comments * cachedAttrsIsStaticMap: revert back to Map from WeakMap with comments * pass emptyAttrs to the Map constructor This slightly reduces the bundle size.
I don't have a simple reprod yet, as haven't spent time looking into it, but FYI something regressed from this change. Most of our test suite is full of errors in
In case it's something due to the way our tests are written.... |
@chadlwilson Please file a new issue about this, so it's easier to track. You can link this PR in it. |
Understood, just wanted to double-check you're expecting zero regressions/behavioral changes from this before digging deeper. |
We were not expecting regressions, that is correct. The only observable changes should be in property access order within |
The line causing the error appears to be the Line 865 in c876faf
This seems odd since this PR is not a change regarding |
At a random guess, possibly because earlier it was breaking in the earlier clause while If i revert the changes just to Also goes away if I undo the bit from Will dig more tomorrow and raise a proper issue. I suspect that the earlier tests were missing a case that was important here. |
@chadlwilson Thank you for your comment!
I also want to confirm: is the error only occurring in the
I feel it's a bit odd that |
In MithrilJS#3041, it seemed that the case of non-element vnodes was not fully considered in terms of not breaking existing behavior.
@chadlwilson I created #3042. Will this branch resolve the issue? |
In MithrilJS#3041, it seemed that the case of non-element vnodes was not fully considered in terms of not breaking existing behavior.
The branch/pr does seem to fix our unit tests, thanks! I didn’t get the chance to run our more full regression suite or raise a proper issue today, sorry. Will try again tomorrow 🙏🏻 |
I raised a proper issue at #3043 - sorry for the delay. |
In #3041, it seemed that the case of non-element vnodes was not fully considered in terms of not breaking existing behavior.
In #3041, it seemed that the case of non-element vnodes was not fully considered in terms of not breaking existing behavior.
Description
This PR improves performance through the following changes:
execSelector()
when attrs isnull
orundefined
the attrs generated by the selector directlyundefined
orAllows attrs to beundefined
, skipping lifecycle-related processing during renderingupdateAttrs()
entirely when the attrs generated by a selector (without form attributes) remain unchangedIn order to support spread syntax, the internal bundler has also been changed in this PR.
result of
npm run perf
Edit: see #3041 (comment)
npm run perf
(on my laptop, node v22)As shown above, the performance of “construct large vnode tree”, “rerender same tree”, and “add large nested tree” will improve.
I've confirmed performance improvements even in real browsers, but detailed benchmark measurements would take much time, so I've put them off for now. (I'll conduct and compile them separately if needed.)
Note that "rerender same tree (with selector-generated attrs)" is measured by changing the code as follows. Only the attribute part has been changed from the original code.
Motivation and Context
I've been interested in improving mithril's performance since last year, and have been experimenting through trial and error. These changes are among those that proved effective with relatively minor code changes.
How Has This Been Tested?
npm run test
including new tests.Types of changes
Checklist