Page MenuHomePhabricator

Long task during enwiki-mobile page load from mobile.init.js
Closed, ResolvedPublic5 Estimated Story Points

Assigned To
None
Authored By
Peter
Dec 19 2019, 12:20 PM
Referenced Files
F36968692: 2023-04-28_15-52-57.png
Apr 28 2023, 10:10 PM
F36968612: after.png
Apr 28 2023, 10:10 PM
F36968609: before.png
Apr 28 2023, 10:10 PM
F36968601: 2023-04-28_14-59-02 (7).png
Apr 28 2023, 10:10 PM
F36963744: Untitled2.mov.gif
Apr 26 2023, 6:09 PM
F36957926: RPReplay_Final1682041757.mov.gif
Apr 21 2023, 2:12 AM
Restricted File
Apr 12 2023, 8:50 PM
Restricted File
Apr 12 2023, 8:50 PM

Description

I've been preparing a blog post about first input delay and did one more test on my Alacatel One phone and see that we have a really long task that happens late when testing https://en.m.wikipedia.org/wiki/Barack_Obama (after onload, the user see all content) that on my phone lasts for 881 ms = the user can't do anything = if the user presses a key etc it causes a first input delay. My guess is that this is the biggest cause of high FID on mobile.

Screen Shot 2019-12-19 at 1.13.27 PM.png (604×1 px, 138 KB)

I've collected a trace log (you can drag and drop the trace-1.json file into devtools/performance tab in Chrome to see what it looks like) so you can use it to zoom in and better see where the time is spent.

This point directly to ResourceLoader adding too much CSS at once. It seems like the client-side code is already chunking up CSS for @import calls that need to be at the top, so there is already some logic that could be reused to reduce the size of nextCssBuffer.

Developer notes

https://web.dev/optimize-long-tasks/ might be a helpful reference when working on this task.

QA steps

  1. Using a phone, visit https://en.m.wikipedia.beta.wmflabs.org/wiki/Dog
  2. Tap an image and verify that the image overlay appears and works.
  3. Exit out of the image overlay and expand and collapse sections of the article. Verify that the sections can expand and collapse.
  4. Reload the page, and verify that the sections that you previously expanded appear expanded after reloading the page.

https://phabricator.wikimedia.org/T241139#8797998
https://phabricator.wikimedia.org/T241139#8808678

QA Results - Beta

ACStatusDetails
1T241139#8797998
2T241139#8797998
3T241139#8797998

QA Results - Prod

ACStatusDetails
1T241139#8808678
2T241139#8808678
3T241139#8808678

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
Peter removed Peter as the assignee of this task.Dec 19 2019, 12:20 PM

It's ResourceLoader loading a bunch of CSS.

Gilles renamed this task from Long task on mobile to ResourceLoader should chunk up CSS it adds to the page (more).Dec 20 2019, 8:50 AM
Gilles updated the task description. (Show Details)
Krinkle renamed this task from ResourceLoader should chunk up CSS it adds to the page (more) to ResourceLoader should flush CSS in smaller chunks.Jan 6 2020, 10:10 PM
@Peter wrote in the task description:

[…] test on my Alacatel One phone and see that we have a really long task that happens late when testing https://en.m.wikipedia.org/wiki/Barack_Obama [that] lasts for 881 ms = the user can't do anything = if the user presses a key etc it causes a first input delay. My guess is that this is the biggest cause of high FID on mobile.

Screen Shot 2019-12-19 at 1.13.27 PM.png (604×1 px, 138 KB)
Dec 2019

It's ResourceLoader loading a bunch of CSS.

Actually, it's not. This is pure JavaScript execution, wholly attributed to mobile.init.js. There is virutally no time spent at all in mw.loader in this screenshot.

The wide pink bar (mw.loader) seems significant, and it's an easy to mistake to make given that we're used to thinking about execution from left to right. But, when a callback is involved, a flame graph always shows the "next" code below the previous one instead of besides it.

