GoogleForCreators / web-stories-wp

Web Stories for WordPress
https://wp.stories.google
Apache License 2.0
768 stars 178 forks source link

Virtualize Dashboard Grids #9675

Closed BrittanyIRL closed 2 years ago

BrittanyIRL commented 3 years ago

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).

samwhale commented 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

BrittanyIRL commented 2 years ago

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:

Screen Shot 2021-11-29 at 11 56 09 AM
samwhale commented 2 years ago

Oh good callout. Checked again and there is a considerable difference here:

Before After
Screen Shot 2021-11-30 at 10.10.32 AM.png Screen Shot 2021-11-30 at 10.26.46 AM.png

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.

swissspidy commented 2 years ago

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?

samwhale commented 2 years ago

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.

samwhale commented 2 years ago

Virtualization vs non-virtualization

Summary

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.

Initial scrolling performance for both virtual and non-virtual story list

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
image image (1)

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.


Non-virtualized list performance for large amounts of items

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:

fps-non-virtualized

This approach is dangerous to users on slow computers when loading a large amount of stories.


Virtualized list performance for large amounts of items

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
fps-virtualized-overscan-4 fps-virtualized-overscan-2 fps-virtualized-overscan-0

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:


To virtualize or not virtualize?

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

dreamofabear commented 2 years ago

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