initial commit for alpha release and testing on MacOS
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# Fossmark
|
||||
|
||||
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.
|
||||
|
||||
## What it measures
|
||||
|
||||
Nine workloads, each a tight assembly kernel:
|
||||
|
||||
| # | 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) |
|
||||
|
||||
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.
|
||||
|
||||
## Scoring
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
```sh
|
||||
make # builds dist/fossmark-<os>-<arch> 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
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```sh
|
||||
cc -O2 src/main.c src/fossmark.S -o dist/fossmark-linux-arm64 -lm
|
||||
```
|
||||
|
||||
On macOS the same command produces a native binary (name it
|
||||
`fossmark-macos-arm64`); on Windows use `clang` from the LLVM/MSVC toolchain.
|
||||
|
||||
## Testing
|
||||
|
||||
`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
|
||||
make test
|
||||
```
|
||||
Reference in New Issue
Block a user