Skip to content

async: 16,000 connections at 5ms, down from 64,000 at 15ms - #1342

Closed
MDA2AV wants to merge 3 commits into
feat/ioxide-async-delayfrom
profile/async-16k-5ms
Closed

async: 16,000 connections at 5ms, down from 64,000 at 15ms#1342
MDA2AV wants to merge 3 commits into
feat/ioxide-async-delayfrom
profile/async-16k-5ms

Conversation

@MDA2AV

@MDA2AV MDA2AV commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Experiment, stacked on #1341 so the diff is only the profile change.

At 64,000 connections the profile turned out to be measuring mostly the read/write path rather than the wait: instrumenting the timer against its own deadline showed it completing 3.2ms late against 23ms of end-to-end overhead, so the delay is roughly 13% of what the number reflects.

This moves the load: 16,000 connections, 5ms wait. Ceiling goes 4.27M -> 3.20M (connections over delay), against a best-so-far of 2.24M, so there is headroom but less than before and the docs now say so instead of claiming the ceiling does not bind.

Touches profiles.sh, the gcannon raw template, the CATALOG row, both profile docs and the README table. Generator runs clean, badge parity holds at 579 ranks.

Existing async-64000 results are orphaned and the column is empty until a re-run. Accepted while experimenting.

Experiment. At 64,000 connections the profile has been reporting a gap between
entries that does not survive inspection: instrumenting the wait shows the timer
completing 3.2ms late against 23ms of end-to-end overhead, so the delay is about
13% of what is being measured and the rest is the read and write path. Fewer
connections and a shorter wait puts the load somewhere different, and whether
the ordering between entries holds is the thing worth knowing.

The ceiling is connections over delay, so this moves it from 4.27M to 3.20M. The
best entry so far is 2.24M, which leaves headroom but less than before; the docs
now say so rather than claiming the ceiling does not bind.

Existing results are all at async-64000 and are orphaned by this, so the column
is empty until the profile is re-run. That is accepted for now.

Generator runs clean and badge parity holds at 579 ranks.
The async column drops out until the profile is re-run, since every existing
result is at async-64000.
@MDA2AV

MDA2AV commented Aug 27, 2026

Copy link
Copy Markdown
Owner Author

/benchmark-multiple -f ioxide,tokio -t async

Profile is now 16,000 connections at 5ms (ceiling 3.20M, down from 4.27M).

Comparing the two entries under the new load. At 64,000/15ms it was ioxide 1,568,519 @ 2802% against tokio 2,263,962 @ 3970% - near-identical efficiency per unit CPU, with ioxide simply not using the box. If the gap closes here, it was connection scaling; if it holds, it is throughput.

Not saving, still experimenting.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Benchmark request received. A collaborator will review and approve the run.

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

Frameworks: 2 | Test: async

ioxide

Test Conn RPS CPU Mem Δ RPS Δ Mem
async 16000 1,842,424 3434.9% 822MiB NEW NEW

tokio

Test Conn RPS CPU Mem Δ RPS Δ Mem
async 16000 2,054,680 3319.6% 672MiB NEW NEW

Experiment on the experiment branch; #1341 stays on the tick.

The ring was measured once, at 64,000 connections and 15ms, where it bought 5%
throughput for 51% more CPU and lost. The profile has changed underneath that
result: 16,000 connections at 5ms is a third of the connections and three times
the request rate, and the two mechanisms scale with different things.

The ring costs a timerfd_settime syscall plus an SQE and a CQE per request, so
its cost tracks the request rate and this profile raises that. The tick costs
one post per reactor per tick regardless of the rate, so its cost is unchanged
at 64 reactors while it now amortises over fewer connections per reactor: 250
here against 1,000 before, which is 250 timers per drain becoming about 60.

Both effects point the same way, against the ring, so this is a check rather
than a hope. /delay/5 answers in 5.22-5.28ms on this path.
@MDA2AV

MDA2AV commented Aug 27, 2026

Copy link
Copy Markdown
Owner Author

/benchmark-multiple -f ioxide,tokio -t async

Same profile, ioxide now on the ring timer (timerfd per request submitted to the reactor ring) instead of the tick. tokio is unchanged and is there as the control.

Tick baseline just measured on this profile: ioxide 1,842,424 @ 3434.9%, tokio 2,054,680 @ 3319.6%.

At 64k/15ms the ring lost, 5% more throughput for 51% more CPU. Worth re-reading here because the profile changed underneath that result: 3x the request rate makes the ring's per-request syscall cost worse, while a quarter of the connections per reactor makes the tick's batching worse. Both point against the ring, so this is a check rather than a hope.

Not saving.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Benchmark request received. A collaborator will review and approve the run.

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

Frameworks: 2 | Test: async

ioxide

Test Conn RPS CPU Mem Δ RPS Δ Mem
async 16000 2,462,698 5831.1% 1.1GiB NEW NEW

tokio

Test Conn RPS CPU Mem Δ RPS Δ Mem
async 16000 2,055,694 3346.0% 673MiB NEW NEW

@MDA2AV

MDA2AV commented Aug 27, 2026

Copy link
Copy Markdown
Owner Author

Folded into #1341 — everything is in one PR now.

The two profile commits (`b3b554a4`, `ff6570b2`) were cherry-picked across. The third commit here, the ring-timer flip, was dropped as redundant: #1341 already defaults to the ring on its own measurement, and it now also carries the deadline guard that this branch was missing.

@MDA2AV MDA2AV closed this Aug 27, 2026
@MDA2AV
MDA2AV deleted the profile/async-16k-5ms branch August 27, 2026 00:17
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