| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
📲 Install BuildsAndroid
|
Sorry, something went wrong.
Performance metrics 🚀
Baseline results on branch: mainStartup times
App size
|
Sorry, something went wrong.
There was a problem hiding this comment.
Sorry, something went wrong.
|
@0xadam-brown yeah I have some concerns, there's a thread going on about this internally: https://sentry.slack.com/archives/C089VFRB8N9/p1782999439636989 |
Sorry, something went wrong.
…plementation ScheduledThreadPoolExecutor's internal DelayedWorkQueue is a heap that resizes dynamically (50% growth from initial capacity 16). prewarm() was introduced to pre-grow that array during init, but doing so on the main thread is itself the worst possible time to trigger allocations — and the queue resize cost is only ~8µs anyway. This replaces the whole approach: a custom executor backed by a PriorityQueue pre-allocated to INITIAL_QUEUE_CAPACITY=64 at construction time. The backing array never resizes during normal SDK operation. A single daemon worker thread uses Object.wait/notifyAll for precise wakeup on scheduled tasks. prewarm() becomes a documented no-op. Key properties: - No array resize at runtime: queue pre-allocated at construction - Precise scheduling: worker sleeps until next task triggerTime, wakes immediately when an earlier task is enqueued - MAX_QUEUE_SIZE (271) and purge-on-overflow semantics preserved - ScheduledTask<T> extends FutureTask<T> for free Future<T> contract - Drops @testonly ScheduledThreadPoolExecutor constructor (nothing to inject) Refs #5681
SentryExecutorService is single-threaded, and prewarm() is submitted ahead of loadLazyFields() during init, so its 40-task schedule/cancel/purge loop cannot reduce first-task latency — it can only delay it. The thread creation and executor class loading it aimed to warm are paid identically by the first real task (loadLazyFields), which is submitted unconditionally right after, so prewarm warms nothing that would not already be warmed. On-device A/B benchmarks on a Galaxy A55 (Android 16) show no measurable first-useful-task speedup from prewarm and ~20us of extra background-thread work. Remove prewarm() from ISentryExecutorService and its implementations and from both init call sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Back | FazBrowse Home | New Git URL |
Summary
prewarm() scheduled 40 dummy tasks far in the future, cancelled them, and purged the queue, in order to pre-grow the single-threaded ScheduledThreadPoolExecutor's work queue during Sentry.init. On-device measurement shows the cost it avoids is negligible-to-zero, so this removes it (and the now-unused constants) along with its two init call sites.
Why it's safe to remove
prewarm's only job was to pre-grow the executor's work queue so a later resize wouldn't happen "in an unexpected area of the SDK". Measured on a Galaxy A55 (Android 16), growing the queue the full 16→54 (all three array resizes) costs ~8µs total — and that is the ceiling: init only ever queues a handful of tasks, well under the default 16-slot capacity, so in practice no resize happens and the saving is 0µs.
PrewarmBenchmarkTest, n=1000 × 3 runs, median µs. Both paths schedule 40 tasks into an empty queue (identical per-insert work); they differ only in whether the backing array resizes:
It also can't help, and slightly regresses init
The executor is single-threaded and prewarm is submitted ahead of the first useful task (loadLazyFields, submitted right after in Sentry.init), so the 40-task loop can only delay that task, never speed it up. Thread creation and executor class-loading are paid by whichever task runs first, so loadLazyFields warms them identically with or without prewarm.
Measured end-to-end, the time until the first useful task finishes (from a fresh executor, n=400 × 3 runs) is ~100µs slower with prewarm than without, at every percentile; a plain submit(noop) is indistinguishable from doing nothing:
(PrewarmBenchmarkTest also records the per-op loop cost and the process-once class-loading cost, which is unavoidable regardless of prewarm; both confirm the above.)
Changes
Original prewarm PR: #4606 — its only stated reason was the queue-growth cost measured above.
🤖 Generated with Claude Code