Skip to content

perf(unix): reduce allocation in post-I/O yields - #605

Merged
Guest0x0 merged 2 commits into
moonbitlang:mainfrom
mizchi:perf/unix-post-io-yield
Sep 14, 2026
Merged

Guest0x0 merged 2 commits into
moonbitlang:mainfrom
mizchi:perf/unix-post-io-yield

Conversation

@mizchi

@mizchi mizchi commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Successful Unix reads and writes yield through protect_from_cancel(() => pause()) before returning their result. This adds an internal pause_after_io helper that performs the same protected yield without the generic higher-order cancellation wrapper, reducing allocation on this path.

The helper preserves every existing yield, queues the coroutine in the same order, and saves/restores its previous cancellation shield. Pending cancellation is delivered at the next cancellation point after returning the completed I/O result. The paths that wait for I/O readiness are unchanged. Four regression tests cover round-robin scheduling, cancellation pending before the yield, cancellation during suspension, and an enclosing cancellation shield.

I collected these measurements while benchmarking Mars, my web server implementation. The detailed results, methodology, limitations, reproduction commands, and raw data are in Mars. This standalone comparison uses async main at 43e41261f99261f4030efbed1970388930229baa; it does not include #603 or #604.

Metric Main This patch
Requested allocation bytes per post-I/O yield 404 296 (−26.7%)
Allocation requests per yield 14 10
Mars instructions per small-response request 93,775 92,067 (−1.8%)

These are native release measurements on macOS arm64. Separate binaries measure allocation and elapsed time; the yield-only microbenchmark's elapsed time is nearly unchanged. The real-server comparison uses six paired rounds with alternating order, a one-second warmup and five-second load, four wrk threads, and 64 connections. All six rounds reduce instructions/request by 1.65–2.20%. The allocation reduction is the primary motivation; these runs do not establish Linux performance or production throughput.

Local validation:

  • Native release: 116 HTTP/io/buffer/coroutine/event-loop tests and 119 public async/socket tests pass.
  • Native debug: 134 coroutine/event-loop/public async/socket tests pass; JS: all five coroutine tests pass.
  • moon check --deny-warn passes for native, JS, Wasm, and Wasm-GC; moon fmt and moon info are clean.
  • Mars's native root-package tests pass 92/92 against both variants; Mars's release checks pass, including 331 JS tests.

Linux native execution is covered by the upstream PR CI. This PR is based directly on main and is independent of #603 and #604.

@Guest0x0
Guest0x0 merged commit 8b6c8a5 into moonbitlang:main Sep 14, 2026
21 checks 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.

2 participants