perf(ios): learn generation-scoped native depth hints in the host AX source - #2427
Conversation
Size Report
Startup median (7 runs, lower is better):
|
|
At 2b68a57, recordRecovery saves the depth hint before createAcquisition validates the returned tree. A response with valid recovery counters but a malformed or over-limit tree can therefore fail acquisition and still affect the next capture. Learn only after acquisition validation succeeds, and add a failed-first-response test proving the next request stays unhinted. Please also reconcile bounded-recovery learning with #2424: the fixture expects a hint when complete is false, but the test ignores that field. Distinguish a successfully completed bounded request from incomplete recovery, and exercise that distinction rather than leaving an unused completeness claim. The 5.2 kB unpacked increase needs a short breakdown and the smaller-design tradeoff. Reconcile this branch with the newly split #2425 before merging. The iOS wait failure also occurs on the earlier base, so its relation to this change is unclear; it is not evidence of a clean full E2E run. |
2b68a57 to
b510ec8
Compare
|
At The fixture now exercises bounded captures, resolving the unused-field test gap. Its learn-from-bounded policy still needs an explicit reconciliation with #2424’s complete-recovery requirement; bounded is not automatically invalid. Please itemize the 5.2 kB growth and smaller-design tradeoff. The stack depends on #2428, which currently conflicts with #2425; iOS CI remains pending. |
…source The host source remembers the native levels a finished recovery accepted, keyed by resolved target id, app generation, and producer, and sends them as nativeLevelsHint so the guest's first request skips the known rejection. The hint changes request strategy only: delivered depth, node bounds, and completeness rules are unchanged. It is learned only after the delivered tree validated, expires after eight hinted captures, is never renewed by a hinted success, and never crosses apps, generations, or producers; explicit raw-depth requests neither use nor teach it. The route's generation circuit stays the only lifecycle owner. The guest reports its request accounting (requests, rejected, continuations, accepted levels) as recovery, which the host emits as the ios_snapshot_source_recovery diagnostic; the source version moves to v1.5.5. The shared recovery fixture gains the host column of every hint case and the hinted recovery cases, replayed through the source adapter itself. The test app gains a deep-tree screen that reproduces the native depth rejection for paired benchmarks.
b510ec8 to
28a681c
Compare
|
Addressed at
|
|
The validation-before-learning fix at 28a681c addresses the previous bug, and the package-growth breakdown explains the retained state and diagnostics. No remaining runtime blocker found. One wording nit: failed hinted requests still consume an expiry use, so describe them as leaving the learned depth unchanged, not all hint state untouched. Merge #2414, #2425 and #2428 first; checks on this head are still running. |
|
Summary
Third step of #2424, stacked on #2428 (runner adapter) and #2425 (fixture + host adapter). The host Simulator AX source keeps a bounded accepted-depth hint per resolved target id, app generation, and producer. After a recovery that observed a native depth rejection and finished, the next capture of that generation sends
nativeLevelsHint, so the guest's first request asks for the accepted levels instead of re-paying the known rejection. A hint changes request strategy only; delivered depth, node bounds, and completeness rules are unchanged. A hint is learned only after the delivered tree validated (a response with sound counters but an unusable tree teaches nothing), never crosses apps, generations, or producers, expires after eight hinted captures (the next one probes the full depth), is never renewed by a hinted success, is left untouched by a failed capture, and explicit raw-depth requests neither use nor teach it. The route's generation circuit stays the only lifecycle owner.The guest reports its request accounting as
recovery(requests, rejected, continuations, accepted levels), emitted as theios_snapshot_source_recoverydiagnostic; a guest that omits it fails closed. Source version v1.5.5. The shared fixture gains the host column of the hint cases and the hinted recovery cases; the hint cases run through the source adapter itself (adapter.test.ts), each step being a real acquisition whose fake guest rejects above the accepted levels and reports boundedness, sorejected,acceptedLevels, andcompleteare all consumed. The test app gainsdeep-tree?chain=N&width=M, which reproduces the native depth rejection without a third-party app.21 files, 875 lines, 343 of them production.
Validation
Tested head:
28a681c570. Runtime sources differ from the benchmarked2b68a57044only in the order of learning versus acquisition validation, which changes no request on the benchmarked paths.pnpm check:affected --run --base <#2428 head>passed.createAcquisition, with a test proving a sound-counters/malformed-tree response leaves the next request unhinted; the fixture'sbounded-recovery-still-learned(complete: false, learned) andrejected-at-every-depth-keeps-the-hint(failure, nothing learned) cases now run through the source adapter, so completeness and failure are both exercised.depth-hints.ts~2 kB bundled (the policy itself),adapter.ts+protocol.ts~1.5 kB (hint decision, recovery parsing, diagnostic), the packaged bridge sources ~2 kB (hint parameter, accounting struct, response block, header docs). A smaller design would droprequests/continuationsfrom the wire and the diagnostic (~0.4 kB) and the 32-target eviction bound (~0.3 kB); both were kept because perf(ios): align AX recovery contracts and evaluate generation-scoped depth hints #2424 requires paired native-call evidence and a bounded owner.never-across-generationsred; hinted success renews →learn-from-complete-recoveryred; native levels off by one → 8 red; missing boundary evidence accepted → 7 red; budget 33 → 3 red.--debugrequest logs showno-hint → learned, thenhintedwith zero rejections and one probe-back after eight uses.Paired benchmark
Base = #2414 head
7c84acfe68; hint =2b68a57044(same runtime sources as this head). Source CLI per variant, one dedicated iPhone 17 Pro / iOS 26.2 simulator, isolated state dir per cell, five relaunch rounds (open, first capture, first press, capture) plus ten warm captures per cell, runner warmed once per cell. Only regular captures change (raw explicit-depth requests bypass hints). Noise floor from the ordinary app: ±6 ms on ~55 ms medians. The #2414 Bluesky figure is not attributed here.The ordinary app never consumes a hint. Deep screens drop exactly the rejected request, whose cost grows with tree size (~50 ms at 90×1, ~260 ms at 70×6). After navigating deep → home inside one generation, hinted captures still take one native request with zero continuations (4/4; 63–78 ms vs 77–78 ms base). The probe-back shows as one full-cost capture per eight (warm max 146 / 619 ms, equal to base medians). No cell regressed beyond noise.