Skip to content

perf(ios): stop per-frame full-buffer copies on the main actor - #279

Merged
schiste merged 3 commits into
mainfrom
agent/ios-app-performance-main-thread-output-b
Oct 23, 2026
Merged

schiste merged 3 commits into
mainfrom
agent/ios-app-performance-main-thread-output-b

Conversation

@schiste

@schiste schiste commented Oct 23, 2026

Copy link
Copy Markdown
Member

Summary

iOS app performance: main-thread output buffering, diagnostics log trimming, renderer leaks

Problem: Three buffers did whole-buffer work on the main actor once they were
full.

  • RemoteTerminalOutputStore rebuilt committed + pending and re-trimmed
    the 200 KB tail on every 16 ms flush, even while the rich renderer was
    on screen and nothing read the text.
  • DiagnosticsLog, at its 8000-entry cap, mapped every category, removed
    index 0 and forced a full JSONL rewrite for each new entry. Verbose
    logging is on by default, so this ran constantly.
  • The renderer engine kept each tab's replay buffer (up to 400 KB) after
    the playback was built from it, although nothing reads it again.
  • The diagnostics viewer filtered all entries twice per body.

Decision: - The output store appends in place and trims only after 32 KB of
slack. visibleOutput still returns exactly the retained cap.

  • DiagnosticsRetentionPolicy gains an entryTarget: over 8000 entries the
    log trims to 7000 in one batch, and removals happen in a single pass.
  • ensurePlayback frees the replay once the playback exists, and the
    diagnostics list filters once per body.

Rationale: All three are amortisation changes with no visible behaviour change:
the same tails are retained and displayed, and the costs move from every
frame or record to once per batch.

Changes

Checklist

  • pnpm quality:prepush passes, or pnpm quality:prepush:full for high-impact changes
  • cargo test --workspace passes in apps/chau7-next (if macOS app changes)
  • swift test --package-path apps/chau7-ios/Chau7Core passes (if Swift changes)
  • New tests added for new behavior
  • Documentation updated (README, CHANGELOG.md if user-facing)

Test plan

xcodebuild test, Chau7RemoteApp scheme, iPhone 17 Pro simulator: 129
tests, 0 failures. New tests cover the newest tail surviving amortised
trims, batch trimming to the target, no trim at the cap, and a target
above the cap failing closed. The replay release has no direct test,
because the engine actor is not in the host-less test bundle.

Commits:

  • e6f5030 perf(ios): stop per-frame full-buffer copies on the main actor

Opened by aethyme broker push as a draft.

Problem:
Three buffers did whole-buffer work on the main actor once they were
full.
- RemoteTerminalOutputStore rebuilt committed + pending and re-trimmed
  the 200 KB tail on every 16 ms flush, even while the rich renderer was
  on screen and nothing read the text.
- DiagnosticsLog, at its 8000-entry cap, mapped every category, removed
  index 0 and forced a full JSONL rewrite for each new entry. Verbose
  logging is on by default, so this ran constantly.
- The renderer engine kept each tab's replay buffer (up to 400 KB) after
  the playback was built from it, although nothing reads it again.
- The diagnostics viewer filtered all entries twice per body.

Decision:
- The output store appends in place and trims only after 32 KB of
  slack. visibleOutput still returns exactly the retained cap.
- DiagnosticsRetentionPolicy gains an entryTarget: over 8000 entries the
  log trims to 7000 in one batch, and removals happen in a single pass.
- ensurePlayback frees the replay once the playback exists, and the
  diagnostics list filters once per body.

Rationale:
All three are amortisation changes with no visible behaviour change:
the same tails are retained and displayed, and the costs move from every
frame or record to once per batch.

Validation:
xcodebuild test, Chau7RemoteApp scheme, iPhone 17 Pro simulator: 129
tests, 0 failures. New tests cover the newest tail surviving amortised
trims, batch trimming to the target, no trim at the cap, and a target
above the cap failing closed. The replay release has no direct test,
because the engine actor is not in the host-less test bundle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@schiste
schiste marked this pull request as ready for review October 23, 2026 15:43
schiste and others added 2 commits October 23, 2026 17:52
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ance-main-thread-output-b

# Conflicts:
#	CHANGELOG.md
@schiste
schiste merged commit 4999f78 into main Oct 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant