NEW315135
[GTK] Loading www.wikipedia.org results in over 2000 calls to SkFontMgr_fontconfig::onMatchFamilyStyle, which is slow
https://bugs.webkit.org/show_bug.cgi?id=315135
Summary [GTK] Loading www.wikipedia.org results in over 2000 calls to SkFontMgr_fontc...
Jeff Fortin
Reported 2026-05-19 16:13:07 PDT
Created attachment 479721 [details] Demonstration video This is a performance bug found as a derivative of investigating https://gitlab.gnome.org/GNOME/epiphany/-/work_items/2880, separately from that Epiphany-specific adblocking / content filters performance issue. See attached video demonstrating the issue on my desktop workstation, with Epiphany 50.4 and WebKitGTK 2.52.3 from Fedora Workstation 44's RPM repositories. To reproduce, simply: epiphany --incognito-mode https://www.wikipedia.org (with or without adblocking filters enabled) On my desktop workstation (Intel Xeon W3520 × 8), it takes 20 to 25 seconds for that page to start loading. On my laptop (Intel Core i5 8250U × 8), it takes 12 seconds for that page to start loading.
Attachments
Demonstration video (810.67 KB, video/webm)
2026-05-19 16:13 PDT, Jeff Fortin
no flags
Sysprof capture (normal non-incognito launch) (25.44 MB, application/x-xz)
2026-05-19 16:36 PDT, Jeff Fortin
no flags
Sysprof capture flamegraph (471.47 KB, image/png)
2026-05-19 16:36 PDT, Jeff Fortin
no flags
Sysprof marks (179.88 KB, image/png)
2026-05-19 16:37 PDT, Jeff Fortin
no flags
Test patch (7.15 KB, patch)
2026-05-20 13:39 PDT, Michael Catanzaro
no flags
Alternative patch (15.26 KB, patch)
2026-05-20 13:50 PDT, Michael Catanzaro
no flags
Alternative patch (16.28 KB, patch)
2026-05-21 06:10 PDT, Michael Catanzaro
no flags
Sysprof capture flamegraph when running with Michael's 2026-05-21 patched build (263.68 KB, image/png)
2026-05-21 12:44 PDT, Jeff Fortin
no flags
Jeff Fortin
Comment 1 2026-05-19 16:36:09 PDT
Created attachment 479723 [details] Sysprof capture (normal non-incognito launch) Here is a Sysprof capture launching Epiphany 50.4 "normally" (not* incognito mode), configured to have all content filters (adblocking, cookie banners blocking, intelligent tracking protection, etc.) disabled. *: in incognito mode, for some reason, Epiphany would process the content filters even if you disable them, so you'd end up with similar results as the Sysprof captures I'm attaching to https://gitlab.gnome.org/GNOME/epiphany/-/work_items/2880.
Jeff Fortin
Comment 2 2026-05-19 16:36:37 PDT
Created attachment 479724 [details] Sysprof capture flamegraph In the flamegraph, we can see here that other than the bunch of webkit web processes being created on startup for the bunch of tabs, the largest tower of function calls has to do with SkFontMgr spamming fontconfig.
Jeff Fortin
Comment 3 2026-05-19 16:37:03 PDT
Created attachment 479725 [details] Sysprof marks I don't know if this one shows anything meaningful.
Michael Catanzaro
Comment 4 2026-05-20 13:08:22 PDT
Just reading the flame graph, it seems SkFontMgr_fontconfig::onMatchFamilyStyle is absurdly slow when calling FcFontMatch() and especially remove_weak(), which itself has a warning "This can be quite expensive." OK, but surely it's not intended to be *this* expensive. 5 seconds is pretty extreme. One of the comments in the Skia code looked familiar. Turns out I copied this slow Skia code into WebKit's FontCacheFreeType.cpp a decade ago, in 165476@main. Then in https://bugs.freedesktop.org/show_bug.cgi?id=19375 the Fontconfig developers added an API that we could use to avoid the need for it. Then many years later, I finally got around to using it and discovered it didn't work, https://gitlab.freedesktop.org/fontconfig/fontconfig/-/work_items/294. Finally I was able to actually use the new API in 241000@main, but I never contributed this change back to Skia. Later on, WebKitGTK switched from Cario to Skia, and therefore from FontCacheFreeType.cpp to FontCacheSkia.cpp. So now that we are using Skia directly, and we are right back to square one. Anyway, key observation: the slow function SkFontMgr_fontconfig::onMatchFamilyStyle looks extremely similar to FontCacheFreeType::strongAliasesForFamily, which is obsolete since 241000@main. It's very likely that the slow code can be replaced by using FcPatternGetWithBinding(), assuming Skia is willing to depend on Fontconfig 2.14, which it should be because that is pretty old now. It would require some investigation, though, because the WebKit code FontCache::createFontPlatformData in FontCacheFreeType.cpp does font matching and then chooses a family that has a strong family binding, whereas SkFontMgr_fontconfig::onMatchFamilyStyle actually does the opposite. And the strategies are also not identical: FontCacheFreeType.cpp ignores all weak family bindings, while Skia conditionally allows them depending on order. (Is that important? I'm not sure, because I don't understand Fontconfig. Probably not?) Also, the Skia code is structured such that the easiest way to implement the change would be to modify the FcPattern directly using FcPatternRemove(), which WebKit does not do. So it's not so simple as just copying the WebKit changes into Skia. But goal should be to remove is_weak() and rewrite remove_weak(). One thing I don't understand: Chrome and Firefox presumably both also use SkFontMgr_fontconfig, so why are they not comparably slow? P.S. The FCLocker in SkFontMgr_fontconfig.cpp is also obsolete and can be deleted for a small win.
Michael Catanzaro
Comment 5 2026-05-20 13:09:30 PDT
P.S.S. If you have made any customizations to your Fontconfig configuration, there's a high chance that's relevant.
Michael Catanzaro
Comment 6 2026-05-20 13:14:46 PDT
P.S.S.S. remove_weak is designed to allow removing FC_FAMILY or FC_POSTSCRIPT_NAME or actually any Fontconfig "object" (a better term might be "property"), but it's only ever used with FC_FAMILY, so that too can be simplified.
Michael Catanzaro
Comment 7 2026-05-20 13:39:26 PDT
Created attachment 479743 [details] Test patch Actually, this was easier than I expected. Try this test patch! Who knows: it might even be correct? Hard to say, because I don't claim to understand what I'm doing. I think it is a behavior change though. The original code permitted weak matches prior to the first strong match, whereas the new code is stricter about requiring only a strong match. This should result in more failed matches and more fallback down CSS font lists. Arguably that is more correct, but whether this will lead to better results or worse results is debatable. I think I've previously decided not to sign the Google CLA -- don't remember why -- so I probably won't contribute this back to Skia. But anybody else is should feel welcome to do so.
Michael Catanzaro
Comment 8 2026-05-20 13:50:49 PDT
Created attachment 479744 [details] Alternative patch Here's an alternative patch, with less-related changes, deleting unnecessary code in this file. Looks like skfontstyle_from_fcpattern() and fcpattern_from_skfontstyle() could both be simplified more, but I did not try.
Adrian Perez
Comment 9 2026-05-20 15:23:18 PDT
I think part of the issue is that we may be hitting a fallback to synchronous file copy of compiled content filter rules -- the Fontconfig/Skia issue still dominates the profile, but seems worth looking into, so I have filed bug #315236 for that.
Michael Catanzaro
Comment 10 2026-05-21 05:53:34 PDT
One more thing: it wouldn't be hard to change my patch to preserve the original behavior of accepting weak matches that are ordered before the first strong match. Maybe that would be a safer approach, because it would avoid changing which font gets selected. I don't understand why that behavior is actually desirable, though. Fontconfig is really confusing.
Michael Catanzaro
Comment 11 2026-05-21 06:10:03 PDT
Created attachment 479750 [details] Alternative patch
Michael Catanzaro
Comment 12 2026-05-21 11:32:14 PDT
I gave Jeff a scratch build containing my changes, but unfortunately he reports it didn't make any difference to performance. Alas. :( I had thought that might at least reduce the performance problem.
Jeff Fortin
Comment 13 2026-05-21 12:44:29 PDT
Created attachment 479762 [details] Sysprof capture flamegraph when running with Michael's 2026-05-21 patched build Indeed, when loading https://www.wikipedia.org incognito on my desktop workstation, with adblocking turned off, I get roughly these results: * Fedora 44's vanilla webkitgtk6 packages: 9-10 seconds * Michael's patched build: 9-10 seconds The 2nd attached flamegraph here (focused on the main tower of function calls) is fairly similar to the previous one, though the FontConfig functions used at the top are different in practice. Another interesting observation is that this does not seem to be a recent regression, I tried with an Ubuntu 20.04 VM here and even Epiphany 3.36.4 takes 20 seconds+ to load www.wikipedia.org (with content filters disabled)...
Michael Catanzaro
Comment 14 2026-05-22 07:19:56 PDT
Next step here: I will try to create a test program for Jeff to run, using Fontconfig on its own, and calling FcFontMatch() on the same FcPatterns used by www.wikipedia.org. Presumably the problem lies in Fontconfig itself....
Michael Catanzaro
Comment 15 2026-05-26 11:46:09 PDT
(In reply to Michael Catanzaro from comment #14) > Next step here: I will try to create a test program for Jeff to run, using > Fontconfig on its own, and calling FcFontMatch() on the same FcPatterns used > by www.wikipedia.org. Presumably the problem lies in Fontconfig itself.... I discovered that WebKit calls SkFontMgr_fontconfig::onMatchFamilyStyle 2105 times when loading www.wikipedia.org. So while the function itself might be a little slow, that is not the fundamental problem here. But good news: we only need to do the work 10 times, because it's only ever called for 5 fonts, and two font styles: onMatchFamilyStyle: familyName=BlinkMacSystemFont style=328080 onMatchFamilyStyle: familyName=Segoe UI style=328080 onMatchFamilyStyle: familyName=Roboto style=328080 onMatchFamilyStyle: familyName=Inter style=328080 onMatchFamilyStyle: familyName=Helvetica style=328080 onMatchFamilyStyle: familyName=BlinkMacSystemFont style=328080 onMatchFamilyStyle: familyName=Segoe UI style=328080 onMatchFamilyStyle: familyName=Roboto style=328080 onMatchFamilyStyle: familyName=Inter style=328080 onMatchFamilyStyle: familyName=Helvetica style=328080 onMatchFamilyStyle: familyName=BlinkMacSystemFont style=328380 onMatchFamilyStyle: familyName=Segoe UI style=328380 onMatchFamilyStyle: familyName=Roboto style=328380 onMatchFamilyStyle: familyName=Inter style=328380 onMatchFamilyStyle: familyName=Helvetica style=328380 onMatchFamilyStyle: familyName=BlinkMacSystemFont style=328380 onMatchFamilyStyle: familyName=Segoe UI style=328380 onMatchFamilyStyle: familyName=Roboto style=328380 onMatchFamilyStyle: familyName=Inter style=328380 onMatchFamilyStyle: familyName=Helvetica style=328380 Creating a cache of results would fix the problem.
Note You need to log in before you can comment on or make changes to this bug.