Skip to content

Profile Lighthouse + Chromium tracing #17403

Open

Description

@connorjclark

🤖 Created by Gemini 4 Argon.

Chromium Runtime & CPU Overhead When Running Lighthouse (https://www.cnn.com/)

Executive Summary

To isolate and quantify the Chromium runtime overhead introduced exclusively when Lighthouse and Perfetto/DevTools tracing are active, we profiled a Release build with symbols of Chromium 157.0.8088.0 (is_debug = false, dcheck_always_on = false, symbol_level = 1) loading https://www.cnn.com/ under three controlled configurations using a custom LD_PRELOAD per-thread CPU-time stack sampling profiler (lhprof.cc, CLOCK_THREAD_CPUTIME_ID at 500 Hz / 2.0 ms per sample across all browser, renderer, GPU, and utility processes):

  1. baseline: Standard Chromium navigation to https://www.cnn.com/ via Lighthouse's Driver with no tracing or Lighthouse gatherers active.
  2. tracing_only: Identical navigation with only Lighthouse's default Tracing.start categories enabled during navigation and Tracing.end drained afterward.
  3. full_lh: Full Lighthouse navigation run (navigation-runner.js) with all 28 default gatherers (prepare, startInstrumentation, startSensitiveInstrumentation, navigation, stopSensitiveInstrumentation, stopInstrumentation, and getArtifact).
Metric (Release Build) baseline tracing_only full_lh tracing_only Overhead vs. baseline full_lh Overhead vs. baseline
Total Chromium CPU Time 6,704 ms (6.70 s) 12,236 ms (12.24 s) 19,162 ms (19.16 s) +5,532 ms (+82.5%) +12,458 ms (+185.8%)
Direct Instrumentation Stacks 1,000 ms 3,860 ms 7,208 ms +2,860 ms (+286.0%) +6,208 ms (+620.8%)
Phase 1: Setup & Prepare (CPU / Wall) 714 ms / 1.26 s 720 ms / 1.25 s 714 ms / 1.24 s +6 ms / -0.01 s +0 ms / -0.02 s
Phase 2: Navigation (CPU / Wall) 5,990 ms / 7.76 s 8,164 ms / 8.49 s 8,370 ms / 8.49 s +2,174 ms (+36.3%) / +0.73 s +2,380 ms (+39.7%) / +0.73 s
Phase 3: Stop Tracing & Drain (CPU / Wall) — 3,352 ms / 3.01 s 3,684 ms / 3.51 s +3,352 ms / +3.01 s +3,684 ms / +3.51 s
Phase 4: Post-Nav getArtifact (CPU / Wall) — 0 ms / 0.00 s 6,394 ms / 10.75 s — +6,394 ms / +10.75 s
Total End-to-End Wall Time 9.03 s 12.75 s 23.98 s +3.72 s (+41.2%) +14.95 s (+165.6%)
Collected Trace Events — 78,377 events 80,749 events +78,377 events +80,749 events

Important

Top 5 Architectural Findings in Chromium (Release Build) + Lighthouse:

  1. Redundant JSON -> CBOR -> JSON Round-Trip & Byte-by-Byte WebSocket Copy During Tracing.end (1,852 ms direct Trace Export CPU + 348 ms ConvertCBORToJSON + 194 ms EncodeFrameHybi17 in Phase 3): Because Lighthouse's Trace gatherer omits transferMode: 'ReturnAsStream', utility:tracing serializes Perfetto trace packets to JSON (836 ms in ConsumerHost::TracingSession::OnTraceData, 586 ms in ExportJson) and sends chunks to CrBrowserMain, where TracingHandler::OnTraceDataCollected (948 ms CPU) scans braces in UpdateTraceDataBuffer (226 ms CPU) and parses the entire JSON trace into CBOR via ConvertJSONToCBOR (790 ms CPU) only for DevToolsSession::DispatchProtocolMessageToClient (322 ms CPU) to immediately convert that CBOR back into JSON via ConvertCBORToJSON (348 ms CPU), followed by EncodeFrameHybi17 copying the payload one byte at a time via std::back_inserter (194 ms CPU). (Fixed in PR #17407 — cuts Browser Phase 3 CPU by 45% and eliminates 948 ms of OnTraceDataCollected.)
  2. Linear $O(N)$ Rule Scans During startRuleUsageTracking, Quadratic $O(M \times N)$ Rule Mapping in InspectorStyleSheet, & Triple CSS.enable/CSS.disable Thrashing (1,658 ms direct CPU):
  3. In-Page Gatherer Script Execution (Runtime.evaluate) & Induced Style/Layout Recalculations (1,316 ms direct CDP in Phase 4 + 1,664 ms extra style/layout CPU): V8RuntimeAgentImpl::evaluate (1,198 ms total: 512 ms in Phase 1 setup + 686 ms in Phase 4) and callFunctionOn (58 ms) execute Lighthouse's DOM-inspection scripts (Accessibility, ImageElements, TraceElements, AnchorElements), increasing Document::UpdateStyleAndLayoutTreeForThisDocument from 728 ms (baseline) to 2,392 ms (full_lh).
  4. Synchronous JPEG + Base64 Encoding of Trace Screenshots on CrBrowserMain & GPU Frame Readback (424 ms direct CPU + +1,168 ms GPU process CPU in Phase 2): TracingHandler::OnFrameFromVideoConsumer (350 ms CPU) records disabled-by-default-devtools.screenshot events whose DevToolsTraceableScreenshot::AppendAsTraceFormat (274 ms CPU) synchronously runs gfx::JPEGCodec::Encode (libjpeg_turbo: 118 ms self in encode_mcu_gather) and base::Base64EncodeAppend directly on the Browser UI thread (CrBrowserMain), while video frame capture increases gpu-process Phase 2 CPU from 272 ms (baseline) to 1,440 ms (full_lh).
  5. Unintended Stylesheet Re-Parsing on CrRendererMain During Navigation via Animation.enable (110 ms CPU in Phase 2): TraceElements enables the Animation domain before navigation solely to capture animation names. Every animation update during page load triggers InspectorAnimationAgent::AnimationUpdated (108 ms) -> CreateCSSId (82 ms) -> InspectorCSSAgent::StyleSheetId (60 ms), synchronously binding and re-parsing stylesheets on the renderer main thread during navigation.

1. Methodology & 4-Phase Architecture

All runs used a local Release-with-symbols Chromium build (commit 0a167bc56d56d, is_debug = false, dcheck_always_on = false, symbol_level = 1) with the custom stack profiler liblhprof.so (lhprof.cc) preloaded into the root browser process and inherited across all child processes (renderer, gpu-process, utility:tracing, utility:network, utility:storage).

Each run is partitioned into four synchronized phases via shared memory (control.bin):

flowchart LR
    P1["Phase 1: Setup & Prepare<br/>(Target/Network/Page/DOM/Debugger enable,<br/>startInstrumentation)"] --> P2["Phase 2: Navigation<br/>(Tracing.start + gotoURL<br/>https://www.cnn.com/)"]
    P2 --> P3["Phase 3: Stop & Drain<br/>(Tracing.end + dataCollected,<br/>takePreciseCoverage)"]
    P3 --> P4["Phase 4: Post-Navigation<br/>(28 Gatherers getArtifact:<br/>CSSUsage, Stylesheets, ImageElements,<br/>Accessibility, TraceElements, etc.)"]
Loading

2. Process & Thread CPU Breakdown Across Configurations (Release Build)

2.1 CPU Time by Process Type (ms)

Process Type baseline (ms) tracing_only (ms) full_lh (ms) tracing_only Delta full_lh Delta Primary Cause of Overhead
renderer 4,638 6,134 11,444 +1,496 (+32.3%) +6,806 (+146.7%) Phase 2 trace emission & Animation.enable; Phase 3 flush; Phase 4 CSS (1,650 ms), Runtime.evaluate (1,198 ms), & induced style recalcs
browser 1,056 2,820 3,982 +1,764 (+167.0%) +2,926 (+277.1%) Phase 2 screenshot JPEG encode (354 ms); Phase 3 TracingHandler JSON->CBOR->JSON & WebSocket send (1,420 ms); Phase 4 BFCache & screenshots (918 ms)
gpu-process 290 1,534 1,852 +1,244 (+429.0%) +1,562 (+538.6%) FrameSinkVideoCapturerImpl video capture for devtools.screenshot (+1,168 ms in Phase 2) + extra compositing in Phase 3/4
utility:tracing 0 1,094 1,032 +1,094 (new) +1,032 (new) Perfetto TracingServiceImpl::ReadBuffersIntoConsumer, TraceSorter, ProtoTraceParser, and ExportJson
utility:network 670 608 792 -62 (-9.3%) +122 (+18.2%) NetworkServiceDevToolsObserver raw request/response CDP serialization & trace events
utility:storage / other 50 46 60 -4 +10 Service worker storage & proxy resolver utility processes
Total CPU Time 6,704 12,236 19,162 +5,532 (+82.5%) +12,458 (+185.8%)

2.2 Top Threads by Total & Direct-Instrumentation CPU Time

Thread (process::thread) baseline Total / Inst (ms) tracing_only Total / Inst (ms) full_lh Total / Inst (ms) full_lh Overhead vs. baseline
renderer::chrome (CrRendererMain) 3,662 / 872 5,380 / 1,226 9,184 / 4,012 +5,522 ms (+150.8%)
browser::chrome (CrBrowserMain) 476 / 100 2,528 / 1,514 2,684 / 1,964 +2,208 ms (+463.9%)
gpu-process::VizCompositorThread 228 / 10 1,522 / 96 1,718 / 98 +1,490 ms (+653.5%)
renderer::ThreadPoolForeground 638 / 12 754 / 28 1,190 / 24 +552 ms (+86.5%)
utility:tracing::chrome (TracingService) 0 / 0 1,058 / 968 976 / 940 +976 ms (100% tracing)
renderer::Compositor 212 / 0 412 / 18 816 / 30 +604 ms (+284.9%)
utility:network::Chrome_ChildIOThread 468 / 2 440 / 2 574 / 2 +106 ms (+22.6%)
browser::Chrome_DevTools (WebSocket I/O) 66 / 2 268 / 4 442 / 26 +376 ms (+569.7%)
browser::Chrome_IOThread 136 / 2 290 / 18 388 / 28 +252 ms (+185.3%)

3. Direct Instrumentation CPU Attribution by Subsystem (Release Build)

By classifying every symbolized stack frame against Chromium's tracing, Perfetto, and DevTools Inspector subsystems (analyze_profile.py), 7,208 ms (37.6% of total Chromium CPU) in full_lh occurs directly inside tracing/DevTools C++ stack frames:

Instrumentation Subsystem baseline (ms) tracing_only (ms) full_lh Total (ms) full_lh P1 (ms) full_lh P2 Nav (ms) full_lh P3 Drain (ms) full_lh P4 Gather (ms)
1. CDP: Transport, Session Dispatch & Runtime.evaluate 568 578 2,052 514 94 128 1,316
2. Trace Export & JSON Serialization 6 1,874 1,852 0 6 1,840 6
3. CDP: Animation & CSS Inspector (CSS / RuleUsage) 0 0 1,658 0 104 6 1,548
4. Perfetto / Trace Event Emission & Buffering 34 490 528 4 412 94 18
5. DevTools Trace Screenshots (devtools.screenshot) 2 422 424 0 424 0 0
6. V8 Debugger & Async Call Stacks (Debugger.enable) 190 324 230 2 196 4 28
7. CDP: Network Inspector (InspectorNetworkAgent) 134 108 220 0 128 0 92
8. CDP: DOM, Audits, Page, Log & WebMCP Agents 52 54 192 2 54 10 126
9. V8 Precise JS Coverage (takePreciseCoverage) 14 8 50 0 8 40 2
10. WebDX Feature Usage Tracking (blink.webdx_feature_usage) 0 2 2 0 0 0 2
Total Direct Instrumentation Stack Time 1,000 3,860 7,208 522 1,426 2,122 3,138
Normal Browser Work (Non-Instrumentation) 5,704 8,376 11,954 192 6,944 1,562 3,256
Total Chromium CPU Time 6,704 12,236 19,162 714 8,370 3,684 6,394

(Note: In baseline, the 514 ms in Phase 1 is prepareTargetForNavigationMode evaluating user-agent/emulation setup via Runtime.evaluate, and the remaining 486 ms in Phase 2 is baseline Network.enable, Page.enable, and Debugger.enable probes used by Lighthouse's Driver to monitor page load.)


4. Deep-Dive Ranked C++ Hotspots (Release Build)

Hotspot #1: Trace Export, Redundant JSON -> CBOR -> JSON Conversion, and Byte-by-Byte WebSocket Framing (1,852 ms Trace Export + 542 ms CBOR->JSON & WebSocket Copy in Phase 3)

When NavigationRunner finishes Phase 2 navigation and calls Trace.stopSensitiveInstrumentation (Tracing.end), Chromium spends 3.51 seconds of wall-clock time and 2.39 seconds of CPU time across three threads (utility:tracing::chrome, browser::chrome, and browser::Chrome_DevTools) just to export and deliver the 80,749-event trace to Lighthouse:

sequenceDiagram
    participant U as utility:tracing::chrome<br/>(908 ms CPU)
    participant B as browser::chrome (CrBrowserMain)<br/>(1,270 ms CPU)
    participant D as browser::Chrome_DevTools<br/>(196 ms CPU)
    participant L as Lighthouse (Node.js)

    U->>U: TracingServiceImpl::ReadBuffersIntoConsumer (908 ms)<br/>1. ProtoTraceParser / TrackEventEventImporter (164 ms)<br/>2. ExportJson -> JsonExporter::ExportSlices (586 ms)
    U->>B: Mojo IPC: ConsumerHost::OnTraceData (JSON chunks)
    B->>B: TracingHandler::OnTraceDataCollected (948 ms)<br/>1. UpdateTraceDataBuffer scans braces/strings (226 ms)<br/>2. TracingNotification::AppendSerialized:<br/>   ConvertJSONToCBOR parses JSON -> CBOR (790 ms)<br/>3. DevToolsSession::DispatchProtocolMessageToClient:<br/>   ConvertCBORToJSON converts CBOR -> JSON (348 ms)
    B->>D: PostTask to Chrome_DevTools I/O thread
    D->>L: ServerWrapper::SendOverWebSocket<br/>EncodeFrameHybi17 -> base::Extend copies payload<br/>byte-by-byte via std::back_inserter (194 ms)
Loading

Call Stacks & Symbolized CPU Breakdown (Phase 3 of full_lh)

  1. utility:tracing Process (908 ms CPU in Trace Export):

  2. browser::chrome (CrBrowserMain, 948 ms in OnTraceDataCollected + 322 ms in DispatchProtocolMessageToClient):

  3. browser::Chrome_DevTools (196 ms CPU in EncodeTextFrame):


Hotspot #2: InspectorCSSAgent & InspectorAnimationAgent Overhead During Both Navigation and Post-Navigation (1,658 ms Direct CPU)

2A. Unintended Stylesheet Re-Parsing on CrRendererMain During Navigation (110 ms CPU in Phase 2)

Before navigation starts, TraceElements.startInstrumentation sends Animation.enable. During Phase 2 page load on CNN.com:

2B. Linear $O(N)$ Rule Scans in startRuleUsageTracking, Quadratic $O(M \times N)$ Rule Mapping in SourceDataForRule, & Triple CSS.enable / CSS.disable Thrashing in Phase 4 (1,650 ms CPU)

In Phase 4, blink::protocol::CSS::DomainDispatcherImpl::Dispatch consumes 1,650 ms of CPU time on CrRendererMain:

CDP Command Inclusive CPU (ms) Calling Lighthouse Gatherer(s) Underlying Blink C++ Function
CSS.startRuleUsageTracking 672 ms CSSUsage InspectorCSSAgent::startRuleUsageTracking -> UpdateStyleAndLayoutTree -> SortAndTransferMatchedRules (666 ms)
CSS.stopRuleUsageTracking 456 ms CSSUsage InspectorCSSAgent::stopRuleUsageTracking -> takeCoverageDelta (450 ms) -> SourceDataForRule (446 ms)
CSS.getMatchedStylesForNode 326 ms ImageElements InspectorCSSAgent::getMatchedStylesForNode (258 ms) -> BuildArrayForMatchedRuleList (220 ms)
CSS.enable (x3) 160 ms CSSUsage, Stylesheets, ImageElements InspectorCSSAgent::CompleteEnabled -> SetActiveStyleSheets (158 ms)
CSS.getStyleSheetText 50 ms Stylesheets InspectorCSSAgent::getStyleSheetText

Three compounding issues make Phase 4 CSS gathering so expensive:

  1. Linear $O(N)$ CSSOM scans during startRuleUsageTracking (666 ms CPU): When StyleRuleUsageTracker is active during the forced full-document style recalculation, ElementRuleCollector::SortAndTransferMatchedRules calls FindStyleSheet (616 ms incl) -> FindStyleRule<CSSStyleSheet> (604 ms incl) and FindStyleRule<CSSRuleList> (402 ms incl, 128 ms self), linearly iterating through every CSSOM rule in every stylesheet (LiveCSSRuleList::length, CSSStyleSheet::item) to locate the parent CSSStyleSheet of each matched StyleRule.
  2. Quadratic $O(M \times N)$ CSSOM traversal inside InspectorStyleSheet::SourceDataForRule (446 ms CPU):
    • For every used CSS rule in takeCoverageDelta and every matched rule in getMatchedStylesForNode, SourceDataForRule(rule) calls RemapSourceDataToCSSOMIfNecessary() (588 ms total) and cssom_flat_rules_.Find(rule).
    • RemapSourceDataToCSSOMIfNecessary() unconditionally creates a fresh CSSRuleVector and walks the entire stylesheet's CSSOM tree via CollectFlatRules(page_style_sheet_.Get(), &cssom_rules) on every single rule lookup, spending 300 ms self time in LiveCSSRuleList<CSSStyleRule>::length(), 42 ms self time in LiveCSSRuleList<CSSGroupingRule>::length(), and 116 ms (96 ms self) in CSSStyleSheet::item.
  3. Repeated CSS.disable resets: CSSUsage, Stylesheets, and ImageElements each call CSS.enable followed by CSS.disable. InspectorCSSAgent::disable calls Reset(), destroying all InspectorStyleSheet objects so the next gatherer must re-parse every stylesheet on the page (BindStyleSheet -> ParseText: 198 ms) and re-run MapSourceDataToCSSOM (318 ms).

Hotspot #3: Post-Navigation Gatherers (Runtime.evaluate, Network.getResponseBody, DOM, Accessibility, BFCacheFailures) in Phase 4

During Phase 4 (getArtifact), full_lh spends 6,394 ms of CPU time (10.75 s wall time). Outside of InspectorCSSAgent (1,548 ms in Phase 4, covered in Hotspot #2), the remaining Phase 4 CPU time is driven by:

  1. Injected DOM & Layout Inspection Scripts via Runtime.evaluate / Runtime.callFunctionOn (1,316 ms direct CDP in Phase 4 + Induced Blink Style/Layout):
  2. Network.getResponseBody & InspectorNetworkAgent (220 ms direct CPU):
  3. BFCacheFailures Gatherer Navigation in Phase 4:
    • BFCacheFailures.getArtifact (~1.89 s wall time) navigates away to about:blank and back to https://www.cnn.com/ at the end of Phase 4, triggering an additional round of page lifecycle/compositor work when the page is not BFCache-eligible.

Hotspot #4: Perfetto TrackEvent Emission During Navigation (528 ms Direct CPU in Release)

During Phase 2 navigation (412 ms) and Phase 3 flush (94 ms), emitting trace events across all threads accounts for 528 ms of direct CPU time in the Release build:

  1. Task & Scheduler Trace Lambdas (126 ms CPU):
  2. V8 & Blink Trace Event Serialization (140 ms CPU):
    • v8::tracing::EscapeAndAppendString (54 ms), v8::internal::Script::TraceScriptRundownSources (26 ms), cc::frame_viewer_instrumentation::ScopedRasterTask (20 ms), and cc::CompositorFrameReporter::ReportCompositorLatencyTraceEvents (18 ms).
  3. V8 Debugger & Async Call Stacks (230 ms CPU):

Hotspot #5: DevTools Trace Screenshots (disabled-by-default-devtools.screenshot, 424 ms Direct CPU + +1,168 ms GPU Process CPU in Phase 2)

Enabling disabled-by-default-devtools.screenshot in Trace._defaultTraceCategories activates continuous frame capture from the GPU compositor into the Browser UI thread during Phase 2 navigation:

  1. GPU Process (VizCompositorThread, 64 ms direct DidCopyFrame CPU + +1,168 ms induced GPU compositing/readback in Phase 2):
    • viz::FrameSinkVideoCapturerImpl::DidCopyFrame (64 ms) reads back compositor surfaces and converts pixel formats (ReadI420Planes, I422ToARGBRow_AVX2), driving total gpu-process Phase 2 CPU from 272 ms (baseline) to 1,404 ms (tracing_only) and 1,440 ms (full_lh).
  2. Browser Process (CrBrowserMain, 354 ms direct CPU):

5. Actionable Optimization Recommendations & Validation Results

In Lighthouse (GoogleChrome/lighthouse)

  1. Stream Trace Collection via transferMode: 'ReturnAsStream' in Trace — Opened as PR #17407:
    • Passing transferMode: 'ReturnAsStream' to Tracing.start and incrementally parsing chunks via IO.read after Tracing.tracingComplete eliminates TracingHandler::OnTraceDataCollected (948 ms -> 0 ms), UpdateTraceDataBuffer (226 ms -> 0 ms), and the entire ConvertJSONToCBOR trace round-trip (790 ms -> 0 ms).
  2. Keep DOM and CSS Domains Enabled Across Phase 4 Gatherers Instead of Toggling CSS.enable / CSS.disable 3 Times — Opened as PR #17408:
  3. Avoid Animation.enable During Navigation in TraceElements:
    • Animation.enable is only used to resolve animation names for TraceElements, yet it causes InspectorAnimationAgent::AnimationUpdated to synchronously bind and re-parse CSS stylesheets on CrRendererMain during Phase 2 page load (110 ms Release CPU).

Validated Impact of PR #17407 + PR #17408 on Chromium Release Build

Metric / Symbol (Release Build) full_lh Baseline (main) full_lh After (PR #17407 + PR #17408) Change
content::protocol::TracingHandler::OnTraceDataCollected 948 ms 0 ms -100.0% (-948 ms)
crdtp::json::ConvertJSONToCBOR (in OnTraceDataCollected) 790 ms 0 ms -100.0% (-790 ms)
content::protocol::TracingHandler::UpdateTraceDataBuffer 226 ms 0 ms -100.0% (-226 ms)
Browser Process Trace Export & JSON Serialization 978 ms 16 ms – 28 ms -97.8% (-950 ms to -962 ms)
Total Browser Process CPU in Phase 3 (Stop Instrumentation) 1,420 ms 774 ms – 794 ms -44.8% (-626 ms to -646 ms)
Total Trace Export & JSON Serialization (All Processes) 1,852 ms 928 ms – 940 ms -49.6% (-912 ms to -924 ms)
blink::InspectorStyleSheet::RemapSourceDataToCSSOMIfNecessary 588 ms 440 ms – 446 ms -24.7% (-142 ms to -148 ms)
blink::InspectorCSSAgent::BindStyleSheet / ParseText 198 ms 136 ms -31.3% (-62 ms)
blink::protocol::CSS::DomainDispatcherImpl::Dispatch 1,650 ms 1,358 ms – 1,390 ms -16.7% (-260 ms to -292 ms)

In Chromium (chromium/chromium)

  1. Eliminate Linear $O(N)$ FindStyleSheet/FindStyleRule Scans in ElementRuleCollector::SortAndTransferMatchedRules (616 ms Release CPU) and Quadratic $O(M \times N)$ CSSOM Re-Traversal in InspectorStyleSheet::SourceDataForRule (446 ms Release CPU):
    • Cache the StyleRule -> CSSStyleSheet mapping during startRuleUsageTracking instead of scanning every stylesheet's CSSRuleList linearly in FindStyleSheet/FindStyleRule (616 ms CPU).
    • Track a dirty bit or mutation counter on CSSStyleSheet so RemapSourceDataToCSSOMIfNecessary() is a no-op when the CSSOM has not mutated, and replace the linear cssom_flat_rules_.Find(rule) with a hash map lookup (446 ms CPU).
  2. Avoid ConvertJSONToCBOR + ConvertCBORToJSON in TracingNotification:
    • TracingHandler::OnTraceDataCollected already holds valid JSON from Perfetto's ExportJson; when the client session uses JSON over WebSocket, pass pre-serialized JSON directly instead of round-tripping through CBOR (~1,138 ms Release CPU across ConvertJSONToCBOR + ConvertCBORToJSON).
  3. Fix Byte-by-Byte std::back_inserter in net::WebSocketEncoder::EncodeFrameHybi17:
    • Replace base::Extend(frame, message) (which falls back to std::ranges::transform with std::back_inserter because frame is std::vector<char> and message is std::string_view, i.e., const char) with frame.insert(frame.end(), message.begin(), message.end()) (194 ms Release CPU).
  4. Offload Screenshot JPEG + Base64 Encoding From CrBrowserMain in TracingHandler::OnFrameFromVideoConsumer:

Activity

  1. connorjclark commented on Oct 21, 2026

    @connorjclark
    CollaboratorAuthor

    Avoid Animation.enable During Navigation in TraceElements:

    Ignoring. Don't want to regress features.

    Offload Screenshot JPEG + Base64 Encoding From CrBrowserMain in TracingHandler::OnFrameFromVideoConsumer:

    Ignoring. @paulirish is working on this already.

    Cache Process ID in perfetto::TraceWriterImpl:

    Ignoring. Don't care about debug builds.

  2. connorjclark commented on Oct 22, 2026

    @connorjclark
    CollaboratorAuthor

    Avoid ConvertJSONToCBOR + ConvertCBORToJSON in TracingNotification:

    Ignoring. Doesn't help Lighthouse once we've moved to streamed tracing.

  3. connorjclark commented on Oct 22, 2026

    @connorjclark
    CollaboratorAuthor

    Fix Byte-by-Byte std::back_inserter in net::WebSocketEncoder::EncodeFrameHybi17:

    https://chromium-review.git.corp.google.com/c/chromium/src/+/8626947

    Benchmark Results (https://www.cnn.com/, Chromium Release Build)

    1. On Lighthouse main
    Metric / Function Before (main) After (crrev.com/c/8626947) Delta
    EncodeFrameHybi17 Self CPU (per-byte loop) 156 ms 2 ms -98.7% (-154 ms)
    EncodeFrameHybi17 Inclusive CPU (encoding + memcpy + alloc) 194 ms 30 ms -84.5% (-164 ms, 6.5x faster)
    net::WebSocketEncoder::EncodeTextFrame Inclusive CPU 196 ms 28 ms -85.7% (-168 ms)
    net::WebSocket::Send Inclusive CPU 312 ms 122 ms -60.9% (-190 ms)
    Total Chrome_DevToolsHandlerThread CPU (entire run) 442 ms 256 ms -42.1% (-186 ms)
    2. With Lighthouse #17407 (ReturnAsStream) + #17408 (Keep DOM/CSS Alive)
    Metric / Function Before After (crrev.com/c/8626947) Delta
    EncodeFrameHybi17 Self CPU (per-byte loop) 148 ms 0 ms -100.0% (-148 ms)
    EncodeFrameHybi17 Inclusive CPU (encoding + memcpy + alloc) 190 ms 42 ms -77.9% (-148 ms, 4.5x faster)
    net::WebSocketEncoder::EncodeTextFrame Inclusive CPU 190 ms 42 ms -77.9% (-148 ms)
    net::WebSocket::Send Inclusive CPU 306 ms 136 ms -55.6% (-170 ms)
    Total Chrome_DevToolsHandlerThread CPU (entire run) 410 ms 252 ms -38.5% (-158 ms)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions