Closed BrittanyIRL closed 2 years ago
After implementing virtualization in the dashboard, I ran several lighthouse tests (in the local environment). While the performance scores were much lower there compared to staging, we could still compare pre and post virtualization locally.
Adding virtualization in both the dashboard and explore templates pages did not noticeably change the lighthouse score. I ran the tests for each 10 times. The average of the performance scores after 10 times showed very little change.
Dashboard
Before (Avg over 10 runs) | After (Avg over 10 runs) |
---|---|
59 | 59 |
Templates
Before (Avg over 10 runs) | After (Avg over 10 runs) |
---|---|
29 | 30 |
Not seeing noticeable change by adding virtualization. This could be due to the data being paginated. I'm guessing virtualization would help once many stories or templates were loaded (>1000?) but to get to that point the user would have to scroll to load more stories for a while. This would never happen with templates because there aren't that many templates.
For now I think no action should be taken on this ticket cc @BrittanyIRL
Thanks, @samwhale !
Was there a considerable difference in the Avoid an excessive DOM size
diagnostic? This is why i thought virtualizing the grid may be helpful:
Oh good callout. Checked again and there is a considerable difference here:
Before | After |
---|---|
I didn't see this metric before. This difference suggests that virtualizing these grids would be helpful. The infinite scrolling logic will have to be changed a little to account for the virtualization of the grid. I'll get started on that.
Do the numbers above include actual user action like scrolling through the list?
Were there any noticeable differences when analyzing with React profiler or the Performance recorder in dev tools?
It seems to me that the lighthouse test only runs the initial page load. I'll grab some data from the profiler when the list is scrolled.
However, the number of elements rendered should remain about the same with the virtualized grid.
Scrolling a virtual list puts the burden on rendering the rows while scrolling, whereas not virtualizing the list places the burden on the first render (and subsequent renders when more data is fetched). Once the rows are rendered, then the browser doesn’t need to worry about them. Note: this only really matters with "small" amounts of rows.
For rendering and scroll performance, there is nothing that’s blocking interaction for either approach with < 500 items or so (on a decently fast computer). Luckily, our stories are already paginated so a user can't end up with 500+ stories on initial page load. It's not very costly to render with stories on page load and even after fetching many pages.
In the pictures below we can see that more work is done scripting while scrolling the virtualized list since new rows have to be rendered to the screen as the user scrolls.
The non-virtualized list instead renders all of them at once once the stories have been fetched (gray section at the top left of the graph), as well as on any subsequent fetches.
Non virtualized | Virtualized |
---|---|
Looking at these two graphs, we see that scrolling a virtual list takes more work while scrolling whereas no work has to be done after rendering all the stories at once.
However, there are some more things to consider If anybody were to scroll to 500 stories (aside: do users make 500 stories?) then they could run into problems with both approaches.
Scrolling is super smooth using the current approach. To analyze this, I used the FPS (frames-per-second) tracker in the chrome dev tools. Users may notice lag if the rate drops down below 45 or 40 FPS. Notice in the gif below that the FPS doesn't drop below 50.
Note: scrolling fast drops the frame rate. Users may not scroll that fast, so it may even be better than what is presented. Not to mention that this is not in a production environment where we'd have an optimized build.
However, a problem arises when many stories are fetched and rendered in the DOM and the user scrolls to the bottom of the page.
Scrolling to the bottom of the page initiates another fetch for new stories. Once many stories are rendered to the page, continuing to add more to the screen can block the user from doing anything for a little while until those are rendered. Users with slower computers will see this amplified and possibly crash the app.
Notice the fps meter at the top left of this gif as we scroll to the bottom to fetch more data:
This approach is dangerous to users on slow computers when loading a large amount of stories.
Scrolling through the virtualized list is kind of slow. The FPS drops, especially as we increase the overscan value (limit as we approach no-virtualization :wink:).
Note: The `overscan` value is set to render rows outside of what's visible in the viewport. So when overscan is set to 2, the virtual grid will render 2 rows at the beginning and 2 rows at the end of the list to make scrolling seem smoother.
With > 500 stories loaded in, I didn't get stuck when loading more stories. This is because there are only a few rows rendered to the screen at any given moment, so the browser doesn't have to re-render 500+ stories when new ones arrive.
Attached are examples with overscan
set to 0, 2, and 4. Notice that the FPS value drops as we scroll through the page. However, we do not get blocked when loading in more stories like before.
Overscan=0 | Overscan=2 | Overscan=4 |
---|---|---|
This approach is good when fetching large story amounts since it will lower the risk for users with slower computers or large amounts of stories. However, the dropping frame rate while scrolling may crash the app on super slow browsers :grimacing:
The goal of this ticket is to see if reducing the number of elements rendered on the screen will be beneficial. Performance is often about where we want to make sacrifices.
We wanted to see if removing stories from the DOM would make the page load faster. From the lighthouse score we can see that rendering less elements to the page does help with loading time. However, this comes at the cost of laggy scrolling.
Virtualizing the list would not be noticeably beneficial to the app's performance until there were a large number of stories. Additionally, virtualizing the list would come at the cost of introducing a visual lag when scrolling.
However, it seems like users are actively discouraged from searching for stories by loading in as many pages as possible. If the users are looking for a specific story, it would be easier to filter or search using the form controls in the header.
There are other avenues that could be explored to decrease rendering time of the stories. @littlemilkstudio thought of some possible some areas we could investigate, but it's possible that most of the rendering time is scripting time that can't be avoided:
Thoughts? @swissspidy @choumx
Nice analysis! Seems like your results suggest that dashboard virtualization is probably not worth it, which is fine.
If super-long lists are an issue, I think we should consider changing UX to use pagination. Infinite scrolling is nice for a social feed but less ergonomic for looking through archival content that you own, IMO. /cc @aaskedall @agingoldseco
Let's try virtualizing the dashboard grids (stories and templates) so that not as many items are rendered to the DOM at one time to see if it helps lower the render load in lighthouse. If this update helps perf then let's keep it, otherwise just document the findings here.
You can see this by running lighthouse perf on the dashboard before and after making the update. Remember that locally running lighthouse is not the same as running it on staging (our dev environment is wayyyyy slower than prod).