Bug #22127
openevent loop slowdowns during normal data table page loads
Description
The red bars represent a stalled event loop, this means the browser is basically unresponsive during these times. With a debug build, total time spent is about 8s.
The release build is better, but event loop is still blocked for about 2s:
One thing that is suspicious is that the stacked blue bars represent "requestContainerStatusCount" and it is only when all those requests are completed that there is a break in the event loop being blocked.
Files
Updated by Peter Amstutz 3 months ago
- Subject changed from filterResources is inefficient to event loop slowdowns during normal data table page loads
Updated by Peter Amstutz 3 months ago
- Assigned To changed from Peter Amstutz to Stephen Smith
Updated by Peter Amstutz 3 months ago
- Description updated (diff)
- File wb2-release-jank.png wb2-release-jank.png added
- File wb2-debug-jank.png wb2-debug-jank.png added
Updated by Peter Amstutz 3 months ago
From the "flame" chart, it also looks like it might be doing multiple expensive re-renders in that 'blocked event loop' period. Perhaps this is happening each time the redux state changes? What is happening here is that actions trigger other actions which independently mutate the state in several steps, which might trigger intermediate renders that are never seen by the user but are still slows things down.
Updated by Peter Amstutz 3 months ago
- Target version changed from Development 2024-09-25 sprint to Development 2024-10-09 sprint
Updated by Peter Amstutz 3 months ago
- Target version changed from Development 2024-10-09 sprint to Development 2024-10-23 sprint
Updated by Peter Amstutz 2 months ago
- Target version changed from Development 2024-10-23 sprint to Development 2024-11-06 sprint
Updated by Peter Amstutz about 2 months ago
- Target version changed from Development 2024-11-06 sprint to Development 2024-11-20
Updated by Peter Amstutz about 2 months ago
- Target version changed from Development 2024-11-20 to Development 2024-12-04
Updated by Peter Amstutz about 1 month ago
- Target version changed from Development 2024-12-04 to Future
Updated by Peter Amstutz 12 days ago
- Target version changed from Future to Development 2025-01-22
Updated by Peter Amstutz 10 days ago
- Target version changed from Development 2025-01-22 to Development 2025-01-08
- Assigned To set to Lisa Knox
Updated by Lisa Knox 3 days ago
- File actionCount_modified.png actionCount_modified.png added
- File actionCount_no_midware.png actionCount_no_midware.png added
- File actionCount_no_setCount.png actionCount_no_setCount.png added
- File actionCount_base.png actionCount_base.png added
- File projectpanelatarefresh.png projectpanelatarefresh.png added
- File profile_w_stagger.png profile_w_stagger.png added
What I believe is the primary source of lag is the way Data Explorer middlewares are initiated combined with the fact that all each new middleware triggers its own SET_COLUMNS call in each Data Explorer. There are 19 Data Explorers and 1 middleware for each, leading to an n^2 situation (19 * 19 = 361). Each Data Explorer has its own set of actions that are dispatched as well.
Here are all the actions dispatched when Workbench, viewing the Project Panel, is refreshed on the main branch (note that many things happen exactly 19 times, some happen 38 times (19 * 2), and SET_COLUMNS happens 380 times (19^2 + 19):
If we remove the calls to SET_COLUMNS, we see a marked improvement:
If we remove the middlwares that are not directly affecting the Project Panel:
In this state, workbench works 100% as expected as long as the only Data Explorer viewed is the Project Panel, including switching between projects. There is still some lag when switching between projects. This might be addressed by using web workers to offload some of the work to another JS thread and/or using scheduler.yield() where appropriate, which is where I will focus my efforts after this initial phase.
The long-term solution to the SET_COLUMNS problem is a substantial rewrite with the aim of lazily-loading each Data Explorer when needed, instead of loading all 19 on the initial page load/refresh.
I am still investigating alternatives to our current middleware implementation. These cannot be done lazily, but if we can offload their side effects until they are needed, we can still save a thousands of unnecessary actions.
I also investigated the "requestContainerStatusCount" issue, and implemented a wrapper that just yields control momentarily after each of the requests resolves. This doesn't speed up the page load time (it actually increases it by a tiny but nonzero amount), but it does break up the jank at several points, meaning UI input can be handled more often.
I have also applied some memoization to reduce some of the more problematic actions. The Data Table now renders twice instead of 16 times per page load, and a slight refactor means the side panel tree item renderer gets called 98 times vs 512 times. The reason for this is that it used a calculation that was located within the mapStateToDispatch for that component. This means it was re-calculated every time Redux state updated, whether or not the calculation was relevant to the component. Best prectice is that mapStateToProps should only select and map state properties directly, not compute derived data. Going through and removing any similar instances of this will go a long way to removing excess re-renders.
So far, my work on 22127-optimization @ 1b8d65a0bf940d5a7fe4a13bafc377ecdd3d0dad has yielded the following results:
The gross jank time, beginning to end, in the above picture is just under 3 seconds with the longest individual jank lasting 835ms. By comparison, Instagram, possibly the most optimized React application there is, averages 1.5-2 seconds of jank time on (logged-in) refresh.
Updated by Lisa Knox 1 day ago
We should also change it so that as soon as the user clicks to navigate to a new url, a loading screen is shown until the next resource is loaded. This will help mitigate visual confusion, as it is possible now that the Details Card of the new resource will be displayed before the data table changes.
Updated by Peter Amstutz about 24 hours ago
Lisa Knox wrote in #note-16:
We should also change it so that as soon as the user clicks to navigate to a new url, a loading screen is shown until the next resource is loaded. This will help mitigate visual confusion, as it is possible now that the Details Card of the new resource will be displayed before the data table changes.
+1
We discussed a few weeks ago using "skeleton" loading indicators (#22360), which are a bit less jarring & produce less flicker (compared to wiping the whole panel and showing a spinner) when transitioning between contents. I agree that when the old visual content stays on screen too long, it's very confusing.