Skip to content

Host GPU/CPU stays high while an agent spinner dirties a large local surface (request: spinner_render_interval_ms) #4546

Closed as not planned

Description

@sege66

Host GPU/CPU stays high while an agent spinner dirties a large local surface

Environment

  • herdr 0.9.1-preview.2026-09-21-0ff0f27e2226
  • macOS 26.6.2 (build 25G83), Apple M1 Max, 10 cores, 32 GB, arm64
  • Host terminal: Ghostty 1.3.1 (local client, not SSH)
  • [experimental] reveal_hidden_cursor_for_cjk_ime = false already
  • herdr --default-config has no FPS, idle-throttle, or spinner interval key. [advanced] only documents scrollback_limit_bytes.
  • At this revision MIN_RENDER_INTERVAL is hardcoded to 16ms (src/app/mod.rs) and applied by can_render_now / can_present_now (src/app/runtime.rs). That is a cadence cap when a render is already needed, not proof of a 60fps idle loop.

What happens

With at least one agent pane producing a spinner (no requirement that the agent be "busy" in herdr's status sense — a waiting CLI still writes frames):

  • Ghostty sustained about 43–55% of one core across repeated 40s ps TIME deltas on 2026-09-23/24, and 53.6% on one 40.05s delta on 2026-09-24 (231:07.46 → 231:28.94) while an agent session was active.
  • Same 2026-09-24 window: herdr server 4.6%, herdr client 0.5%.
  • On the earlier passes, WindowServer sat around 35–45%. Stack samples of Ghostty were dominated by glyph work (addGlyph / rebuildRow / renderGlyph), i.e. the host terminal rasterizing updates, not herdr's own process.
  • Reported surface at the time of those passes: about 554×109 (client) and 594×109 (surface). Geometry was not re-measured on 2026-09-24.

changed_rows in src/server/headless/retained_surface.rs at this revision already emits a changed cell run (plus one following cell for wide-glyph coverage), not a mandatory full-width row, as long as the dirty snapshot is valid. The expensive part observed here is still on the far side of that update: Ghostty rebuilds glyphs for a very wide row often enough to pin a core, and WindowServer composites it. MIN_RENDER_INTERVAL of 16ms means a spinner that dirties a cell every tick can present up to ~62fps.

This is adjacent to, not the same as:

Proposal

Add an opt-in coalesce for high-frequency, tiny PTY updates (spinners) before they are presented to the host terminal. Suggested key, default = current behavior:

[advanced]
# Minimum interval between presenting spinner-scale pane updates
# to the host terminal. 0 or unset = today's MIN_RENDER_INTERVAL (16ms).
spinner_render_interval_ms = 100

Either a dedicated key or a broader "idle output throttle" is fine. Requirements:

  1. Default remains 16ms so existing sessions do not change.
  2. Apply only to small dirty runs (a few cells), not to real streaming transcript output.
  3. Coalesce in the presenter (drop or merge intermediate spinner frames). Do not busy-wait.
  4. A 100ms cap would still look animated and would cut host rasterization by roughly 6× versus a 16ms tick, which is the cost center on a ~550-column Ghostty surface.
  5. Please do not treat herdr status glyphs (● / ◐) as the fix. Those are static. The frames come from the pane's own CLI spinner.

Happy to re-measure with the same 40s ps TIME delta after a build. I have not attached a sample trace here.

Activity

  1. self-assigned this
    on Sep 23, 2026
  2. ogulcancelik commented on Sep 23, 2026

    @ogulcancelik
    Collaborator

    i tested the exact reported herdr preview on macos 26.6.2 with ghostty 1.3.1, using a one-cell spinner over a 568×108 text area on an m2 max.

    at 60 hz, 40-second cpu measurements showed ghostty at about 21% directly versus 24% through herdr. windowserver was about 34% directly versus 35–37% through herdr. herdr’s client used about 1.2% and its server about 3.9%. the small-patch path was working.

    i didn’t reproduce the full ghostty cpu figure, but most of the animation cost also occurs without herdr. i’m closing this rather than adding a spinner-specific throttle on this evidence.

    if you can show a larger herdr-specific difference, please open a new issue using the bug template and link this one. include the agent cli/version, exact reproduction steps, and a direct-versus-herdr comparison. please keep proposed implementations out of the bug report.

  3. kangal-bot commented on Sep 24, 2026

    @kangal-bot
    Collaborator

    Implemented on master and queued for the next release. This is not available in a published Herdr release yet.

  4. kangal-bot commented on Sep 28, 2026

    @kangal-bot
    Collaborator

    Released on the preview channel in 2026-09-28-80c0c07250d2. This is available to preview users, but is not in a stable Herdr release yet.

  5. sege66 commented on Sep 28, 2026

    @sege66
    Author

    The preview feed (https://herdr.dev/preview.json) still points to build_id: 2026-09-21-0ff0f27e2226, and 2026-09-28-80c0c07250d2 isn't in the builds map either (checked 2026-09-29 with cache-busting). So for a preview install on 09-21, herdr update reports up to date and the 09-28 build can't be reached from the channel.

    Manually installing the release binary doesn't stick either: the updater treats the manifest build_id as the expected preview build, so the next launch detects the mismatch and reinstalls 09-21 over it (observed a fresh binary write plus a release-notes.json rewrite on the next start).

    Could the preview feed be promoted to 09-28?

  6. kangal-bot commented on Sep 29, 2026

    @kangal-bot
    Collaborator

    Released in v0.9.2.

  7. kangal-bot commented on Oct 12, 2026

    @kangal-bot
    Collaborator

    Released on the preview channel in 2026-10-12-6d697e1e75ff. This is available to preview users, but is not in a stable Herdr release yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

maintainer-neededrequires maintainer judgment or maintainer-only reproductionpreview-releasedAvailable on the preview channel, pending a stable release.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions