# A paste-timer bug that slowed OMD startup

Published 2026-10-14. URL: https://ohmydiff.me/blog/startup-latency-extra-frame

OMD's startup missed its latency target by less than a millisecond, and a second run narrowed the miss to 0.21 ms. Instead of rerunning until the median passed, I looked at the individual samples. They fell into two groups about ten milliseconds apart, and in some runs the terminal showed one extra cursor-only frame before the text appeared. That frame led me to a scheduling bug in the startup composer's paste handling.

This post reconstructs OMD's October 12, 2026 investigation against Codex `rust-v0.162.1`. The numbers come from saved measurements; I did not rerun the benchmark on the current release. The deadline mechanism and its regression test are still in the Codex `0.166.0` patch that OMD 1.4.0 ships.

The target required OMD Core and OMD+ to reach a typeable prompt, and to respond to the selected slash command, within 1.10 times stock Codex's median in both Kitty and Orca. A prompt counted as typeable once the typed probe appeared in application output. The harness measured the first composer frame and completed mod preparation separately.

Earlier work had already made `/mods` fast enough, so the only remaining failure was OMD+ startup in Orca. A quiet run measured 78.527 ms against stock Codex's 70.771 ms, or 1.110 times upstream. A repeat measured 72.386 ms against 65.613 ms, or 1.103 times upstream. Both missed the unchanged 1.10 threshold. Kitty passed.

Because the failing measurement was startup, and the prompt accepted input before mod preparation finished, I stopped optimizing command callbacks and traced startup instead.

The trace split startup into two intervals: reaching the first composer, then making the input visible. The first interval cost about the same every time. OMD's composer arrived roughly 5–7 ms after upstream's across the saved runs, even when the OMD binary ran without mods. Profiling pointed to binary startup and first-frame work, and stripping symbols did not improve the measured launch probe.

The second interval was bimodal, with samples clustered about ten milliseconds apart. Kitty's OMD+ runs usually landed in the faster cluster, and Orca's often landed in the slower one. Stock Codex showed related bimodality too, and neither terminal was uniformly slower.

The harness sent `LATENCY_PROBE` in one write. The keys arrived fast enough that the composer classified them as a paste burst, so it buffered the text and emitted cursor-only frames. A later check had to decide that the burst had ended and move the buffered text into the visible draft.

The paste policy used an 8 ms idle timeout, and during a burst the scheduling code requested another frame after a fixed 9 ms. On their own, those two values looked compatible: wait a little longer than the timeout, draw again, and the text flushes.

The frame requester merged pending redraws by keeping the earliest deadline, so the check did not always run nine milliseconds after the last character. Later characters did not push the check back to follow the end of the burst. A timer scheduled from an earlier key could fire just before the idle timeout, measured from the final key, had elapsed.

Here are two traced samples:

| Orca OMD+ sample | Last key | Flush check | Idle time at check | Result |
| --- | ---: | ---: | ---: | --- |
| Fast | 56.30 ms | 64.49 ms | 8.19 ms | Flushes |
| Slow | 66.85 ms | 74.65 ms | 7.80 ms | Too early; another check needed |

In the slow sample, the text appeared at 85.92 ms. When the check ran a few tenths of a millisecond too early, the composer waited almost another full scheduling interval, so a small amount of jitter split the samples into two separate groups.

Because the composer relied on a redraw timer to check whether the paste had ended, a delayed frame delayed the input even after the paste timeout had passed.

Requesting the next frame after only the remaining paste timeout would not have fixed this. The frame requester's 120 FPS limiter kept at least about 8.33 ms between emitted frames, so if an early check had just emitted a frame, a second request shortly afterward could still push the flush back by a full frame interval.

Instead, I gave the startup draft its own paste deadline. `PasteBurst::flush_deadline()` returned the last character's timestamp plus the applicable timeout and a 100 µs margin. The margin is there because the existing flush predicate used a strict `>` comparison, so waking at exactly the timeout did not yet count as a valid flush.

The draft stored that instant and added a `sleep_until` branch to its event loop. When the deadline expired, the draft drew the composer, and the composer's first step flushed any buffered input that was due. Later keys moved the deadline later. The paste timer could now wake the composer without going through the frame requester.

