You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[non-fatal-anr-support] Find the right polling interval and sane defaults #6208
I've created a demo screen and a benchmark test (see the markushi/feat/anr-profiling-overhead branch), and ran them on a Pixel 8a test device.
Test setup
10 runs with the integration (66 ms polling interval) and 10 runs without it, in alternating order: with, without, with, without, and so on.
Each run is one 10s measurement window, with a ~4s main-thread hang (a backlog of queued messages) that starts at t = 2s.
All per-unit values are p95 values: 95 % of the measured events were at or below this value.
Measured per-unit costs (Pixel 8a, 66 ms, p95)
Poll rate: every 66 ms, as configured.
Idle poll cycle (background thread): 0.53 ms of CPU (median 0.30 ms). This covers the check, the Handler.post calls and waking up from sleep. The check itself takes 0.034 ms.
Idle poll cycle (main thread): the CPU cost is not measurable. The run-to-run variation is larger than that difference. This is great news!
Extra main-thread wakeups (due to polling): 12.3 per second (average). The other poll messages run in wakeups that frame redraws cause anyway.
Profile collection during a hang (background thread), per captured stack: 2.11 ms of CPU and 7.27 ms of wall time. The stack trace capture takes 0.69 ms (0.59 ms CPU), and the write to disk takes 5.68 ms (1.48 ms CPU). The capture pauses the main thread for up to 0.69 ms. The write blocks only the background thread.
Example use case: an app runs for one minute, no hiccup
Background thread: 909 poll cycles × 0.53 ms ≈ 482 ms of CPU. That is 0.8 % of one CPU core. This is an upper limit, because not every cycle reaches the p95 value. With the average cycle cost (0.31 ms), it is ≈ 282 ms.
Main thread: ≈ 741 extra wakeups (12.3 per second × 60), and up to 909 if no frames are drawn. The CPU overhead is not measurable, because each wakeup only writes one timestamp.
Conclusion
A 66ms polling interval has no measurable influence on the main thread, although we cause 12.3 extra main-thread wakeups per second. Most of the costs are on the background thread: ≈ 282–482 ms of CPU per minute.
Further optimizations
We could sync the polling with frame redraws / input dispatches. This way if the app is completely idle, we wouldn't need to poll at all.
Activity
linear-code commentedon Oct 1, 2026
JAVA-765
markushi commentedon Oct 2, 2026
I've created a demo screen and a benchmark test (see the
markushi/feat/anr-profiling-overheadbranch), and ran them on a Pixel 8a test device.Test setup
Measured per-unit costs (Pixel 8a, 66 ms, p95)
Example use case: an app runs for one minute, no hiccup
Conclusion
A 66ms polling interval has no measurable influence on the main thread, although we cause 12.3 extra main-thread wakeups per second. Most of the costs are on the background thread: ≈ 282–482 ms of CPU per minute.
Further optimizations
We could sync the polling with frame redraws / input dispatches. This way if the app is completely idle, we wouldn't need to poll at all.