2.5 KiB
Performance Analysis
Measurements with the --perf flag on desktop.glbc.
Measurement Results
Steady state (no events, idle):
VDOM: 6.6ms | Style: 0.3ms | Render: 7.2ms | Total: 14.1ms
Under events (slider drag, peak values):
VDOM: 17.4ms | Style: 0.8ms | Render: 15.2ms | Total: 33.5ms ⚠️
60 FPS frame budget: 16ms. At idle we fit (14ms), under events — not (up to 33ms).
Bottleneck Analysis
Style matching — NOT a bottleneck (0.3-0.8ms)
Style matching takes less than 1ms even at peak. This is the result of:
- Phase 4 (StyleIndex) — O(K) instead of O(N×M)
- Phase 8 (StyleCache) — computed style memoization
VDOM eval — main consumer (6-17ms)
At idle ~6.6ms — a full traversal of the Element tree. Under events up to 17ms:
evaluate_vdom_incrrecalculates dirty elements (Phase 3)- Each event can dirty entire subtrees
- Internally:
resolve_string,resolve_prop,compute_cached, recursive traversal
Render — second consumer (7-15ms)
This is where the main optimization headroom lies. render_element creates ALL Iced widgets
from scratch every frame, even if the Element hasn't changed. Iced then diffs the new tree
against the old one — but the tree construction itself costs ~7ms.
Scenario: slider
SliderChanged→ageandvolume_levelchangetracker.on_variable_changed→ dirty_set for dependent elementsevaluate_vdom_incrrecalculates dirty elements and their childrenview()→render_elementfor ALL elements (full re-render)- Result: VDOM 13ms + Render 14ms = 27ms — frame drop
The first few frames after an event are the heaviest (VDOM ~13ms), then stabilize (~7ms) as dirty_set gradually clears.
Recommendations
-
Phase 9.3 — widget cache — would reduce Render from 7ms to ~0ms for unchanged elements. If 1 out of 50 elements changes, only that one needs re-rendering. This would lower total time from 14ms to ~7ms at idle.
-
Phase 1 — Value enum in styles —
ComputedStyle::compute()andparse_*()take&strand parse each property. Passing&Valueinstead would skip parsing. Potentially speeds up both style matching and VDOM eval. -
Phase 2 — InternedStr — string comparisons (
type_name == "Button",key == "padding-top") happen thousands of times per frame. Replacing with u32 comparison would give 1.5-2× in VDOM and render paths. -
Phase 0.3 — flamegraph — confirm hypotheses with profiler measurements (
perf record) before investing in optimization.