Closed Bug 2082419 Opened 1 month ago Closed 17 days ago

Return already-populated HTMLCollection entries directly on safe indexed-getter cache hits, and batch ContentList population only for sequential next-index lazy misses.

Categories

(Core :: DOM: Core & HTML, defect)

defect

Tracking

()

RESOLVED FIXED
161 Branch
Tracking Status
firefox161 --- fixed

People

(Reporter: perf-ai-bot, Assigned: vhilla)

References

(Blocks 1 open bug)

Details

(Whiteboard: [foofrix:e37b0059-6f6a-4ef8-a4eb-96cc97eb39d1])

Attachments

(3 external files)

Return already-populated HTMLCollection entries directly on safe indexed-getter cache hits, and batch ContentList population only for sequential next-index lazy misses.

Problem

Firefox retains matched live-collection elements but re-enters the virtual Item path for indexed getter access. Sequential indexed access also discovers one new match per miss, so this patch returns cached populated entries directly and populates up to 64 matches when the caller requests exactly the next uncached index after an existing valid prefix.

Performance

  • Benchmark: speedometer3
  • Suite: speedometer3
  • Test: sp3-full
  • Component: DOM
  • Local Improvement: 2.62%

Validation

Blocks: speedometer3

Please move this bug to the correct component.

Once the Phabricator patch is ready for review, please commandeer it.

Flags: needinfo?(vhilla)

Return already-populated HTMLCollection entries directly on safe indexed-getter cache hits, and batch ContentList population only for sequential next-index lazy misses.

Assignee: nobody → vhilla
Component: Performance: Automated Findings → DOM: Core & HTML
Flags: needinfo?(vhilla)

Bug 2075738 changed something here recently and is relevant for rebasing.

See Also: → 2075738

Rebased, there might be fewer performance gains without mFlushesNeeded being removed. Though the patch still does some pre-fetching in PopulateSelf and perhaps a more direct IndexedGetter.

https://perf.compare/compare-lando-results?landoInstance=lando-prod-2025&baseLando=106959&newLando=106960&baseRepo=try&newRepo=try&framework=13

Linux try profile before https://share.firefox.dev/4sducJP
Linux try profile after https://share.firefox.dev/4zLhogp

mozilla::dom::HTMLCollection::IndexedGetter went from 520 to 407 samples, of which mozilla::dom::ContentList::PopulateSelf went from 460 to 345 samples.

mozilla::dom::ContentList::PopulateSelf in total went from 474 to 360 samples.

So this still seems worthwhile.

Severity: -- → S3

I think the improvement came from HTMLCollection::IndexedGetter being non-virtual and not having to call a virtual method in the case where the element is already in mElements. I tried around a bit with the prefetching in PopulateSelf but didn't see clear wins. At the same time, prefetching has the risk of doing unneeded work that might be expensive for some collections. Edit: oops but comment 5 pointed to the opposite. So prefetching looked promising on try, being non-virtual was useful locally, we might do both.

Try push for my WIP https://perf.compare/compare-lando-results?landoInstance=lando-prod-2025&baseLando=106959&newLando=107221&baseRepo=try&newRepo=try&framework=13

Tests: https://treeherder.mozilla.org/jobs?repo=try&revision=cd388755d53d6267e2eb122ce95a3b47fe882b6e

I had claude add a bit of logging and do some analysis for an sp3 run.

There are no ContentList::Item calls where aIndex > mElements.Length() && mState != State::UpToDate, i.e. the "frontier" always moves forward sequentially (though access overall might not be).

Histogram of highest indices needed per "epoch" (one list instance after creation / becoming dirty)

Highest index + 1 Epochs
1 1,091
5-8 10
9-16 3,010
17-64 160
65-256 640
>256 2,150

-> There are lots of lists that get populated to a large size.

Simulated prefetch policies

Policy PopulateSelf calls Fetched items (upper bound)
none 1,235,901 1,235,901
min(count,4) 259,401 1,244,881
min(count,8) (current) 155,641 1,266,681
min(count,16) 94,671 1,272,681
min(count,64) 48,151 1,355,201
fixed 64 24,331 1,581,515
count (doubling, uncapped) 39,601 1,948,641

The number of fetched items might be lower because we might not have more items in the tree that match the selector.

-> I considered doing min(count, 8) while the original did a fixed 64. Try shows a statistically significant improvement between them. Even though the number of PopulateSelf calls saved only goes from 88% to 98%.

Looking at profiles, min(count, 64) spends a comparable time to fixed 64 in IndexedGetter. But the try perf results are still worse.

Attachment #9664603 - Attachment description: WIP: Bug 2082419 - Avoid virtual calls for HTMLCollection::{Item, IndexedGetter}. r=#dom-core → Bug 2082419 - Avoid virtual calls for HTMLCollection::{Item, IndexedGetter}. r=#dom-core
Pushed by vhilla@mozilla.com: https://github.com/mozilla-firefox/firefox/commit/4936a6a87b79 https://hg.mozilla.org/integration/autoland/rev/b107c03b073f Avoid virtual calls for HTMLCollection::{Item, IndexedGetter}. r=dom-core-reviewers,webidl,hsivonen https://github.com/mozilla-firefox/firefox/commit/bafd2a4450b0 https://hg.mozilla.org/integration/autoland/rev/fe8f54024938 Prefetch successive elements for ContentList::PopulateSelf. r=dom-core-reviewers,hsivonen
Status: UNCONFIRMED → RESOLVED
Closed: 17 days ago
Resolution: --- → FIXED
Target Milestone: --- → 161 Branch
Regressions: 2089071
No longer regressions: 2089071
QA Whiteboard: [qa-triage-done-c162/b161]
Regressions: 2089071
No longer regressions: 2089071
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: