Open Bug 1191389 Opened 9 years ago Updated 11 months ago

Extremely slow frame reconstruction when all the (many) kids of a block switch from display:block to display:list-item

Categories

(Core :: Layout: Block and Inline, defect)

defect

Tracking

()

People

(Reporter: sebastian, Unassigned)

References

(Depends on 1 open bug)

Details

Attachments

(3 files)

User Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:41.0) Gecko/20100101 Firefox/41.0
Build ID: 20150727205026

Steps to reproduce:

I'm working on a webapp that involves re-rendering large amounts of tabular data on scrolling events.

I can't reproduce my code here, but I found a fiddle on SO which I believe to be representative of my problem: http://jsfiddle.net/6D7sM/1/


Actual results:

Chrome and Opera apply the .fixed class within fractions of a second. FF hangs for about 20 seconds, although the profiler doesn't show anything out of the ordinary.


Expected results:

FF should be as fast as other browsers when it comes to style recomputation.
(In reply to sebastian from comment #0)
> I can't reproduce my code here, but I found a fiddle on SO which I believe
> to be representative of my problem: http://jsfiddle.net/6D7sM/1/

That's a bit dangerous to say; it's entirely possible we could fix that in a way that doesn't help your code.

(There's also been a bunch of work recently that may already help.)
Yes, I'm aware of that risk. Still, I've encountered a handful of cases of slow reflows specifically in FF in the last year or two. This fiddle was a particularly egregious case. It may be that I'm misdiagnosing things, or that all of those cases have unrelated causes, but if I could just get feedback on that particular fiddle that'd be a good start.
OS: Unspecified → Linux
Hardware: Unspecified → x86_64
Flags: needinfo?(cam)
The recent work that David mentions is bug 1180118, but for this fiddle it doesn't really help, since the relevant content looks like:

  <style>.fixed li { ... }</style>
  <ul>
    <li>...</li>
    ...
  </ul>

and then script toggles the fixed class on the ul.  Chrome is definitely faster on this case, but I'm not sure yet what we can do for it.  (I wonder if it's the reframing due to the display change on the li elements that's the slow part?)

Sebastian, for your actual webapp, can you try it in a Nightly (43) or Developer Edition (42) version of Firefox?  It may be that the bug 1180118 work helps with your particular content.
Flags: needinfo?(cam) → needinfo?(sebastian)
The reason the fiddle doesn't take advantage of the bug 1180118 work is that it just helps with avoiding restyling any intermediate nodes between the one that had the class change and the descendants that match the selector; since the li elements are direct children, there aren't any nodes to skip restyling.
Attached file profile.json
A profile taken on http://jsfiddle.net/6D7sM/1/
Thanks. I'm using the August 19 build, so I believe I'm using that patch already. Not too worried about the webapp; it's not as smooth as it is in Chrome, but nothing overly problematic either.

Still confused about that fiddle, though. I've attached the profiler output above, as I can't really make sense of what happens during those 22 seconds of total paralysis. I'm not running any add-ons, either (and safe-mode makes no difference). At this point I'm out of ideas how I could diagnose the problem; any input is appreciated! :)
Flags: needinfo?(sebastian)
So looking at the fiddle in a current inbound build, about 75% of the time is in fact in frame reconstruction.  About 13% is ContentRemoved (almost all of this is under nsBlockFrame::RemoveFrame; lots of nsLineBox::IndexOf and self time in RemoveFrame) and the rest is ContentRangeInserted.

Under ContentRangeInserted, about half the time is GetInsertionPrevSibling, almost all of which is ExplicitChildIterator::GetNextChild, either self time or calling ShadowRoot::IsShadowInsertionPoint (!).

The other half of the time is nsBlockFrame::InsertFrames, which calls nsBlockFrame::AddFrames, which does RFindLineContainingFrame.

So basically the first and third pieces of time are due to the known-terrible line box datastructure in blockframes.  The second piece I need to look into a bit more, because that should _not_ take as long as it's clearly taking in this case; sounds like some sort of fallout from the various shadow DOM stuff.

In any case, we could _try_ to not reframe when going from display:block to display:list-item or vice versa.  But of course it's not clear to me whether that's what actually happens in Sebastian's original webapp... see also comment 1.  In any case, resummarizing this to cover the testcase we do have.
Component: CSS Parsing and Computation → Layout: Block and Inline
Summary: Extremely slow reflow with descendant selectors → Extremely slow frame reconstruction when all the (many) kids of a block switch from display:block to display:list-item
Depends on: 1201692
Thanks so much for doing some actual profiling, Boris. Seems like you've found at least one of the culprits!

I'm unfamiliar with the internals of Gecko, so I don't have the mental context to gauge to what degree my app will be affected by a fix. I'll wait for this to be resolved before deciding whether or not to open another bug report on it (by that time, if necessary, I'll hopefully be able to link to a running app and/or code).
I filed bug 1201692 on the child iterator issue.
Note that I suspect that fixing the child iterator thing is easy but fixing the linebox stuff is not.  We have a number of other performance issues on file due to the linebox stuff.
I guess bug 682052 is what covers the linebox stuff.
Depends on: 682052
Severity: normal → S3

Here's a testcase taken directly from the reporter's jsfiddle. Based on comment 3, I think the STR here are to click "inject UL" and then "Fix list".

For me, in current Nightly, the "Fix list" operation triggers a 525ms restyle, which is almost entirely frame reconstruction. Here's a profile: https://share.firefox.dev/3BBrzYK

This is a little bit slower than Chrome, but not a 20-second-hang as in comment 0 & comment 6. I was going to assume that this meant the issue had largely gone away; but in fact I see similar results in Nightly builds from around when this bug was filed. So I think maybe things are really just unchanged here, and the 20-22second hang were from the original undisclosed webapp and/or from different hardware than mine that's slower (though 500ms vs. 22 seconds is an extremely substantial difference...)

I think bz's analysis in comment 7 is largely still relevant; If I toggle "invert call stack" in my profile, it shows us spending most of our time in nsLineBox::RFindLineContaining, nsLineBox::Contains, and nsLineBox::IndexOf.

For comparison, here's what Chrome's profile shows this is like for them. They spend ~220ms in "rendering/painting" categories, as shown in the pie chart at the bottom.

So they're faster than us, but it seems to be a factor of ~2x rather than >20x.

Status: UNCONFIRMED → NEW
Ever confirmed: true
OS: Linux → All
Hardware: x86_64 → All
Version: 41 Branch → Trunk
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: