Repository navigation
Conversation
e61a9e0 to
951c7d1
Compare
951c7d1 to
6df689a
Compare
6df689a to
b972cf0
Compare
b972cf0 to
b232ee4
Compare
b232ee4 to
07e3e16
Compare
Review of #983 at 07e3e16, round 1ArchitectureThe kernel and the SDK are unchanged. osbench is one std program under The capability grant is justified:
The separate boot is justified by its quiet image. The row holds only that each group ran to its end, which matches its job: it delivers evidence and asserts no bound. Size: +2051 lines. About 1900 are osbench, plus a 62-line image, a 24-line row and a 26-line issue. There are no tests. I am not asking for host tests: the crate is guest-built, so I checked these and found them sound:
Evidence:
FindingsBLOCKER 1. The Linux half is compiled by no gate.
BLOCKER 2.
BLOCKER 3. CPUID intrinsics and a
BLOCKER 4. A garbled doc comment.
BLOCKER 5. A stale module contract.
SUGGESTION 6. SUGGESTION 7. SUGGESTION 8. SUGGESTION 9. SUGGESTION 10. SUGGESTION 11. In PR description (for the orchestrator)
SEND BACK |
07e3e16 to
808fd33
Compare
|
Review r1 answered at Blockers
Suggestions
The PR body's Risks are corrected: the 44.9 s list end, the scope and placement results as measured, and the |
808fd33 to
fea120d
Compare
The owner asked for scheduler and system numbers against Linux. osbench is one std program, built unchanged for x86_64-unknown-toyos and, statically, for x86_64-unknown-linux-musl, so a bench runs the same workloads under both systems: - system: null syscall, thread spawn+join, process spawn+exit+wait, pipe throughput at 64 B and 64 KiB writes, anonymous memory map/touch/unmap, 4 KiB map+unmap, and one thread's memory bandwidth. - sched: a cyclictest-style periodic wake idle and under load, a wake onto a busy CPU beside idle ones, a futex wake storm's call and drain, and pipe and futex ping-pong on one CPU and across cores. - fair: a single-threaded build, a threaded job and a helper-spawning job on every CPU, each job's share of the work over five windows, once with all three in this program's own group and once with a group per job. ToyOS's groups are jobs: `onejob`, and `perjob` with a job each from `SysCap::new_job`. Linux runs `flat`, a session per job, and a `systemd-run --user` scope per job, and says which fair unit applied. Each benchmark has a fixed warm-up and repetition count and prints one `osbench v=1 os=... key=value` line with its distribution; Linux also prints the conditions it read (kernel, governor, EPP, SMT, power, THP, mitigations, load). The code is shared except where the facility differs: the futex, the clock every process shares, anonymous mappings, the CPU a thread is on, and placement, which Linux does by affinity and ToyOS, having none, by staging spawned threads and reporting whether they stayed. It lives in tests/guest/ because that is where a program only the test estate runs sits, and it builds as a workspace member so the image's program row builds it. tests/images/benchcase.toml boots it with only the servers a job list needs, its runner holding `jobs` and `dup`, and the `osbench` metal row runs the three groups on the T14 and the AMD bench. The row is evidence and asserts no number: it holds only that each group ran to its end. File I/O is left out: ToyOS stores on a USB stick and Linux on NVMe. No image builds the Linux half, so a clippy shape checks `-p osbench` for x86_64-unknown-linux-musl, and the host job installs that target with the bare ones. ToyOS has no call naming a thread's CPU, so osbench reads it with CPUID in src/arch/, and refuses AArch64 by name: issues/osbench-builds-for-no-aarch64-toyos.md records the gap. .cargo/config.toml links the musl target with the toolchain's rust-lld, so the Linux binary needs no C toolchain, and keeps the frame pointers the ToyOS targets keep. issues/osbench-times-cpus-leaving-idle-and-not-their-settled-clock.md records that each benchmark reads CPUs leaving idle, not their settled clock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fea120d to
37d913b
Compare
Review of #983 at 37d913b, round 2AssessmentThe r1 head was Blockers from r1:
The suggestions that were taken are implemented correctly:
FindingsBLOCKER 1.
PR description (for the orchestrator)
SEND BACK |
The owner asked for a scheduler benchmark and other numbers against Linux. This adds
osbench, one std program built unchanged for ToyOS and, statically, for Linux, so each bench runs the same workloads under both of its systems. It builds on #987 (landed), which fixed two ToyOS std defects the benchmark ran into, and on #979's jobs.Measured. Both benches have run both sides at
951c7d1: Linux binary e4b84c18, ToyOS main, and a #979 probe that never lands. The comparison is published at https://claude.ai/artifact/C155GeweFbe6CAsqRWNQ7K. A separate round-2 PR stacks on this branch.What it measures
Every benchmark has a fixed warm-up and repetition count, stated in its doc comment, and prints one line,
osbench v=1 os=<os> bench=<name> unit=<u> n=<samples> min= p50= p90= p99= p999= max= mean= [key=value...]. A percentile the sample count cannot resolve prints-.syscall.null(getpid),thread.spawn_join,process.spawn_wait(this program asosbench nop),pipe.throughput.{64,65536},mem.anon.{map,touch,unmap,total}over 32 MiB,mem.map_unmap_4k, andmem.bw.{read,write,copy,memcpy}over 64 MiB, all on one thread.wake.periodic.{idle,loaded}(cyclictest-style, 500 µs, 5,000 samples, p50 to p99.9 and max),wake.onto_busy(a wake whose thread last ran on a CPU a spinner holds, with idle CPUs beside it),wake.storm.{call,drain}.w{1,4,16,64}, andpingpong.{pipe,futex}.{same_cpu,cross_core}.buildis one process with one thread,threadedis one process with a thread per CPU, andhelperskeeps one 100 ms helper process per CPU alive. The result is each job's share of the work over five 2 s windows. Linux runs it three ways:flat(one session and cgroup, so the unit is the thread),session(setsid per job, so the unit is the autogroup where autogroup applies) andscope(systemd-run --user --scope -p CPUWeight=100per job). Acond fair.<grouping>.unitsline says which unit actually applied, for example3xcgroupor1xcgroup. Ifsystemd-runis missing or refused, the scope run printsrefused=...rather than a number. ToyOS runs it two ways, now that Scheduler stage 3: job fairness, with per-CPU two-level EEVDF and creation authority #979's jobs have landed:onejob(all three in osbench's own job, the counterpart offlat) andperjob(each a job of its own fromSysCap::new_job, the counterpart ofscope). A per-job run that cannot make a job is refused, never run as one job.Linux also prints the conditions it reads, and changes none of them: kernel, distro, SMT, cpufreq driver, governor, EPP, max frequency, turbo/boost, AC and battery, THP, autogroup, timer slack, every
vulnerabilities/*mitigation, load average and a 1 s busy fraction. No host name, path or identifier is printed. A cgroup path names the user, so the scope check compares paths and never prints them. Both systems printcpu.chunk: the same instructions on both, timed as a 2 ms single-threaded burst. It shows the clock a CPU leaving idle runs at, not the clock it settles at. An earlierest_mhzfield assumed 4 cycles per iteration and read 9.5 GHz on the T14, so the compiler had shortened the chain; the field is gone.File I/O is not measured, because ToyOS stores on the boot USB stick and Linux on NVMe.
Decisions
tests/guest/osbench. A program only the test estate runs sits intests/guest/(build::PROGRAM_ROOTS), and as a workspace member the image's[programs.osbench]row builds it. The ToyOS side is a metal row, as the repository's other measurements are. There is no benchmarks directory to follow.src/sys/{linux,toyos}.rshold the futex, the clock every process shares (needed to align the fairness windows across processes; std'sInstantcannot cross a process), anonymous mappings, the CPU a thread is on and CPU topology. Linux usessched_getcpuand sysfs. ToyOS has no such call, so it uses CPUID's APIC ID. Placement (src/place.rs) pins with affinity on Linux. ToyOS has no affinity, so it starts candidate threads until one lands on the wanted CPU, and the results report whether the threads stayed there (placed_held,ran_on_busy,skipped_spinner_away).scheduler::futex_wait); a Linux one returns on any wake. The storm bumps a generation, so both systems end the wait the same way.nightly-2026-07-22, the upstream compiler nearest ToyOS's 1.99 fork, which the build already installs as its stage-0 cargo. It uses the sametoyosprofile and forced frame pointers like the ToyOS targets..cargo/config.tomllinks the musl target with the toolchain'srust-lld, so no C toolchain or Linux toolchain is involved.osbench(ontests/images/benchcase.toml) holds only that each of the three groups exited 0 and printedgroup=<g> done. A group exits non-zero only if one of its benchmarks could not measure. The image starts only what a job list needs to reach its log on the boot stick: logkeeper, diskserver, fileserver, usbd and the runner, with annvmerow that only QEMU's controller matches. The runner's capability carries onlyjobsanddup:jobsfor the per-job run, anddupto hand it down. Supported machines: the T14 and the AMD bench, as stated at the row.threadedwould be one thread on ToyOS.Checks
First measured at
951c7d1, on maindb0cf636d; ToyOS was rerun on both benches at07e3e16(PASS), with theonejob/perjobruns that replacefair.process. The PR head is37d913b, on main2661b9e; it differs from808fd33, where the host checks ran, only in doc comments. Since07e3e16it adds review r1's fixes:-p osbenchforx86_64-unknown-linux-musl, the Linux half no image builds (it found an unread pipe read, now handled);wake.onto_busyrecording where each partner parked;src/arch/, withissues/osbench-builds-for-no-aarch64-toyos.md;Logs and their exact commands for the measured head: https://github.com/ToyOSOrg/evidence/tree/c5502d37c215a8c67b4283fb113a61b913dad231/osbench (its
READMEsays which revision each file is at).cargo run -- --ci host69eafdb(the tree of07e3e16) and at808fd33cargo run -- --build-only --boot-config tests/images/benchcase.tomlcargo +nightly-2026-07-22 build -p osbench --target x86_64-unknown-linux-musl --profile toyoscargo test --test toyos-build -- --metal --metal-readback <dir> osbenchcpus=1and thethread::spawnOOM; after: all three groups exit 0osbench allas a native arm64 musl build in a Linux containerfair.scoperefused (nosystemd-runin the container)The QEMU and container runs show only that the code paths run end to end. Their numbers mean nothing and are not in any comparison. They did catch the std defects, a bug in reading the process's CPU mask from a pinned thread, and the futex semantic difference, all fixed here. The Linux pinning, sysfs topology,
setsidand cgroup paths ran on a real Linux kernel in the container.systemd-runand the x86 Linux binary run only on the benches.Running it
ToyOS (the orchestrator runs metal):
The numbers are the
osbench v=1 os=toyoslines of the readback's log.Linux (built here, run over ssh, nothing left behind):
alltakes about a minute. Nothing it starts outlives it: the load child stops itself after 30 s, and the fairness jobs end with their last window.Report template
Follow-up: the clock it measures at
issues/osbench-times-cpus-leaving-idle-and-not-their-settled-clock.md, filed here. Each benchmark runs straight after the previous one, andcpu.chunkis a 2 ms burst at the start of each group, so both read CPUs that are leaving idle. On the T14 the same chunk took 10.5 µs on Linux and 3.3 µs on ToyOS, which idles inhlt. Under all-CPU load both run one clock (2.389 M against 2.371 M chunks/s). Dividing bycpu.chunkis therefore no normalisation: the equal memory bandwidths come out a quarter apart. The fix is a steady-state warm-up before each benchmark, with the chunk time printed beside it. It changes the protocol, so both sides would have to be rerun; that is round 2's to decide, not this PR's.Risks
fair.scopedepends onsystemd-run --userreaching the user manager over ssh. If it cannot, the run is reported as refused, not faked; on both benches it reached it (3xcgroup).pingpong.*.same_cpudoes not stay on one CPU: it readplaced_held=0/41on the T14. Without affinity it is a cross-CPU number, andplaced_heldsays so.wake.onto_busyon ToyOS now skips, and counts inskipped_parked_elsewhere, every round whose partner did not park on the busy CPU, and reports where the waker ended instead of refusing. The runs at07e3e16predate this, so their ToyOSran_on_busy=0does not by itself say that ToyOS moved the wake. osbench round 2: files, IPC, networking, processes, memory, time, the tail, a workload and boot #989's boots run this code.07e3e16the T14's last job ended at 44.9 s, and the AMD bench's Scheduler stage 3: job fairness, with per-CPU two-level EEVDF and creation authority #979 probe at 40.1 s, now that fairness runs two groupings of about 12 s each.unitscheck reads the cgroup hierarchy the process can see. Inside a container's cgroup namespace it misreads (the arm64 container reported3xautogroupforsession, although the container's own cgroup governs). Over ssh on a bench it sees the whole hierarchy.🤖 Generated with Claude Code