Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why are styles computed lazily?

24 views
Skip to first unread message

Seth Fowler

unread,
Oct 4, 2012, 6:17:40 PM10/4/12
to dev-tec...@lists.mozilla.org
Just bringing myself up to speed on the layout subsystem of Gecko and there's something that's not immediately clear to me. The docs suggest that styles are computed lazily. What I'm curious about is: why this is preferable to doing it eagerly?

Certainly there are cases where we don't need the value of certain properties - for example, we don't care about anything under a DOM node with "display: none", and we can ignore certain properties when a node has "visibility: hidden". But it seems to me like these cases could easily be taken into account when evaluating styles eagerly.

Does anyone know why this design choice was made?

Thanks,
- Seth

Boris Zbarsky

unread,
Oct 5, 2012, 12:42:30 AM10/5/12
to
On 10/4/12 6:17 PM, Seth Fowler wrote:
> Just bringing myself up to speed on the layout subsystem of Gecko and there's something that's not immediately clear to me. The docs suggest that styles are computed lazily. What I'm curious about is: why this is preferable to doing it eagerly?

I think the premise was that a large enough fraction of style structs is
not needed on a large enough fraction of nodes.

I'm not sure to what extent this is true now that we've been doing
things like applying SVG effects to all elements (and thus needing the
SVG structs for everything). I _think_ the XUL structs are still
largely unused for HTML elements.

And yes, the display:none cases, and getComputedStyle() when only one
property is asked for...

dbaron might know more about other details here, if any.

-Boris

L. David Baron

unread,
Oct 6, 2012, 12:51:46 PM10/6/12
to Seth Fowler, dev-tec...@lists.mozilla.org
On Thursday 2012-10-04 15:17 -0700, Seth Fowler wrote:
> Just bringing myself up to speed on the layout subsystem of Gecko and there's something that's not immediately clear to me. The docs suggest that styles are computed lazily. What I'm curious about is: why this is preferable to doing it eagerly?

I'm assuming that you're talking about nsStyleContext lazily
computing the results of GetStyle*() when those functions are called
rather than computing it at the time the style context is created
(which is itself buffered up in ways that are very important).

Off the top of my head, I think the biggest reason for this is that
it's probably a bit of a savings in memory use, since there are a
bunch of structs that we won't create for many elements -- though as
bz said, I'm not sure how true that is anymore. It's no longer true
for nsStyleUserInterface since we eagerly create it to trigger
cursor loading. (Probably that was the wrong fix and we should have
moved the cursor property to nsStyleVisibility instead.)

It may well be true for nsStyleUIReset, nsStyleTable,
nsStyleTableBorder, nsStyleQuotes, nsStyleContent, nsStyleXUL,
nsStyleColumn, and nsStyleList. But we don't have any tests to
check that we're not creating them in the cases where we think we
ought not to be creating them, so odds are we're creating them in a
bunch of cases where we need not.

> Certainly there are cases where we don't need the value of certain properties - for example, we don't care about anything under a DOM node with "display: none", and we can ignore certain properties when a node has "visibility: hidden". But it seems to me like these cases could easily be taken into account when evaluating styles eagerly.

What would the advantage of computing them eagerly be?

-David

--
𝄞 L. David Baron http://dbaron.org/ 𝄂
𝄢 Mozilla http://www.mozilla.org/ 𝄂

Seth Fowler

unread,
Oct 9, 2012, 8:06:59 PM10/9/12
to L. David Baron, dev-tec...@lists.mozilla.org
> I'm assuming that you're talking about nsStyleContext lazily
> computing the results of GetStyle*() when those functions are called
> rather than computing it at the time the style context is created
> (which is itself buffered up in ways that are very important).

Indeed.

> Off the top of my head, I think the biggest reason for this is that
> it's probably a bit of a savings in memory use, since there are a
> bunch of structs that we won't create for many elements -- though as
> bz said, I'm not sure how true that is anymore.

That makes perfect sense, but what isn't immediately clear to me is whether
there are any cases where an eager CSS implementation couldn't determine
in advance that a given style struct wouldn't be needed. If so, laziness is just
an implementation strategy for this optimization and not a requirement.

> What would the advantage of computing them eagerly be?

That is a bit hard for me to answer right now as I don't yet have a good mental
model of Gecko's CSS and layout subsystems, but it might be that computing
them eagerly could be made more efficient. For example:

- We might be able to better overlap eager style computations with IO.
- Eager style computations might have better locality characteristics.
- We might be able to use a different, more efficient algorithm.
- We might be able to better take advantage of parallelism.
- Eager style computations might yield code that is easier to understand.

I've seen cases where all of these "might"s have been true on other projects I've
worked on; sometimes there are substantial costs to laziness, even if it's more
work efficient. They're not always true, and right now I can't say whether the tradeoffs
favor laziness in this case or not, but I do think it's something worth thinking about.

Thanks,
- Seth

L. David Baron

unread,
Oct 10, 2012, 5:43:41 PM10/10/12
to Seth Fowler, dev-tec...@lists.mozilla.org
On Tuesday 2012-10-09 17:06 -0700, Seth Fowler wrote:
> > I'm assuming that you're talking about nsStyleContext lazily
> > computing the results of GetStyle*() when those functions are called
> > rather than computing it at the time the style context is created
> > (which is itself buffered up in ways that are very important).
>
> Indeed.
>
> > Off the top of my head, I think the biggest reason for this is that
> > it's probably a bit of a savings in memory use, since there are a
> > bunch of structs that we won't create for many elements -- though as
> > bz said, I'm not sure how true that is anymore.
>
> That makes perfect sense, but what isn't immediately clear to me is whether
> there are any cases where an eager CSS implementation couldn't determine
> in advance that a given style struct wouldn't be needed. If so, laziness is just
> an implementation strategy for this optimization and not a requirement.

We can never determine for sure that a given struct will
never need a struct, since a page can always ask for a style in it
via getComputedStyle (except for the rare style context that
couldn't potentially be used from a getComputedStyle call). So I
don't see how we can keep the space optimization without also
keeping the ability to compute lazily.

That said, some of the advantages of computing eagerly might remain.

Seth Fowler

unread,
Oct 11, 2012, 2:13:04 PM10/11/12
to L. David Baron, dev-tec...@lists.mozilla.org

On Oct 10, 2012, at 2:43 PM, L. David Baron <dba...@dbaron.org> wrote:
> We can never determine for sure that a given struct will
> never need a struct, since a page can always ask for a style in it
> via getComputedStyle (except for the rare style context that
> couldn't potentially be used from a getComputedStyle call). So I
> don't see how we can keep the space optimization without also
> keeping the ability to compute lazily.

Yes, that's true. You might want the ability to do lazy style
computations, even if your primary strategy was eager, just for cases
like this. It'd be interesting to know how often it's beneficial to
not just go ahead and compute the styles for e.g. display: none
subtrees, though. Presumably they are on the page because
eventually some event will cause them to be displayed, right?
(Quite hypothetical without data, of course.)

- Seth
0 new messages