Project notes
CPU Benchmark
CPU Benchmark is a browser-based tool that measures single-core and multi-core performance. It uses Web Workers for concurrency, SharedArrayBuffer for timing coordination between threads, and a small compute kernel written directly in WebAssembly bytecode rather than compiled from source.
What I Built
- A benchmark UI with worker count controls, workload presets, live progress, score cards, and an expandable Run Details panel.
- A compute kernel written as hand-assembled WebAssembly bytecode, generated byte by byte with no compiler toolchain.
- A Web Worker pipeline for single-core, fixed-work, and multi-core phases, with each worker returning its own compute duration and checksum.
- A code viewer and About page so the project can be understood at different depths: a quick demo for a first look, and full source and methodology for anyone who wants to dig further.
- A Node regression test suite that runs the real worker kernel, checks score rendering and observed spread, verifies cancellation behavior, and asserts that the embedded fallback worker matches
cpu-worker.js.
Why It Matters
- This isn't just a UI demo. The benchmark involves concurrency, browser APIs, WebAssembly byte assembly, a defined timing methodology, fallback behaviour, and regression tests.
- Together, these demonstrate systems-level thinking in the browser: workers, shared memory, synchronisation, browser throttling, measurement noise, and hardware limits.
- The scores are backed by measured worker compute time, verified checksums, per-round detail, and observed spread labels rather than a single unexplained number.
Benchmark Methodology
- Dedicated worker execution. CPU work runs inside Web Workers so the main page can manage UI state while worker threads do the benchmark work.
- Worker compute time is scored. Each worker measures its own compute duration after the start signal. This worker compute time is scored; wall time is tracked separately as an end-to-end detail.
- Warm-up is discarded. Each phase runs a discarded warm-up round before measured rounds, reducing distortion from engine tier-up and other first-run effects.
- Median-based results. The benchmark takes multiple measured rounds and reports the median, which is more stable than trusting whichever round happened to be fastest or slowest.
- Rotating phase order. Measured rounds rotate between Single/Fixed/Multi, Fixed/Multi/Single, and Multi/Single/Fixed. No phase is permanently favored by always running on the coolest or hottest CPU state.
- Short settling pauses. Brief pauses reduce scheduling carry-over between workloads and measured rounds.
- Synchronized starts when possible. If the page is cross-origin isolated, workers use
SharedArrayBufferandAtomicsas a start barrier. If that is unavailable, the benchmark falls back to a normalpostMessagestart path and reports that mode.
What The Scores Mean
- Single-Core. How fast one worker completes the selected workload; this is the baseline for the rest of the benchmark.
- Multi-Core. Total throughput when the selected worker count runs the workload in parallel.
- Scaling. Multi-core score divided by single-core score. This answers: how much faster did the workload get when more workers were added?
- Fixed Speedup. How much faster the same total amount of work finishes when split across multiple workers instead of running on one worker.
- Spread labels. The percentage beside each score is the maximum observed deviation from its median. Scaling and Fixed Speedup are calculated for each measured cycle first, so their spread comes from real ratio samples instead of an assumed variance formula.
- Run confidence and drift. Confidence is graded from observed spread, foreground/background state, and the change between the first and last single-core rounds. Positive drift means the later round was slower, which can point to heat, power throttling, or competing work.
Accuracy Safeguards
- Verified checksums. Every worker must return the known warm-up checksum, and the verification job must return
0xe31d283a. A mismatch stops the benchmark before scores are produced. - Background-tab detection. If the tab is backgrounded during a run, the completion status visibly recommends rerunning because browsers may reduce scheduling priority for background pages.
- Worker source reporting. The Run Details panel reports whether the benchmark used the fetched
cpu-worker.jssource or the embedded fallback copy. - Fallback consistency test. The test suite compares the embedded worker source inside the HTML against
cpu-worker.jsso the two copies do not silently drift apart. - Hardware clamp visibility. Worker count is capped at 32, and the UI notes when the browser reports more logical threads than the benchmark will use.
- Timeout recovery. Worker startup, synchronized starts, and long-running rounds fail with a clear message instead of leaving the page stuck.
Known Limits
- Browser benchmarks are approximate. Browser scheduling, thermal state, power mode, other apps, and OS behavior can all affect results.
hardwareConcurrencymeans logical threads. The browser reports the CPU threads Windows/macOS can schedule, not necessarily the number of physical cores. A CPU with six hyper-threaded performance cores and eight single-threaded efficient cores, for example, has 14 physical cores but reports 20 logical threads.- Threads are not identical cores. Hyper-threaded sibling threads share parts of one physical core, and hybrid CPUs combine different kinds of cores. Scaling can flatten before the reported worker count without indicating a benchmark problem.
- Cross-origin isolation changes start quality. The deployed site sends the COOP and COEP headers required for the Atomics barrier. If a copied deployment removes those headers, the benchmark falls back to
postMessageand reports that mode. - Short runs are noisier. Quick workloads finish faster, which makes browser wake-up and message timing a larger part of the overall measurement.
- Scores are best compared on the same machine and browser. Different browsers may compile WebAssembly and schedule workers differently.
What The Browser Cannot Control
- CPU temperature and boost behaviour. A cool CPU may boost higher than one that has been busy for a while.
- Power mode and battery state. Windows, macOS, and laptop firmware can limit performance to save power or reduce heat.
- Other work on the computer. Background apps, updates, antivirus, browser tabs, and operating-system scheduling can compete for CPU time.
- Browser implementation. Different browsers can compile WebAssembly and schedule Web Workers differently, even on the same computer.
- Hardware outside the CPU. Memory pressure, cooling, firmware, and operating-system configuration can affect a result without changing the processor itself.
- Fair comparison. Results are most useful when compared on the same machine, browser, power mode, and workload after closing unnecessary background work.
Notable Implementation Details
- No WASM compiler. The kernel's bytes, including LEB128/SLEB128 integer encoding, WASM section layout, and raw opcodes, are built by hand in
cpu-worker.jsand compiled at runtime withWebAssembly.compile. - Fair multi-core timing. Workers can block on
Atomics.wait()until a shared barrier releases, so a multi-core round starts much more consistently than it would if each worker began whenever its message arrived. - Observed spread, not claimed statistical variance. The labels describe movement in the measured samples and do not pretend to be laboratory confidence intervals.
- Test-hardened, not just tested. Checksum verification, phase rotation, spread rendering, barrier cancellation, Stop/Run lifecycle, and worker fallback drift are protected by regression checks.
Files
cpu-benchmark.html
Page structure, controls, result panels, and the embedded fallback copy of the worker source.
cpu-benchmark.js
Orchestrates workers, rotates warm-up and measured phases, verifies checksums, computes scores and observed spread, and renders results.
cpu-worker.js
Hand-assembles and runs the WebAssembly benchmark kernel inside each worker thread.
cpu-benchmark-test.js
Node tests for the scoring math, the score-rendering path, and the worker-loading fallback logic.