lighthouse: OOM in ImageElements gatherer
We were getting OOMs in LR and I managed to confidently bisect down to the commit where #11188 was merged. (its core(image-elements): collect CSS sizing, ShadowRoot, & position
)
node lighthouse-cli http://cosmetiqon.gr/ --only-audits=unsized-images -G
Here’s one URL where this can sometimes OOM, though I definitely can’t get an OOM locally. I’m not entirely sure which context is getting the OOM… the page or Lighthouse.
I do know that if I comment out these lines…
…the imageGatherer takes 2s instead of 28s.
I attempted to do some memory debugging but didn’t get too far. Still requires a bit of investigation
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 16 (6 by maintainers)
isCss
is a significant speed up, enough to reland this change. https://github.com/GoogleChrome/lighthouse/issues/11289quick followup we will do is sort the elements by image size, then apply a sensible time budget to fetching source rules. https://github.com/GoogleChrome/lighthouse/pull/11340#issuecomment-682259063
I did some exploring on this issue, but I couldn’t find (& don’t think I have) access to LR so this is coming from observing what happens on my local machine:
I don’t know if the slow down from
getMatchedStylesForNode
has to do with the OOM issue, but my intuition believes they might be two separate things to consider, especially after reading the performance issues previously encountered when usinggetMatchedStylesForNode
In the
font-size
audit, as far as I could tell, we optimize how many times we actually callgetMatchedStylesForNode
, which is not something I did when I wroteunsized-images
, because I didn’t realize how slowgetMatchedStylesForNode
can be. In order to improve the runtime ofunsized-images
by reducing calls togetMatchedStylesForNode
one optimization that I think we should include is to changeto
since we don’t currently check css background-images in
unsized-images
anyway, andcssWidth/cssHeight
aren’t as relevant to background-images because of background-repeat & parent element sizingAdditionally, I agree with @patrickhulce about
or other workarounds that can reduce the total calls to
getMatchedStylesForNode
.I noticed that a large amount of the ImageElements in
http://cosmetiqon.gr/
had the samesrc
because they were the default gif for the site’s lazy loaded images. There might be potential here to reduce the calls togetMatchedStylesForNode
, i.e. caching theCSS.GetMatchedStylesForNodeResponse
for sibling nodes that have the same CSS class (might make OOM worse), or not callinggetMatchedStylesForNode
on lazy loaded images outside of the viewport (not sure if this is what we’d want to encourage)As for the OOM issue, I had some leads I could think of:
unsized-images
onhttp://cosmetiqon.gr/
there was a handful of errors likethat disappear after adding
&& !element.isCss
. Is there a possibility for a memory leak caused from too many errors?to
In worst case this somehow causes a circular reference since
nodeId
was declared earlier & in best case this is just a nitasync afterPass(passContext, loadData)
withinimage-elements.js
image-elements.js
:I JSON.stringified instances of
matchedRules
and found sizes ranging from ~30000 chars/bytes to ~200000 chars/bytes with median fitting between ~100000-150000 chars/bytes for a page such ashttp://cosmetiqon.gr/
Based off a 150000 byte ballpark for each instance of
matchedRules
inhttp://cosmetiqon.gr/
, and the fact that it has ~100 ImageElements, we get 150000 * 100 -> ~15MBI do not know if this is a reasonable use of memory / cache when running lighthouse or LR, & whether we do save all
matchedRules
, I’ll check ndb later to see if this happens locallyOnce we have some urls we can start digging in more…
I’d start by adding timing marks around these three areas:
DOM.pushNodeByPathToFrontend
CSS.getMatchedStylesForNode
getEffectiveSizingRule
It’s unfortunate we are working from a devtools node path here. Perhaps there’s a way to grab the DOM snapshot (must verify that the width/height properties from the snapshot don’t include intrinsic image sizes) and then connect that data to the image element we scraped.
Either:
isExpicitlySized
(no need to get the actual size, audit doesn’t care): ImageElements would haveisExpicitlySized
set to true iff the snapshot for that element has a value for width and height.After discussing with @lemcardenas, we’re going to revert ##11217 and #11188 before we ship 6.3.0