When the browser receives a module with CSS and JS, the flushCssBuffer is called to apply the styles, and then it calls runScript to let the browser execute the module's JavaScript code, in this case mobile.init.js.

With that information in mind, the screenshot is quite clearly spending more than half a second in the various JavaScript functions bundled with mobile.init.js.

To illustrate, below I've added a magnifying glass to show where the time actually spent in flushCssBuffer is visualised, and where it is not:

Screen_Shot_2019-12-19_at_1.13.27_PM-1.png (604×1 px, 67 KB)

I've taken a new capture today of https://en.m.wikipedia.org/wiki/Barack_Obama in latest Chrome with emulated mobile CPU, and it seems it's still spending quite lot of time in mobile.init.js, with uninterrupted back-to-back synchronous calls doing expensive work in JS during page load, whilst the device is frozen for user input.

24 Aug 2021
Screenshot 2021-08-24 at 19.54.19.png (1×2 px, 793 KB)
Krinkle renamed this task from ResourceLoader should flush CSS in smaller chunks to Long task during enwiki-mobile page load from mobile.init.js.Aug 24 2021, 7:00 PM

2023 update — this is still an issue. Here is the profile from my $100 Xiaomi Redmi 9A phone:

2023-03-15_17-38-52 (1).png (1×3 px, 534 KB)

Jdlrobson added a subscriber: ovasileva.

hi @ovasileva I checked in with Nick and this is still a problem and we have an idea of how we might fix it. Could we perhaps prioritize this next sprint?

ovasileva lowered the priority of this task from High to Medium.Mar 27 2023, 5:32 PM
ovasileva raised the priority of this task from Medium to High.
LGoto set the point value for this task to 5.Mar 27 2023, 5:39 PM

Mo's out this week, but since this is performance related it would be good to get him involved in this task!

Change 908333 had a related patch set uploaded (by Nray; author: Nray):

[mediawiki/extensions/MobileFrontend@master] Remove ~250ms of unnecessary JS execution

https://gerrit.wikimedia.org/r/908333

Step 1: Remove unnecessary JavaScript

In Toggler.js, there was a small block of JavaScript that wasn't working correctly and was taking ~250ms to execute on my xiaomi redmi 9a phone. Therefore, I'm removing it in https://gerrit.wikimedia.org/r/908333. For long pages like the "United_States" page, this small step cuts the task's JS execution time by nearly half.

Before: {F36948677}

After:: {F36948678}

Change 908333 merged by jenkins-bot:

[mediawiki/extensions/MobileFrontend@master] Remove ~250ms of unnecessary JS execution

https://gerrit.wikimedia.org/r/908333

Change 908588 had a related patch set uploaded (by Nray; author: Nray):

[mediawiki/extensions/MobileFrontend@master] Split toggling code into a smaller task

https://gerrit.wikimedia.org/r/908588

Change 908674 had a related patch set uploaded (by Nray; author: Nray):

[mediawiki/extensions/MobileFrontend@master] Add 'getThumbnail` method to PageHTMLParser

https://gerrit.wikimedia.org/r/908674

Change 908675 had a related patch set uploaded (by Nray; author: Nray):

[mediawiki/skins/MinervaNeue@master] Replace slow thumbnail init code with event delegation

https://gerrit.wikimedia.org/r/908675

Change 908588 abandoned by Nray:

[mediawiki/extensions/MobileFrontend@master] Split Toggler.js initialization into a smaller task

Reason:

I think this may interfere with code that depends on mobile.init's toggling initialization being synchronous such as https://github.com/wikimedia/mediawiki-extensions-DiscussionTools/blob/master/modules/controller.js#L450 . If I have time at end of sprint, I will circle back to this

https://gerrit.wikimedia.org/r/908588

Change 908588 restored by Nray:

[mediawiki/extensions/MobileFrontend@master] Split Toggler.js initialization into a smaller task

https://gerrit.wikimedia.org/r/908588

Change 908674 merged by jenkins-bot:

[mediawiki/extensions/MobileFrontend@master] Add 'getThumbnail` method to PageHTMLParser