In simplified pseudocode, the change was:

```text
Before:
  on draw during paste:
    request a frame in 9 ms

After:
  on draw:
    pending_deadline = paste.flush_deadline()

  on pending_deadline:
    clear the timer
    draw composer, flushing input that is now due
```

Only the startup draft got a separate timer. Other editors kept the existing frame-request fallback, and the startup handoff still flushed pending input.

Each measurement ran a PTY relay inside the real terminal, with native terminal negotiation. The harness took timestamps when it read application output, before the terminal painted. The reports identify macOS 26.5.2 on arm64 but no CPU model, so I don't assume a particular Apple chip. Orca used a 120×40 terminal and Kitty 110×36.

Each matrix ran ten interleaved measured launches per arm after one warmup. The four arms were stock upstream, the patched binary in direct mode, Core, and OMD+, for 44 launches per terminal. The reports record binary identities and harness hashes. The before and after reports use the same harness hash and upstream binary, and only the OMD candidate changes. No model requests were sent, and no compiler processes were sampled during these runs.

Here are the launch-to-typeable medians, recomputed independently from the saved non-warmup samples:

| Terminal and arm | Before deadline fix | After deadline fix | Change |
| --- | ---: | ---: | ---: |
| Orca, stock Codex control | 70.771 ms | 70.521 ms | −0.250 ms |
| Orca, OMD Core | 74.331 ms | 67.376 ms | −6.955 ms |
| Orca, OMD+ | 78.527 ms | 69.804 ms | −8.723 ms |
| Kitty, stock Codex control | 65.682 ms | 63.742 ms | −1.940 ms |
| Kitty, OMD Core | 63.739 ms | 59.603 ms | −4.136 ms |
| Kitty, OMD+ | 62.707 ms | 62.052 ms | −0.655 ms |

Orca OMD+ dropped by about 11.1%, and its ratio to the control measured alongside it went from 1.110 to 0.990. Kitty's OMD+ changed much less, which fits with its before samples already landing mostly in the fast mode. The before and after matrices are separate, unpaired runs, so with background load and shifts in the distribution I can't credit every decimal place to the patch.

I also report the repeat before run, which shows a smaller gain. It measured OMD+ at 72.386 ms, much closer to the after result, but still above the gate relative to its own faster upstream control. Both before runs show the extra-frame pattern, and the trace explains it; I put more weight on that than on any single percentage.

After the fix, Kitty's OMD+ composer-to-typeable intervals fell within 25.5–27.7 ms, where before they split into modes of roughly 26 and 36 ms. Orca's intervals still varied but no longer split into two modes about ten milliseconds apart. The after OMD+ launch-to-typeable p95 was 62.8 ms in Kitty and 74.5 ms in Orca. With ten measured samples per arm, those p95 values describe these runs and are weak estimates of the wider population.

The final matrices passed the startup and command median gates in both terminals. The command comparison used upstream `/status` and OMD `/mods`, which were chosen in advance as the useful operation in each, so the two sides of that comparison ran different workloads. A load spike late in the Kitty run affected the stock and direct-mode samples, so the audit also reported a load-filtered view. Orca's results and the filtered views still passed.

Two regression tests cover the new behavior. One checked that the deadline followed the last character and landed on an instant where the flush succeeds. The other sent a burst through the startup loop with a dummy frame requester, then asserted that the text flushed without a frame request delivering the wake-up. The second test fails if the flush comes to depend on frame scheduling again. The focused paste/startup test run at the time passed 63 tests.

Several questions remain open. The first composer still arrived later than upstream's, and mod preparation still finished after the prompt became typeable. I did not remeasure streaming latency, and the final performance runs came before later review fixes and the frame-pacing integration done at merge time. Historical idle RSS for OMD+ was about 313 MiB against upstream's 110 MiB; figures for today's V8 implementation and its memory footprint belong to a different comparison.

The probe sent its input as one burst, so these results don't show an equivalent improvement for isolated human keystrokes. The timestamps came from application output and don't include display scanout.
