From 5c3903509bfc170a75a7b1cbb1a16606af6b8a5d Mon Sep 17 00:00:00 2001 From: Owen Rummage Date: Fri, 17 Jul 2026 14:03:30 -0500 Subject: [PATCH] fix readme, rc1 ready --- README.md | 226 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 128 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index b0e0c20..bad4d76 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,154 @@ -# Fossmark +# fossbench -A single-threaded CPU benchmark for ARM64 (AArch64), with the numeric kernels -hand-written in assembly and a small portable C driver to run and score them. +fossbench is an open-source CPU benchmark with nine assembly workloads and a +small C driver. It measures each workload twice: once on a single core and once +across every available core. The final report includes separate single-core and +multicore scores. -## What it measures +The repository currently builds an executable named `fossmark` for ARM64 and +x86-64. The C driver handles timing, memory, threads, output, and scoring. The +performance-sensitive kernels live in architecture-specific assembly files. -Nine workloads, each a tight assembly kernel: +## Workloads -| # | Test | What it exercises | -|---|-------------------------|--------------------------------------------------------------| -| 1 | Integer Math | 64-bit ALU: `madd`, `umulh`/`smulh`, `udiv`/`sdiv`, bit ops | -| 2 | Floating Point Math | scalar double: `fmadd`, `fdiv`, `fsqrt` | -| 3 | Prime Numbers | sieve of Eratosthenes to 2,000,000 (strided memory + ALU) | -| 4 | Extended Instructions | NEON/ASIMD: 128-bit integer, widening, table, float vectors | -| 5 | Compression | LZ77 match-finder over a 4 MiB corpus (branchy, cache probe) | -| 6 | Encryption | ChaCha20, 20 rounds, NEON, over 1 MiB | -| 7 | Physics | 512-body direct-summation gravity, double precision | -| 8 | Sorting | in-place heapsort of 1M `uint32` (branch + cache stress) | -| 9 | Single-Threaded | dependent-load pointer chase over 16 MiB (memory latency) | +| Test | What it measures | +|---|---| +| Integer math | 64-bit multiplication, division, shifts, and bit operations | +| Floating point math | Scalar double-precision multiplication, addition, division, and square roots | +| Prime numbers | A sieve of Eratosthenes up to 2,000,000 | +| Extended instructions | 128-bit SIMD integer and floating point work using NEON or SSE2 | +| Compression | An LZ77 match finder over a 4 MiB generated corpus | +| Encryption | ChaCha20 with 20 rounds over a 1 MiB buffer | +| Physics | Direct-sum gravity for 512 bodies | +| Sorting | In-place heapsort of one million 32-bit integers | +| Memory latency | Dependent pointer chasing through a private 16 MiB cycle | -Each test auto-calibrates its iteration count until it runs long enough to be -timed reliably, then reports the best of several runs (the run least disturbed -by the OS scheduler). Every kernel returns a checksum that the driver verifies -across runs, so a miscompiled or non-deterministic kernel is caught rather than -silently mis-scored. +The benchmark increases each test's iteration count until one run takes at +least two seconds. It then keeps the fastest of three runs. Each kernel returns +a checksum, and fossbench stops if repeated runs produce different results. -## Scoring +During the multicore pass, every thread gets its own mutable workspace. This +keeps the kernels free of data races and prevents shared scratch buffers from +distorting the result. -Each test's raw rate is normalised against a **reference machine** into a -unitless score, and the overall is a **weighted geometric mean** of those -scores: +## Build and run -``` -S_i = TARGET * (rate_i / REF_i) (per-test score) -Overall = TARGET * exp( Σ w_i·ln(rate_i/REF_i) / Σ w_i ) (weighted geo. mean) -``` - -The reference rates are the tuning machine's own rates, and `TARGET` is 20000, -so that machine scores ~20000 on every test and overall. Scaling is linear in -performance: a machine half as fast scores ~10000, one 10× slower ~2000, and a -future machine twice as fast ~40000 — so there is unbounded room both below and -above the reference. - -The weights reflect each test's influence on **everyday, common-workload user -experience** — integer/general-purpose throughput and memory-latency-bound -responsiveness matter most; specialised floating-point and physics matter -least. This mirrors the weighted, integer-dominant approach of mainstream -suites such as Geekbench 6 (which splits integer/FP roughly 65/35 and combines -real-world workloads with a weighted mean). - -| Test | Weight | -|---|---:| -| Integer Math | 20% | -| Single-Threaded | 16% | -| Compression | 14% | -| Sorting | 12% | -| Extended Instructions | 11% | -| Floating Point | 9% | -| Encryption | 8% | -| Prime Numbers | 6% | -| Physics | 4% | - -Everything above is configurable via `#define`s at the top of `src/main.c`: -`FM_TARGET_SCORE`, the nine `FM_REF_*` reference rates, and the nine -`FM_WEIGHT_*` weights. Weights are relative — the code normalises by their sum, -so you can change one without rebalancing the rest. To re-baseline for a -different reference machine, set each `FM_REF_*` to that machine's measured -rate. - -Note: the pointer-chase (Single-Threaded) test measures raw memory latency and -is the noisiest to sample, so the overall typically varies ~1–2% run to run. - -## "Runs on all operating systems" - -The **assembly is** OS-independent: `src/fossmark.S` contains no system calls, -no libc calls, and no external relocations. Every routine is a pure function of -its arguments under the AAPCS64 calling convention, so the same source -assembles and runs correctly on Linux (ELF), macOS (Mach-O), Windows (COFF) and -the BSDs. It avoids `x18` (reserved on Darwin/Windows) and the `v8`–`v15` -callee-saved vector bank. - -A single *binary* that runs everywhere is not possible — Linux, macOS and -Windows use incompatible executable formats and system-call ABIs. So the -portable C driver (`src/main.c`) supplies the per-OS parts (timing, memory, -I/O), and you build one binary per platform. The Linux build is named -`fossmark-linux-arm64`. - -## Build +You need a C compiler, GNU Make, pthreads, and the system math library. + +```sh +make +make bench +``` + +`make` builds a binary for the host at +`dist/fossmark--`. `make bench` builds that binary and runs it. + +Other targets are available for explicit platforms and architectures: ```sh -make # builds dist/fossmark-- for the host make linux-arm64 make linux-amd64 make macos-arm64 make macos-amd64 -make bench # build and run the benchmark -make test # build and run the kernel correctness tests +make all ``` -Both macOS targets can be built on either Apple Silicon or Intel Macs; Apple -Clang selects the requested architecture with `-arch`. They produce -`dist/fossmark-macos-arm64` and `dist/fossmark-macos-amd64`, respectively. - -Or by hand: +`make all` builds both Linux targets. Cross-compilation requires a suitable +toolchain. Override the target compiler when its name differs from the default: ```sh -cc -O2 src/main.c src/fossmark.S -o dist/fossmark-linux-arm64 -lm +make linux-arm64 CC_ARM64=aarch64-linux-gnu-gcc +make linux-amd64 CC_AMD64=x86_64-linux-gnu-gcc ``` -On macOS the same command produces a native binary (name it -`fossmark-macos-arm64`); on Windows use `clang` from the LLVM/MSVC toolchain. +Apple Clang can build either macOS architecture with `-arch`. Windows timing +and allocation code exists in the driver, but the Makefile does not include a +Windows target and the x86-64 assembly currently follows the System V ABI. -## Testing +Run the benchmark with extra per-test details by passing `--verbose`: -`src/test_kernels.c` is a standalone harness that validates each kernel against -an independent reference or invariant — the sieve against a C reference sieve, -the NEON ChaCha20 against a scalar reference anchored to the RFC 8439 -known-answer vector, the sort against `qsort`, the N-body step against -conservation of momentum, and so on. It exits non-zero if any check fails. +```sh +./dist/fossmark-linux-amd64 --verbose +``` + +The exact filename depends on the host platform and architecture. + +## Scores + +Each workload receives a score relative to a reference rate: + +```text +test score = 10000 * measured rate / reference rate +``` + +The single-core and multicore totals are weighted geometric means of the nine +test scores. Both passes use the same reference rates and weights, so their +ratio gives a direct view of scaling across the machine's available cores. + +| Test | Weight | +|---|---:| +| Integer math | 20% | +| Memory latency | 16% | +| Compression | 14% | +| Sorting | 12% | +| Extended instructions | 11% | +| Floating point math | 9% | +| Encryption | 8% | +| Prime numbers | 6% | +| Physics | 4% | + +The reference rates, weights, target score, workload sizes, calibration floor, +and repeat count are compile-time constants in `src/main.c`. Changing them +creates a different benchmark profile, so scores from that build should not be +compared with scores from the default build. + +Memory latency is displayed as nanoseconds per access, but its score uses the +underlying pointer-chase throughput. Latency results are sensitive to memory +placement and operating-system activity, so some variation between runs is +normal. + +## Architecture support + +The assembly kernels use only baseline instructions for their architecture: + +* `src/fossmark.S` uses ARMv8-A and NEON under AAPCS64. +* `src/fossmark_x86_64.S` uses baseline x86-64 and SSE2 under the System V ABI. + +The kernel files contain no system calls or calls into the C library. The same +ARM64 source can be assembled for Linux, macOS, Windows, and BSD object formats. +The current x86-64 source supports Linux, macOS, and the BSDs that use the +System V calling convention. + +One binary cannot run on every supported target because operating systems and +architectures use different executable formats and instruction sets. Build a +separate binary for each operating system and architecture pair. + +## Tests + +The correctness suite checks all nine kernels against C reference +implementations, known answers, or invariants. Most checks also run concurrently +on every available core to catch shared-state and reentrancy bugs. ```sh make test ``` + +The suite covers the RFC 8439 ChaCha20 test vector, prime counts, sorting output, +physics momentum, pointer-chase behavior, and deterministic results. It exits +with a nonzero status if any check fails. + +## Source layout + +```text +src/main.c portable benchmark driver and scoring +src/fossmark.S ARM64 kernels +src/fossmark_x86_64.S x86-64 kernels +src/test_kernels.c correctness suite +Makefile native and cross-build targets +dist/ generated binaries +``` + +## License + +No license file is included in this repository yet. Add one before distributing +fossbench or accepting outside contributions as an open-source project.