-
Notifications
You must be signed in to change notification settings - Fork 8.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
[SIEM] Adds performance enhancements such by removing wasted renderers and adding incremental DOM rendering #43157
[SIEM] Adds performance enhancements such by removing wasted renderers and adding incremental DOM rendering #43157
Conversation
Pinging @elastic/siem |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx
Outdated
Show resolved
Hide resolved
x-pack/legacy/plugins/siem/public/components/super_date_picker/index.test.tsx
Show resolved
Hide resolved
This comment has been minimized.
This comment has been minimized.
x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.test.ts
Outdated
Show resolved
Hide resolved
isFavorite={isFavorite} | ||
timelineId={timelineId} | ||
updateIsFavorite={updateIsFavorite} | ||
showDescription={width >= showDescriptionThreshold} |
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.
i appreciate how moving the width
threshold check up to this component eliminates the need to propagate width
to children, since width
updates when the timeline is resized 👍
* 100 milliseconds through the range of .range([100, 2000]). | ||
* | ||
* NOTE: ScaleLog cannot be given a zero, so the domain starting at 1 | ||
* like so: domain([1, 100]) is intentional. |
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.
👍
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.
Thanks @FrankHassanabad for all the profiling and time you’re spending in the chrome & react dev tools to improve performance, and especially for the incremental loading you implemented to break up the rendering of events in this PR.
Tested locally in Chrome, FF, and Safari
LGTM 🚀
…added more React.memo for more performance gains
This comment has been minimized.
This comment has been minimized.
Please merge master to fix percy failure |
This comment has been minimized.
This comment has been minimized.
…ompact vs. non-compact
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
retest this |
getUpdatedAt={getUpdatedAt} | ||
width={width} | ||
/> | ||
<TimelineContext.Provider value={{ isLoading: loading }}> |
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.
great catch here! ⚾️
@@ -106,7 +104,6 @@ class StatefulBodyComponent extends React.PureComponent<StatefulBodyComponentPro | |||
addNoteToEvent={this.onAddNoteToEvent} | |||
browserFields={browserFields} | |||
id={id} | |||
isLoading={isLoading} | |||
columnHeaders={columnHeaders || []} |
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.
optional since I don't think we saw an impact while profiling, but perhaps we should replace || []
with a stable reference?
columnHeaders={columnHeaders} | ||
columnRenderers={columnRenderers} | ||
expanded={expanded} | ||
data={data} |
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.
optional: move this one line up, and since _id
is now id
, it can also optionally move down
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.
@FrankHassanabad went above and beyond after the initial PR review to add even more performance gains, which required a lot of detective work 🕵️♀️ , profiling, and finding perf issues in code that seemed innocuous 🥂
LGTM X2 🚀 🚀
💚 Build Succeeded |
…s and adding incremental DOM rendering (elastic#43157) ## Summary Whenever you wiggle the "resize" bar on the timeline or any other action within the UI which alters the redux store, all the components `mapStateProps` will and redux will re-render if it sees any of the properties have a shallow compare that returns false. Ref: https://react-redux.js.org/using-react-redux/connect-mapstate This PR fixes Super Date Selector which unnecessarily causes re-renders due to not using selectors or memoizations per guidance from the react-redux project. --- Super Date Selector will re-render on every single change to the redux store regardless of if it needs to render or not. <img width="2072" alt="Screen Shot 2019-08-12 at 11 49 13 AM" src="https://user-images.githubusercontent.com/1151048/62900648-eaa39f80-bd17-11e9-91fd-ed4b1e81e27b.png"> You can see that with something as simple as a slight wiggle of the resizer of timeline causes around 23'ish re-renders <img width="2338" alt="Screen Shot 2019-08-12 at 11 52 54 AM" src="https://user-images.githubusercontent.com/1151048/62900666-f7c08e80-bd17-11e9-8ddd-77093553aaf9.png"> After the fixes it no longer shows it being re-rendered on a redux change and the number or renders drop significantly when resizing timeline. It renders only once: <img width="1857" alt="Screen Shot 2019-08-12 at 3 51 20 PM" src="https://user-images.githubusercontent.com/1151048/62901080-1c693600-bd19-11e9-8f8e-f19df029dd31.png"> --- Some parts of the application are expensive to render and are wasted renders by something such as anonymous functions. By swapping out the `pure` for `React.memo` and using the property compare we can cut out those renders and speed up the application considerably. Before with all the flyout sections causing re-renders when state did not change: <img width="1718" alt="Screen Shot 2019-08-12 at 4 46 29 PM" src="https://user-images.githubusercontent.com/1151048/62959566-589ea400-bdb6-11e9-89f1-047c268e74fd.png"> After with the fix to where the ms is really cheap and the expensive pieces are not rendered unless needed now: <img width="1682" alt="Screen Shot 2019-08-13 at 10 32 55 AM" src="https://user-images.githubusercontent.com/1151048/62959667-82f06180-bdb6-11e9-9cc9-c9c609a0fb71.png"> --- For the timeline and its properties section it contained a large volume of JSX logic called "PropertiesLeft" and "PropertiesRight" in which both were having re-renders involving expensive calculations such as width changes to the date time when you did something as simple as type in a new title or description into the timeline. I broke those two out into PropertiesLeft and PropertiesRight subsections utilizing `React.Memo` for performance improvements. Before when typing a title of timeline, PropertiesRight renders 49+ times: <img width="2030" alt="properties-right-before" src="https://user-images.githubusercontent.com/1151048/63056285-f7093300-bea4-11e9-8f33-a2e63d0468ac.png"> Now it renders only 2 times and most of the time it is skipped with something as simple as a title changing: <img width="2020" alt="properties-right-after" src="https://user-images.githubusercontent.com/1151048/63056339-10aa7a80-bea5-11e9-9c0c-ae12be5b5851.png"> --- When rending feature rich renderers on the timeline it was having a difficult time rendering them all within a reasonable amount of time. This introduces a simple scheduler of incremental loading using `requestIdleCallback` when it is available from the browser, otherwise this will fall back on a `setTimeout` as a polyfill/shim. Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback https://developers.google.com/web/updates/2015/08/using-requestidlecallback https://www.w3.org/TR/requestidlecallback/ Tested on Safari, Firefox, and Chrome. IE-11 looked like it didn't crash :-) Gif of each of the browsers incrementally rendering the rows: Chrome: ![incremental-load-1](https://user-images.githubusercontent.com/1151048/63057036-8fec7e00-bea6-11e9-83f9-7bad3f79746f.gif) ![incremental-load-2](https://user-images.githubusercontent.com/1151048/63057043-92e76e80-bea6-11e9-8f0d-95ea2559b6c7.gif) Firefox: ![firefox-incremental-1](https://user-images.githubusercontent.com/1151048/63057052-97138c00-bea6-11e9-8e15-19f5b7cb0a5e.gif) ![firefox-incremental-2](https://user-images.githubusercontent.com/1151048/63057060-98dd4f80-bea6-11e9-9bf3-c8ae798db9e4.gif) Safari (polyfil/shim only working): ![safari-polyfill-1](https://user-images.githubusercontent.com/1151048/63057099-adb9e300-bea6-11e9-9b94-32a08b5905ad.gif) ![safari-polyfill-2](https://user-images.githubusercontent.com/1151048/63057101-b01c3d00-bea6-11e9-9261-77444272992d.gif) Before Performance picture of the full load. You can see that it will render all rows of 25+ and that causes a lag of up to 2 seconds depending on how complex the renderers are: <img width="1346" alt="Screen Shot 2019-08-13 at 4 24 56 PM" src="https://user-images.githubusercontent.com/1151048/63059739-77339680-bead-11e9-818a-ee19df596855.png"> Afterwards you can see it chops it more evenly up between the rendering and tries to get in 5 at a time and maintain a 60 FPS: <img width="691" alt="Screen Shot 2019-08-14 at 3 59 31 PM" src="https://user-images.githubusercontent.com/1151048/63059764-83b7ef00-bead-11e9-84da-bf66de8a35e6.png"> ### Checklist Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR. ~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~ ~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~ ~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~ - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios ~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~ ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process) - [x] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)
…s and adding incremental DOM rendering (#43157) (#43500) ## Summary Whenever you wiggle the "resize" bar on the timeline or any other action within the UI which alters the redux store, all the components `mapStateProps` will and redux will re-render if it sees any of the properties have a shallow compare that returns false. Ref: https://react-redux.js.org/using-react-redux/connect-mapstate This PR fixes Super Date Selector which unnecessarily causes re-renders due to not using selectors or memoizations per guidance from the react-redux project. --- Super Date Selector will re-render on every single change to the redux store regardless of if it needs to render or not. <img width="2072" alt="Screen Shot 2019-08-12 at 11 49 13 AM" src="https://user-images.githubusercontent.com/1151048/62900648-eaa39f80-bd17-11e9-91fd-ed4b1e81e27b.png"> You can see that with something as simple as a slight wiggle of the resizer of timeline causes around 23'ish re-renders <img width="2338" alt="Screen Shot 2019-08-12 at 11 52 54 AM" src="https://user-images.githubusercontent.com/1151048/62900666-f7c08e80-bd17-11e9-8ddd-77093553aaf9.png"> After the fixes it no longer shows it being re-rendered on a redux change and the number or renders drop significantly when resizing timeline. It renders only once: <img width="1857" alt="Screen Shot 2019-08-12 at 3 51 20 PM" src="https://user-images.githubusercontent.com/1151048/62901080-1c693600-bd19-11e9-8f8e-f19df029dd31.png"> --- Some parts of the application are expensive to render and are wasted renders by something such as anonymous functions. By swapping out the `pure` for `React.memo` and using the property compare we can cut out those renders and speed up the application considerably. Before with all the flyout sections causing re-renders when state did not change: <img width="1718" alt="Screen Shot 2019-08-12 at 4 46 29 PM" src="https://user-images.githubusercontent.com/1151048/62959566-589ea400-bdb6-11e9-89f1-047c268e74fd.png"> After with the fix to where the ms is really cheap and the expensive pieces are not rendered unless needed now: <img width="1682" alt="Screen Shot 2019-08-13 at 10 32 55 AM" src="https://user-images.githubusercontent.com/1151048/62959667-82f06180-bdb6-11e9-9cc9-c9c609a0fb71.png"> --- For the timeline and its properties section it contained a large volume of JSX logic called "PropertiesLeft" and "PropertiesRight" in which both were having re-renders involving expensive calculations such as width changes to the date time when you did something as simple as type in a new title or description into the timeline. I broke those two out into PropertiesLeft and PropertiesRight subsections utilizing `React.Memo` for performance improvements. Before when typing a title of timeline, PropertiesRight renders 49+ times: <img width="2030" alt="properties-right-before" src="https://user-images.githubusercontent.com/1151048/63056285-f7093300-bea4-11e9-8f33-a2e63d0468ac.png"> Now it renders only 2 times and most of the time it is skipped with something as simple as a title changing: <img width="2020" alt="properties-right-after" src="https://user-images.githubusercontent.com/1151048/63056339-10aa7a80-bea5-11e9-9c0c-ae12be5b5851.png"> --- When rending feature rich renderers on the timeline it was having a difficult time rendering them all within a reasonable amount of time. This introduces a simple scheduler of incremental loading using `requestIdleCallback` when it is available from the browser, otherwise this will fall back on a `setTimeout` as a polyfill/shim. Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback https://developers.google.com/web/updates/2015/08/using-requestidlecallback https://www.w3.org/TR/requestidlecallback/ Tested on Safari, Firefox, and Chrome. IE-11 looked like it didn't crash :-) Gif of each of the browsers incrementally rendering the rows: Chrome: ![incremental-load-1](https://user-images.githubusercontent.com/1151048/63057036-8fec7e00-bea6-11e9-83f9-7bad3f79746f.gif) ![incremental-load-2](https://user-images.githubusercontent.com/1151048/63057043-92e76e80-bea6-11e9-8f0d-95ea2559b6c7.gif) Firefox: ![firefox-incremental-1](https://user-images.githubusercontent.com/1151048/63057052-97138c00-bea6-11e9-8e15-19f5b7cb0a5e.gif) ![firefox-incremental-2](https://user-images.githubusercontent.com/1151048/63057060-98dd4f80-bea6-11e9-9bf3-c8ae798db9e4.gif) Safari (polyfil/shim only working): ![safari-polyfill-1](https://user-images.githubusercontent.com/1151048/63057099-adb9e300-bea6-11e9-9b94-32a08b5905ad.gif) ![safari-polyfill-2](https://user-images.githubusercontent.com/1151048/63057101-b01c3d00-bea6-11e9-9261-77444272992d.gif) Before Performance picture of the full load. You can see that it will render all rows of 25+ and that causes a lag of up to 2 seconds depending on how complex the renderers are: <img width="1346" alt="Screen Shot 2019-08-13 at 4 24 56 PM" src="https://user-images.githubusercontent.com/1151048/63059739-77339680-bead-11e9-818a-ee19df596855.png"> Afterwards you can see it chops it more evenly up between the rendering and tries to get in 5 at a time and maintain a 60 FPS: <img width="691" alt="Screen Shot 2019-08-14 at 3 59 31 PM" src="https://user-images.githubusercontent.com/1151048/63059764-83b7ef00-bead-11e9-84da-bf66de8a35e6.png"> ### Checklist Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR. ~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~ ~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~ ~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~ - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios ~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~ ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process) - [x] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)
…_update_json_spec * 'master' of github.com:elastic/kibana: (35 commits) fix: 🐛 pass whole action context to isCompatible() method (elastic#43457) Deleted old kbn-top-nav directive (elastic#43168) [ML] Fixing cloning of single metric distinct count job (elastic#43435) Update @elastic/charts version 8.1.6 > 9.1.1 (elastic#43516) [Inspector Views] [Request View] - Migrate inspector_views to new platform (elastic#43191) [ML] Adding loading indicators to all wizard charts (elastic#43382) disable flaky test (elastic#43492) feature(code/frontend): cancel file blob and directory commits request if outdated (elastic#43348) fix(code/frontend): button group url should have previous query string (elastic#43428) [SIEM] Fixes index substring incorrectly matching configured indices and failing to install ML job (elastic#43409) [SIEM] Adds performance enhancements such by removing wasted renderers and adding incremental DOM rendering (elastic#43157) disable flaky test (elastic#37859) Added sass lint to Canvas (elastic#43410) [Maps] add indicator when layer is filtered by search bar (elastic#43283) Properly validate current user password during password change. (elastic#43447) Spaces - allow for hex color codes that include uppercase characters (elastic#43470) [Reporting] Add a bit more logging and a few more logging level promotions (elastic#43415) Partially convert index pattern server to typescript (elastic#43291) [Infra UI] Use sum for aggregating AWS metrics. (elastic#43293) [SIEM] Format bytes columns in timeline (elastic#43147) ...
Summary
Whenever you wiggle the "resize" bar on the timeline or any other action within the UI which alters the redux store, all the components
mapStateProps
will and redux will re-render if it sees any of the properties have a shallow compare that returns false.Ref: https://react-redux.js.org/using-react-redux/connect-mapstate
This PR fixes Super Date Selector which unnecessarily causes re-renders due to not using selectors or memoizations per guidance from the react-redux project.
Super Date Selector will re-render on every single change to the redux store regardless of if it needs to render or not.
You can see that with something as simple as a slight wiggle of the resizer of timeline causes around 23'ish re-renders
After the fixes it no longer shows it being re-rendered on a redux change and the number or renders drop significantly when resizing timeline.
It renders only once:
Some parts of the application are expensive to render and are wasted renders by something such as anonymous functions. By swapping out the
pure
forReact.memo
and using the property compare we can cut out those renders and speed up the application considerably.Before with all the flyout sections causing re-renders when state did not change:
After with the fix to where the ms is really cheap and the expensive pieces are not rendered unless needed now:
For the timeline and its properties section it contained a large volume of JSX logic called "PropertiesLeft" and "PropertiesRight" in which both were having re-renders involving expensive calculations such as width changes to the date time when you did something as simple as type in a new title or description into the timeline. I broke those two out into PropertiesLeft and PropertiesRight subsections utilizing
React.Memo
for performance improvements.Before when typing a title of timeline, PropertiesRight renders 49+ times:
Now it renders only 2 times and most of the time it is skipped with something as simple as a title changing:
When rending feature rich renderers on the timeline it was having a difficult time rendering them all within a reasonable amount of time. This introduces a simple scheduler of incremental loading using
requestIdleCallback
when it is available from the browser, otherwise this will fall back on asetTimeout
as a polyfill/shim.Ref:
https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
https://developers.google.com/web/updates/2015/08/using-requestidlecallback
https://www.w3.org/TR/requestidlecallback/
Tested on Safari, Firefox, and Chrome. IE-11 looked like it didn't crash :-)
Gif of each of the browsers incrementally rendering the rows:
Chrome:
Firefox:
Safari (polyfil/shim only working):
Before Performance picture of the full load. You can see that it will render all rows of 25+ and that causes a lag of up to 2 seconds depending on how complex the renderers are:
Afterwards you can see it chops it more evenly up between the rendering and tries to get in 5 at a time and maintain a 60 FPS:
Checklist
Use
strikethroughsto remove checklist items you don't feel are applicable to this PR.- [ ] This was checked for cross-browser compatibility, including a check against IE11- [ ] Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support- [ ] Documentation was added for features that require explanation or tutorials- [ ] This was checked for keyboard-only and screenreader accessibilityFor maintainers