Skip to content

osbench: a futex round trip is one that slept, waits are judged slack-free, and an HPET boot is no run - #1230

Draft
Japabu wants to merge 3 commits into
mainfrom
benchrows
Draft

Japabu wants to merge 3 commits into
mainfrom
benchrows

Conversation

@Japabu

@Japabu Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

Test and tooling code only: tests/guest/osbench and its compare. No product code and no manual page: no requirement or architectural decision changes.

The scoreboard of 2026-11-11 (#1217) and its review found places where a table could say what the runs did not. This PR fixes the ones in the tooling. A stacked draft (benchrows-launch) adds proc.launch.first_output and records the rows still owed.

What changes

  • A futex round trip across cores is one that slept. pingpong.futex.cross_core read 250 to 257 ns in three of the T14's four boots and 4.6 µs in the fourth. A side that finds its word already changed never sleeps, and across two cores a whole round can pass that way. Each side now keeps what its wake found: the count that FUTEX_WAKE and SYS_FUTEX_WAKE both return. The cross-core row keeps the rounds in which both wakes found their waiter queued in the kernel. The other rounds are pingpong.futex.cross_core.unparked, and a run in which none parked reports the row unmeasured. Both futex rows say how many such wakes a kept round had, of two (parks_per_round). The split is osbench_rules::parked, tested on the host.
  • Waits are judged slack-free. Linux pads an ordinary thread's sleep by its 50 µs timer slack and ToyOS pads none. osbench-compare now judges a wait at Linux's .slack1ns row, against ToyOS's row of the unsuffixed name. The unsuffixed row gets no verdict, and its line says where it is judged.
  • An HPET boot is no run. On Linux, osbench exits 3 before any group when the clocksource is not the one its vDSO reads (tsc on x86-64). The compare holds clocksource among a system's setup and refuses runs that differ. PER_RUN is deleted: it stated the clocksource run by run and never refused a run.
  • Verdicts where a direction exists. ps, ns/MiB, kops/s and chunks/s are times and rates. The six fair.* share rows still get no verdict, since a share is neither better higher nor better lower. The new fair.<grouping>.off_fair is how far the worst-served job's share lay from its max-min fair share at equal weights (osbench_rules::fair), lower better.
  • Linux's nvme.read.* in the run the compare reads. osbench all starts nvme through sudo -n where that asks no password. Otherwise it starts nvme as itself, and the rows say they were refused, as before.

Decisions a reviewer should weigh

  • The same-CPU futex row keeps every round. On one CPU a round cannot pass unless each side leaves the CPU, by sleeping or by being preempted at the wake. There, "both wakes found a waiter" would split rounds that all paid two switches. The row reports parks_per_round and is otherwise unchanged.
  • "Parked" is what the wake's count says: the waiter was queued in the kernel when the wake came. A waiter queued and woken before it left its CPU counts as parked on both systems alike. ToyOS's SYS_FUTEX_WAIT returns 0 both when it was woken and when it never blocked, so the wait's own return value cannot tell.
  • The clocksource refusal has no override. The README used to say that an HPET boot is Linux as booted and is compared together with TSC boots. That is how a median of five came to be an HPET run's. A bench whose Linux boots onto the HPET is booted again. On the AMD bench that was 17 boots of 30 (README, "The clock"), so its Linux runs will cost extra reboots.
  • off_fair's entitlement is max-min fairness between the grouping's units: threads under shared, the three workloads under apart and session. Under a per-workload grouping that did not take effect (1xcgroup) the row is unmeasured. Shares are counted in chunks, and starting a helper costs chunks, so the row has a floor above zero on any system. On the scoreboard's T14 figures it would have read about 8 for ToyOS's apart and 15 for Linux's. These figures were computed by hand from the committed share rows, not measured.

Checks

Status: draft. The host suite and the metal boot are pending; nothing below is claimed beyond what ran.

  • Guest row osbench_capability_groups_exit_0 (guest runner, x86-64 q35) at e9f6d3c8b: green, run 43022944399. It runs identity, fair (with the new off_fair rows) and energy on the benchcase image, each to exit 0. The later commit 0876d634e changes only a message in code compiled for Linux and the README, so the result still applies.
  • Negative controls on the host tests, each applied to a copy of osbench-rules and osbench-compare at 54cfdfd2a (this branch plus the stacked commit, which touches neither crate), built, run and restored: a round counted parked when either wake found a waiter; the ponger's parks not counted; max-min without the demand cap; the padded row judged; clocksource out of SETUP. Each built and turned its test red (exit 101). Unmutated: 35 and 18 tests pass.
  • Linux build cargo +nightly-2026-07-22 build -p osbench --target x86_64-unknown-linux-musl --profile toyos at 0876d634e: exit 0.
  • Host suite cargo run -- --ci host: pending, at the stacked head 54cfdfd2a, which contains this branch whole. An earlier run at e9f6d3c8b ended when this host's disk filled and is not counted.
  • Metal and Linux bench runs: staged at the stacked head and requested; pending.

Not verified

  • The Linux half has not run. It compiles for x86_64-unknown-linux-musl (built) and aarch64-unknown-linux-musl (clippy). No Linux machine was available to this branch. The clocksource refusal, the sudo -n start and the Linux futex counts are first exercised by the bench runs requested for the stacked branch.
  • No metal boot yet. No guest row runs pingpong.*, because the suite's sched group is metal-only. The new futex fields are first read on the staged boot.

🤖 Generated with Claude Code

Japabu and others added 2 commits November 11, 2026 14:43
…-free, and an HPET boot is no run

The scoreboard of 2026-11-11 (#1217) and its review found four ways a
table could say what the runs did not.

pingpong.futex.cross_core read 250 to 257 ns in three of the T14's four
boots and 4.6 us in the fourth: a side that finds its word already changed
never sleeps, and across two cores a whole round can pass that way. Each
side now keeps what its wake found (FUTEX_WAKE's and SYS_FUTEX_WAKE's
count). The cross-core row is the rounds in which both wakes found their
waiter queued; the others are `<row>.unparked`, and a run in which none
parked is `unmeasured`. On one CPU no round passes without both sides
leaving it, so that row keeps every round. Both say the parks a kept round
had, of two (`parks_per_round`). The split is osbench_rules::parked, tested
on the host.

Linux pads an ordinary thread's sleep by its timer slack and ToyOS pads
none. osbench-compare now judges a wait at Linux's `.slack1ns` row, against
ToyOS's row of the unsuffixed name, and gives the unsuffixed row no verdict.

Three of the AMD bench's five Linux runs kept time on the HPET, so the
median of five was an HPET run's. osbench on Linux now exits 3 before any
group where the clocksource is not the one its vDSO reads, and the compare
holds the clocksource among a system's setup, refusing runs that differ.
PER_RUN, which stated it run by run, goes.

Rows that had no verdict get one where a direction exists: ps, ns/MiB,
kops/s and chunks/s are times and rates, and `fair.<grouping>.off_fair` is
how far the worst-served job's share lay from its max-min fair share
(osbench_rules::fair), which the six share rows could not say.

`osbench all` on Linux starts `nvme` through `sudo -n` where that asks no
password, so the raw namespace is read in the run the compare reads.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The message carried a run of spaces from a broken line, and the README's
compare item one line past its width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… parks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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