https://gerrit.wikimedia.org/r/908674

Change 908588 merged by jenkins-bot:

[mediawiki/extensions/MobileFrontend@master] Read browser width at beginning of Toggler _enable method

https://gerrit.wikimedia.org/r/908588

Change 908675 merged by jenkins-bot:

[mediawiki/skins/MinervaNeue@master] Replace slow thumbnail init code with event delegation

https://gerrit.wikimedia.org/r/908675

nray claimed this task.
nray added a subscriber: Edtadros.
nray updated the task description. (Show Details)

Test Result - Beta

Status: ✅ PASS
Environment: beta
OS: macOS Ventura
Browser: Chrome
Device: MBP
Emulated Device:NA

Test Artifact(s):

QA Steps

Using a phone, visit https://en.m.wikipedia.beta.wmflabs.org/wiki/Dog
✅ AC1: Tap an image and verify that the image overlay appears and works.
✅ AC2: Exit out of the image overlay and expand and collapse sections of the article. Verify that the sections can expand and collapse.
✅ AC3: Reload the page, and verify that the sections that you previously expanded appear expanded after reloading the page.

RPReplay_Final1682041757.mov.gif (640×296 px, 2 MB)

Edtadros closed this task as Resolved.EditedApr 26 2023, 6:09 PM
Edtadros removed ovasileva as the assignee of this task.

Test Result - Prod

Status: ✅ PASS
Environment: enwiki
OS: iOS 16
Browser: Chrome
Device: iPhone
Emulated Device:NA

Test Artifact(s):

QA Steps

Using a phone, visit https://en.m.wikipedia.org/wiki/Dog
✅ AC1: Tap an image and verify that the image overlay appears and works.
✅ AC2: Exit out of the image overlay and expand and collapse sections of the article. Verify that the sections can expand and collapse.
✅ AC3: Reload the page, and verify that the sections that you previously expanded appear expanded after reloading the page.

Untitled2.mov.gif (640×296 px, 1 MB)

Results

I think this work resulted in a ~50% reduction in JavaScript execution time for the task on long pages like the "Barack_Obama" page which represents around ~260ms improvement in Total Blocking Time on the Moto G (5) phone that our synthetic tests use:

2023-04-28_14-59-02 (7).png (1×1 px, 237 KB)

https://grafana.wikimedia.org/goto/0ZaWBSsVz?orgId=1

On my $100 Xiaomi Redmi 9A phone, there is roughly a ~450ms improvement in total blocking time going from a ~725ms task to one that takes ~280ms:

Before:

before.png (1×2 px, 270 KB)

After:

after.png (1×3 px, 286 KB)

Future work

Note that any task greater than 50ms is considered a "long task" and can affect the usability and responsiveness of a website. Therefore, on low-end devices, this task is still too long, but much improved. I cut away at some of the lowest hanging fruit by removing unnecessary JavaScript and using event delegation, but there are ways that this can be reduced further:

  1. A costly forced synchronous layout can still happen as a result of the mobile.init module/Resource Loader appending a stylesheet and then the Toggler.js reading the browser's width. Ideally, the browser width check would happen at the beginning of the frame and before any invalidation (e.g. DOM mutations) so that the browser can simply read the values from the previous frame.
  2. The toggling code persists sections that the user opens using sessionStorage. When it tries to expand them during page load it can lead to a massive task (e.g. ~2s on my MacBook Pro using 6x CPU throttle testing the "United_States" page with all sections open) from the recalc style/layout it causes:

2023-04-28_15-52-57.png (2×3 px, 523 KB)

I think we should reconsider how valuable this feature is relative to its performance/user experience penalty. If it is valuable, perhaps there is a way that we can open the sections before the page is rendered so that the browser doesn't have to do expensive layout calculations multiple times during page load.

@pmiazga's dared me to write a blog post about this experience during our super happy dev time meeting, so it's at https://www.nray.dev/blog/300ms-faster-reducing-wikipedias-total-blocking-time/ 🙂