Unexpected latency in Dart isolate despite low CPU usage in VM Service
0 reputation · 19 Sept 2020, 14:44 UTC
0 reputation · 19 Sept 2020, 14:44 UTC
When a Dart isolate shows higher-than-expected wall‑clock latency but the VM Service CPU sampler reports minimal Dart function time, the team must decide whether the delay originates from asynchronous waits, garbage‑collection pauses, or work performed outside the Dart isolate such as platform channels or rendering frames.
The investigation should be run in release/profile mode with a realistic load to avoid debug‑mode artefacts, and the same scenario must be replayed before any code change to obtain a comparable baseline. Because timeline overhead and sampling frequency can distort short intervals, and behavior varies across SDK and DevTools versions, the attribution of time to each source remains uncertain.
Which VM Service metric—CPU samples, async gap duration, or GC event count—most reliably isolates the dominant contributor? How can the timeline view be configured to separate platform‑channel callbacks from Dart‑isolated work? What threshold of asynchronous wait time relative to total latency indicates that optimization should target I/O rather than Dart code?
35275 reputation · 20 Sept 2020, 01:20 UTC
Start with async gap duration, not CPU samples. The three metrics answer different questions, and only one of them measures the thing you actually observe—wall-clock time:
await) and resumption. This is where I/O waits, platform-channel round trips, timer delays, and event-loop scheduling delays all land. It is the metric that most directly accounts for the missing time.So the practical order is: confirm CPU samples are low (done), check GC pauses to exclude them, then attribute the remainder to async gaps and break those gaps down by source.
In the DevTools timeline, enable the full event stream rather than the default sampled view, then:
flutter run --profile or an AOT build with the VM Service enabled) under realistic load. Debug mode inflates async overhead enough to invalidate the comparison.Timeline.startSync('channel:methodName') / Timeline.finishSync() (from dart:developer) so each call appears as a labeled interval you can sum.Caveat: the exact category names and thread separation vary across DevTools and SDK versions—older versions lump non-Dart work into a generic interval. Verify against your installed version before trusting the labels.
There is no authoritative threshold published by the Dart team, so treat this as a heuristic, not a rule: if summed async gap time exceeds roughly 20–30% of total operation latency while Dart CPU time stays negligible, optimization effort belongs on the I/O side—channel batching, fewer round trips, connection reuse, or moving work off a contended thread. Below that, look at scheduling and GC first.
Because sampling frequency and timeline overhead can distort short intervals, cross-check before changing code:
pidstat -w 1 -p <pid> shows voluntary context switches consistent with I/O waiting, independent of the VM's own accounting.One detail would sharpen this further: whether the latency is uniform per operation or appears as occasional spikes—spikes point toward GC or scheduling, uniform delay points toward channel or I/O round trips.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.