Add authenticated uploads and rename fossmark to fossbench

Uploads can now be attributed to a fossbench.net profile: set
FOSSBENCH_TOKEN and the client sends it as a Bearer token, which the
server auto-approves and links to the account. Anonymous, pending-review
upload stays the default when no token is set. New --upload/--noupload
flags skip the interactive prompt for scripted runs, and the client
reports HTTP 401/422 distinctly from other failures. The token is never
printed or logged.

Also renames the project and its internal identifiers (FM_/fm_ macros
and symbols, source filenames, binary output names) from fossmark to
fossbench, matching the actual product name. The "fossmark_version"
field in the upload payload is left as-is, since it's the server API's
fixed contract field, not this client's own name.
This commit is contained in:
2026-07-18 06:02:24 -05:00
parent 357e6f0d6d
commit ad5791bbb7
10 changed files with 406 additions and 336 deletions
+9 -9
View File
@@ -107,8 +107,8 @@ jobs:
make "${{ matrix.target }}" \ make "${{ matrix.target }}" \
TLS_CFLAGS="-I$GITHUB_WORKSPACE/openssl-static/include" \ TLS_CFLAGS="-I$GITHUB_WORKSPACE/openssl-static/include" \
TLS_LDLIBS="$OPENSSL_LIBDIR/libssl.a $OPENSSL_LIBDIR/libcrypto.a $EXTRA_STATIC_LIBS" TLS_LDLIBS="$OPENSSL_LIBDIR/libssl.a $OPENSSL_LIBDIR/libcrypto.a $EXTRA_STATIC_LIBS"
file "dist/fossmark-${{ matrix.target }}" file "dist/fossbench-${{ matrix.target }}"
if ldd "dist/fossmark-${{ matrix.target }}" | grep -E 'lib(ssl|crypto)'; then if ldd "dist/fossbench-${{ matrix.target }}" | grep -E 'lib(ssl|crypto)'; then
echo 'Linux release uses dynamic OpenSSL' >&2 echo 'Linux release uses dynamic OpenSSL' >&2
exit 1 exit 1
fi fi
@@ -122,19 +122,19 @@ jobs:
make "${{ matrix.target }}" \ make "${{ matrix.target }}" \
TLS_CFLAGS="-I$GITHUB_WORKSPACE/openssl-static/include" \ TLS_CFLAGS="-I$GITHUB_WORKSPACE/openssl-static/include" \
TLS_LDLIBS="$OPENSSL_LIBDIR/libssl.a $OPENSSL_LIBDIR/libcrypto.a" TLS_LDLIBS="$OPENSSL_LIBDIR/libssl.a $OPENSSL_LIBDIR/libcrypto.a"
if otool -L "dist/fossmark-${{ matrix.target }}" | grep -E 'lib(ssl|crypto)'; then if otool -L "dist/fossbench-${{ matrix.target }}" | grep -E 'lib(ssl|crypto)'; then
echo 'macOS release uses dynamic OpenSSL' >&2 echo 'macOS release uses dynamic OpenSSL' >&2
exit 1 exit 1
fi fi
- name: Package artifact - name: Package artifact
run: tar -czf fossmark-${{ matrix.target }}.tar.gz -C dist fossmark-${{ matrix.target }} run: tar -czf fossbench-${{ matrix.target }}.tar.gz -C dist fossbench-${{ matrix.target }}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: fossmark-${{ matrix.target }} name: fossbench-${{ matrix.target }}
path: fossmark-${{ matrix.target }}.tar.gz path: fossbench-${{ matrix.target }}.tar.gz
if-no-files-found: error if-no-files-found: error
release: release:
@@ -149,13 +149,13 @@ jobs:
- name: Download build artifacts - name: Download build artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
pattern: fossmark-* pattern: fossbench-*
path: release path: release
merge-multiple: true merge-multiple: true
- name: Create checksums - name: Create checksums
working-directory: release working-directory: release
run: sha256sum fossmark-*.tar.gz > SHA256SUMS run: sha256sum fossbench-*.tar.gz > SHA256SUMS
- name: Create release and attach artifacts - name: Create release and attach artifacts
env: env:
@@ -166,7 +166,7 @@ jobs:
--repo "$GITHUB_REPOSITORY" \ --repo "$GITHUB_REPOSITORY" \
--yes 2>/dev/null || true --yes 2>/dev/null || true
gh release create "$RELEASE_TAG" \ gh release create "$RELEASE_TAG" \
release/fossmark-*.tar.gz \ release/fossbench-*.tar.gz \
release/SHA256SUMS \ release/SHA256SUMS \
--repo "$GITHUB_REPOSITORY" \ --repo "$GITHUB_REPOSITORY" \
--title "Release $RELEASE_TAG" \ --title "Release $RELEASE_TAG" \
+29 -29
View File
@@ -1,18 +1,18 @@
# fossmark - multi-core CPU benchmark # fossbench - multi-core CPU benchmark
# #
# The assembly kernels are architecture-specific: # The assembly kernels are architecture-specific:
# src/fossmark.S AArch64 (ARM64) # src/fossbench.S AArch64 (ARM64)
# src/fossmark_x86_64.S x86-64 (AMD64) # src/fossbench_x86_64.S x86-64 (AMD64)
# src/fossmark_i386.S x86 32-bit (i386, Pentium 4 baseline) # src/fossbench_i386.S x86 32-bit (i386, Pentium 4 baseline)
# src/fossmark_ppc32.c PowerPC 32-bit, including big-endian systems # src/fossbench_ppc32.c PowerPC 32-bit, including big-endian systems
# and the portable PPC64 kernel implementations # and the portable PPC64 kernel implementations
# The C driver (src/main.c) is portable across architectures and OSes. A # The C driver (src/main.c) is portable across architectures and OSes. A
# "binary that runs everywhere" is not possible - each OS/arch pair uses a # "binary that runs everywhere" is not possible - each OS/arch pair uses a
# different executable format and instruction set - so output is named per # different executable format and instruction set - so output is named per
# platform, e.g. dist/fossmark-linux-arm64, dist/fossmark-linux-amd64. # platform, e.g. dist/fossbench-linux-arm64, dist/fossbench-linux-amd64.
# #
# Common targets: # Common targets:
# make build for the host arch (dist/fossmark-<os>-<arch>) # make build for the host arch (dist/fossbench-<os>-<arch>)
# make linux-arm64 build the Linux/ARM64 binary # make linux-arm64 build the Linux/ARM64 binary
# make linux-amd64 build the Linux/AMD64 binary # make linux-amd64 build the Linux/AMD64 binary
# make linux-ppc64be build Linux/PPC64 big-endian for an iMac G5 # make linux-ppc64be build Linux/PPC64 big-endian for an iMac G5
@@ -47,12 +47,12 @@ PTHREAD := -pthread
DIST := dist DIST := dist
DRIVER := src/main.c DRIVER := src/main.c
ASM_ARM64 := src/fossmark.S ASM_ARM64 := src/fossbench.S
ASM_AMD64 := src/fossmark_x86_64.S ASM_AMD64 := src/fossbench_x86_64.S
ASM_I386 := src/fossmark_i386.S ASM_I386 := src/fossbench_i386.S
SRC_PPC32 := src/fossmark_ppc32.c SRC_PPC32 := src/fossbench_ppc32.c
ASM_PPC32 := src/fossmark_ppc32_ext.S ASM_PPC32 := src/fossbench_ppc32_ext.S
SRC_PPC64 := src/fossmark_ppc32.c SRC_PPC64 := src/fossbench_ppc32.c
# ---- host detection: normalise `uname -m` to our arch names ---- # ---- host detection: normalise `uname -m` to our arch names ----
HOST_ARCH := $(shell uname -m) HOST_ARCH := $(shell uname -m)
@@ -76,7 +76,7 @@ else
$(error unsupported host architecture '$(HOST_ARCH)') $(error unsupported host architecture '$(HOST_ARCH)')
endif endif
ifeq ($(HOST_ARCHNAME),i386) ifeq ($(HOST_ARCHNAME),i386)
# The kernels are hand-written assembly (fossmark_i386.S) using SSE2 # The kernels are hand-written assembly (fossbench_i386.S) using SSE2
# directly, so -msse2/-mfpmath=sse have nothing left to gate - only # directly, so -msse2/-mfpmath=sse have nothing left to gate - only
# main.c (the portable driver) is still compiled from C here. # main.c (the portable driver) is still compiled from C here.
# #
@@ -136,7 +136,7 @@ else
CC_PPC64BE ?= powerpc64-linux-gnu-gcc CC_PPC64BE ?= powerpc64-linux-gnu-gcc
endif endif
NATIVE_BIN := $(DIST)/fossmark-$(OSNAME)-$(HOST_ARCHNAME) NATIVE_BIN := $(DIST)/fossbench-$(OSNAME)-$(HOST_ARCHNAME)
# `make` with no target builds the host binary, as before. # `make` with no target builds the host binary, as before.
.DEFAULT_GOAL := native .DEFAULT_GOAL := native
@@ -148,39 +148,39 @@ all: linux-arm64 linux-amd64 linux-i386 linux-ppc32be linux-ppc64be
# `make native` (and bare `make`) build for whatever host you are on. # `make native` (and bare `make`) build for whatever host you are on.
native: $(NATIVE_BIN) native: $(NATIVE_BIN)
linux-arm64: $(DIST)/fossmark-linux-arm64 linux-arm64: $(DIST)/fossbench-linux-arm64
linux-amd64: $(DIST)/fossmark-linux-amd64 linux-amd64: $(DIST)/fossbench-linux-amd64
linux-i386: $(DIST)/fossmark-linux-i386 linux-i386: $(DIST)/fossbench-linux-i386
linux-ppc32be: $(DIST)/fossmark-linux-ppc32be linux-ppc32be: $(DIST)/fossbench-linux-ppc32be
linux-ppc64be: $(DIST)/fossmark-linux-ppc64be linux-ppc64be: $(DIST)/fossbench-linux-ppc64be
macos-arm64: $(DIST)/fossmark-macos-arm64 macos-arm64: $(DIST)/fossbench-macos-arm64
macos-amd64: $(DIST)/fossmark-macos-amd64 macos-amd64: $(DIST)/fossbench-macos-amd64
$(DIST)/fossmark-linux-arm64: $(DRIVER) $(ASM_ARM64) | $(DIST) $(DIST)/fossbench-linux-arm64: $(DRIVER) $(ASM_ARM64) | $(DIST)
$(CC_ARM64) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_ARM64) $(LDLIBS) $(CC_ARM64) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_ARM64) $(LDLIBS)
@echo "built $@" @echo "built $@"
$(DIST)/fossmark-linux-amd64: $(DRIVER) $(ASM_AMD64) | $(DIST) $(DIST)/fossbench-linux-amd64: $(DRIVER) $(ASM_AMD64) | $(DIST)
$(CC_AMD64) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS) $(CC_AMD64) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS)
@echo "built $@" @echo "built $@"
$(DIST)/fossmark-linux-i386: $(DRIVER) $(ASM_I386) | $(DIST) $(DIST)/fossbench-linux-i386: $(DRIVER) $(ASM_I386) | $(DIST)
$(CC_I386) -m32 -march=pentium4 -fno-pie -no-pie $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_I386) $(LDLIBS) $(CC_I386) -m32 -march=pentium4 -fno-pie -no-pie $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_I386) $(LDLIBS)
@echo "built $@" @echo "built $@"
$(DIST)/fossmark-linux-ppc32be: $(DRIVER) $(SRC_PPC32) $(ASM_PPC32) | $(DIST) $(DIST)/fossbench-linux-ppc32be: $(DRIVER) $(SRC_PPC32) $(ASM_PPC32) | $(DIST)
$(CC_PPC32BE) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_PPC32) $(ASM_PPC32) $(LDLIBS) $(CC_PPC32BE) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_PPC32) $(ASM_PPC32) $(LDLIBS)
@echo "built $@" @echo "built $@"
$(DIST)/fossmark-linux-ppc64be: $(DRIVER) $(SRC_PPC64) | $(DIST) $(DIST)/fossbench-linux-ppc64be: $(DRIVER) $(SRC_PPC64) | $(DIST)
$(CC_PPC64BE) -mcpu=970 -maltivec $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_PPC64) $(LDLIBS) $(CC_PPC64BE) -mcpu=970 -maltivec $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_PPC64) $(LDLIBS)
@echo "built $@" @echo "built $@"
$(DIST)/fossmark-macos-arm64: $(DRIVER) $(ASM_ARM64) | $(DIST) $(DIST)/fossbench-macos-arm64: $(DRIVER) $(ASM_ARM64) | $(DIST)
$(CC_MACOS_ARM64) -arch arm64 $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_ARM64) $(LDLIBS) $(CC_MACOS_ARM64) -arch arm64 $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_ARM64) $(LDLIBS)
@echo "built $@" @echo "built $@"
$(DIST)/fossmark-macos-amd64: $(DRIVER) $(ASM_AMD64) | $(DIST) $(DIST)/fossbench-macos-amd64: $(DRIVER) $(ASM_AMD64) | $(DIST)
MACOSX_DEPLOYMENT_TARGET=$(MACOS_AMD64_MIN) $(CC_MACOS_AMD64) -arch x86_64 -mmacosx-version-min=$(MACOS_AMD64_MIN) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -Wl,-no_fixup_chains -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS) MACOSX_DEPLOYMENT_TARGET=$(MACOS_AMD64_MIN) $(CC_MACOS_AMD64) -arch x86_64 -mmacosx-version-min=$(MACOS_AMD64_MIN) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -Wl,-no_fixup_chains -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS)
@echo "built $@" @echo "built $@"
+29 -17
View File
@@ -5,7 +5,7 @@ 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 across every available core. The final report includes separate single-core and
multicore scores. multicore scores.
The repository currently builds an executable named `fossmark` for ARM64, The repository currently builds an executable named `fossbench` for ARM64,
x86 (Pentium 4 or newer), x86-64, and 32- or 64-bit big-endian PowerPC. The C driver handles timing, memory, x86 (Pentium 4 or newer), x86-64, and 32- or 64-bit big-endian PowerPC. The C driver handles timing, memory,
threads, output, and scoring. Performance-sensitive kernels live in threads, output, and scoring. Performance-sensitive kernels live in
architecture-specific backend files. architecture-specific backend files.
@@ -43,7 +43,7 @@ make bench
``` ```
`make` builds a binary for the host at `make` builds a binary for the host at
`dist/fossmark-<os>-<arch>`. `make bench` builds that binary and runs it. `dist/fossbench-<os>-<arch>`. `make bench` builds that binary and runs it.
Other targets are available for explicit platforms and architectures: Other targets are available for explicit platforms and architectures:
@@ -81,23 +81,35 @@ deployment floor when needed with `MACOS_AMD64_MIN`, for example
Run the benchmark with extra per-test details by passing `--verbose`: Run the benchmark with extra per-test details by passing `--verbose`:
```sh ```sh
./dist/fossmark-linux-amd64 --verbose ./dist/fossbench-linux-amd64 --verbose
``` ```
The exact filename depends on the host platform and architecture. The exact filename depends on the host platform and architecture.
At startup, fossmark reports the detected CPU model, physical cores, logical At startup, fossbench reports the detected CPU model, physical cores, logical
threads, installed memory, operating system, architecture, and compiler. At the threads, installed memory, operating system, architecture, and compiler. At the
end it prints the composite scores and total benchmark duration, then asks end it prints the composite scores and total benchmark duration, then asks
whether to upload the result. Uploading is anonymous and opt-in; no account or whether to upload the result. Uploading is opt-in and anonymous by default; no
API token is required. account or API token is required. Pass `--upload` to upload without asking, or
`--noupload` to skip the prompt and never upload.
The API base URL is defined by `FM_API_BASE_URL` in `src/main.c` and defaults to To associate results with your fossbench.net profile instead of submitting
anonymously, create an API token under Account -> Benchmark client API token
and set it in the environment:
```sh
export FOSSBENCH_TOKEN=fb_your_token_here
./dist/fossbench-linux-amd64 --upload
```
The token is never printed or logged by fossbench.
The API base URL is defined by `FB_API_BASE_URL` in `src/main.c` and defaults to
`https://fossbench.net`. A release build can override it without editing the `https://fossbench.net`. A release build can override it without editing the
source: source:
```sh ```sh
make CFLAGS='-O2 -Wall -Wextra -DFM_API_BASE_URL=\"https://bench.example.com\"' make CFLAGS='-O2 -Wall -Wextra -DFB_API_BASE_URL=\"https://bench.example.com\"'
``` ```
HTTPS uploads use OpenSSL with certificate and hostname verification. HTTPS uploads use OpenSSL with certificate and hostname verification.
@@ -162,14 +174,14 @@ normal.
The kernel backends use only baseline instructions for their architecture: The kernel backends use only baseline instructions for their architecture:
* `src/fossmark.S` uses ARMv8-A and NEON under AAPCS64. * `src/fossbench.S` uses ARMv8-A and NEON under AAPCS64.
* `src/fossmark_x86_64.S` uses baseline x86-64 and SSE2 under the System V ABI. * `src/fossbench_x86_64.S` uses baseline x86-64 and SSE2 under the System V ABI.
* `src/fossmark_i386.S` uses baseline 32-bit x86 (Pentium 4) and SSE2 under the * `src/fossbench_i386.S` uses baseline 32-bit x86 (Pentium 4) and SSE2 under the
i386 System V (cdecl) ABI. With only six general-purpose registers, no i386 System V (cdecl) ABI. With only six general-purpose registers, no
64-bit integer registers, and half of amd64's SSE2 register file (xmm0-7), 64-bit integer registers, and half of amd64's SSE2 register file (xmm0-7),
several kernels keep working state on the stack instead of in registers - several kernels keep working state on the stack instead of in registers -
a real cost of the architecture, not an oversight. a real cost of the architecture, not an oversight.
* `src/fossmark_ppc32.c` is endian-safe and keeps a baseline 32-bit PowerPC * `src/fossbench_ppc32.c` is endian-safe and keeps a baseline 32-bit PowerPC
fallback. At runtime, the extended-instruction test uses Paired Singles when fallback. At runtime, the extended-instruction test uses Paired Singles when
the device-tree `compatible` property begins with `nintendo,`; otherwise it the device-tree `compatible` property begins with `nintendo,`; otherwise it
selects VSX, AltiVec, or the scalar fallback in that order according to selects VSX, AltiVec, or the scalar fallback in that order according to
@@ -208,11 +220,11 @@ with a nonzero status if any check fails.
```text ```text
src/main.c portable benchmark driver and scoring src/main.c portable benchmark driver and scoring
src/fossmark.S ARM64 kernels src/fossbench.S ARM64 kernels
src/fossmark_x86_64.S x86-64 kernels src/fossbench_x86_64.S x86-64 kernels
src/fossmark_i386.S i386 (Pentium 4) kernels src/fossbench_i386.S i386 (Pentium 4) kernels
src/fossmark_ppc32.c PPC32/PPC64 big-endian kernels src/fossbench_ppc32.c PPC32/PPC64 big-endian kernels
src/fossmark_ppc32_ext.S optional PPC32 PS, VSX, and AltiVec kernels src/fossbench_ppc32_ext.S optional PPC32 PS, VSX, and AltiVec kernels
src/test_kernels.c correctness suite src/test_kernels.c correctness suite
Makefile native and cross-build targets Makefile native and cross-build targets
dist/ generated binaries dist/ generated binaries
+28 -28
View File
@@ -1,5 +1,5 @@
/* /*
* fossmark.S - AArch64 CPU benchmark kernels * fossbench.S - AArch64 CPU benchmark kernels
* *
* OS-independent: contains no syscalls, no libc calls, no relocations against * OS-independent: contains no syscalls, no libc calls, no relocations against
* external data. Every routine is a pure function of its arguments under the * external data. Every routine is a pure function of its arguments under the
@@ -51,14 +51,14 @@
.text .text
/* =================================================================== /* ===================================================================
* uint64_t fm_int_math(uint64_t iters) * uint64_t fb_int_math(uint64_t iters)
* *
* Four largely independent accumulator chains to expose instruction-level * Four largely independent accumulator chains to expose instruction-level
* parallelism, mixed with high-latency serialising ops (udiv/sdiv) and the * parallelism, mixed with high-latency serialising ops (udiv/sdiv) and the
* bit-manipulation instructions. Returns a checksum so the compiler and the * bit-manipulation instructions. Returns a checksum so the compiler and the
* driver cannot elide the work. * driver cannot elide the work.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_int_math) FN_BEGIN(fb_int_math)
cbz x0, .Lim_zero cbz x0, .Lim_zero
MOV64(x1, 0x9E3779B97F4A7C15) /* a */ MOV64(x1, 0x9E3779B97F4A7C15) /* a */
@@ -117,18 +117,18 @@ FN_BEGIN(fm_int_math)
.Lim_zero: .Lim_zero:
mov x0, xzr mov x0, xzr
ret ret
FN_END(fm_int_math) FN_END(fb_int_math)
/* =================================================================== /* ===================================================================
* uint64_t fm_fp_math(uint64_t iters) * uint64_t fb_fp_math(uint64_t iters)
* *
* Double-precision scalar FP. Four fmadd chains cover the pipelined * Double-precision scalar FP. Four fmadd chains cover the pipelined
* multiply-add path; fdiv and fsqrt cover the non-pipelined divide/sqrt unit, * multiply-add path; fdiv and fsqrt cover the non-pipelined divide/sqrt unit,
* which is usually the real differentiator between cores. * which is usually the real differentiator between cores.
* Returns the result bit-cast to u64. * Returns the result bit-cast to u64.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_fp_math) FN_BEGIN(fb_fp_math)
cbz x0, .Lfp_zero cbz x0, .Lfp_zero
mov x6, x0 mov x6, x0
@@ -187,7 +187,7 @@ FN_BEGIN(fm_fp_math)
.Lfp_zero: .Lfp_zero:
mov x0, xzr mov x0, xzr
ret ret
FN_END(fm_fp_math) FN_END(fb_fp_math)
.p2align 4 .p2align 4
.Lfp_consts: .Lfp_consts:
@@ -198,7 +198,7 @@ FN_END(fm_fp_math)
/* =================================================================== /* ===================================================================
* uint64_t fm_primes(uint64_t limit, uint8_t *sieve) * uint64_t fb_primes(uint64_t limit, uint8_t *sieve)
* *
* Sieve of Eratosthenes over [0, limit). The caller supplies `limit` bytes of * Sieve of Eratosthenes over [0, limit). The caller supplies `limit` bytes of
* scratch; this routine clears it itself, so the clearing pass is part of the * scratch; this routine clears it itself, so the clearing pass is part of the
@@ -207,7 +207,7 @@ FN_END(fm_fp_math)
* Strided stores over a buffer larger than L1 make this a memory-hierarchy * Strided stores over a buffer larger than L1 make this a memory-hierarchy
* test as much as an arithmetic one. * test as much as an arithmetic one.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_primes) FN_BEGIN(fb_primes)
cmp x0, #2 cmp x0, #2
b.lo .Lpr_none b.lo .Lpr_none
@@ -280,11 +280,11 @@ FN_BEGIN(fm_primes)
.Lpr_none: .Lpr_none:
mov x0, xzr mov x0, xzr
ret ret
FN_END(fm_primes) FN_END(fb_primes)
/* =================================================================== /* ===================================================================
* uint64_t fm_simd(uint64_t iters, void *buf) * uint64_t fb_simd(uint64_t iters, void *buf)
* *
* "Extended instructions": the ASIMD/NEON unit, which is architecturally * "Extended instructions": the ASIMD/NEON unit, which is architecturally
* mandatory on AArch64 and therefore safe to use without runtime feature * mandatory on AArch64 and therefore safe to use without runtime feature
@@ -294,7 +294,7 @@ FN_END(fm_primes)
* *
* buf must be at least 128 bytes and 16-byte aligned. Returns a checksum. * buf must be at least 128 bytes and 16-byte aligned. Returns a checksum.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_simd) FN_BEGIN(fb_simd)
cbz x0, .Lsd_zero cbz x0, .Lsd_zero
mov x6, x0 mov x6, x0
@@ -394,7 +394,7 @@ FN_BEGIN(fm_simd)
.Lsd_zero: .Lsd_zero:
mov x0, xzr mov x0, xzr
ret ret
FN_END(fm_simd) FN_END(fb_simd)
.p2align 4 .p2align 4
.Lsd_perm: .Lsd_perm:
@@ -402,7 +402,7 @@ FN_END(fm_simd)
/* =================================================================== /* ===================================================================
* uint64_t fm_compress(const uint8_t *src, uint64_t len, uint32_t *ht) * uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht)
* *
* The match-finding inner loop of an LZ77 compressor (the LZ4 fast strategy): * The match-finding inner loop of an LZ77 compressor (the LZ4 fast strategy):
* hash the next 4 bytes, probe a single-entry-per-bucket table, verify, then * hash the next 4 bytes, probe a single-entry-per-bucket table, verify, then
@@ -412,7 +412,7 @@ FN_END(fm_simd)
* ht must hold 1<<16 uint32_t (256 KiB); this routine clears it itself. * ht must hold 1<<16 uint32_t (256 KiB); this routine clears it itself.
* Returns the encoded size in bytes. * Returns the encoded size in bytes.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_compress) FN_BEGIN(fb_compress)
stp x29, x30, [sp, #-96]! stp x29, x30, [sp, #-96]!
mov x29, sp mov x29, sp
stp x19, x20, [sp, #16] stp x19, x20, [sp, #16]
@@ -523,11 +523,11 @@ FN_BEGIN(fm_compress)
ldp x19, x20, [sp, #16] ldp x19, x20, [sp, #16]
ldp x29, x30, [sp], #96 ldp x29, x30, [sp], #96
ret ret
FN_END(fm_compress) FN_END(fb_compress)
/* =================================================================== /* ===================================================================
* uint64_t fm_chacha20(uint8_t *buf, uint64_t len, const uint8_t key[32], * uint64_t fb_chacha20(uint8_t *buf, uint64_t len, const uint8_t key[32],
* uint64_t rounds) * uint64_t rounds)
* *
* ChaCha20 stream cipher, NEON, four 128-bit state rows. Chosen over AES * ChaCha20 stream cipher, NEON, four 128-bit state rows. Chosen over AES
@@ -566,7 +566,7 @@ FN_END(fm_compress)
VROTL_ASM \b, \b, v24, 7 VROTL_ASM \b, \b, v24, 7
.endm .endm
FN_BEGIN(fm_chacha20) FN_BEGIN(fb_chacha20)
and x1, x1, #~63 /* whole 64-byte blocks only */ and x1, x1, #~63 /* whole 64-byte blocks only */
cbz x1, .Lcc_zero cbz x1, .Lcc_zero
cbz x3, .Lcc_zero cbz x3, .Lcc_zero
@@ -664,7 +664,7 @@ FN_BEGIN(fm_chacha20)
.Lcc_zero: .Lcc_zero:
mov x0, xzr mov x0, xzr
ret ret
FN_END(fm_chacha20) FN_END(fb_chacha20)
.p2align 4 .p2align 4
.Lcc_sigma: .Lcc_sigma:
@@ -672,7 +672,7 @@ FN_END(fm_chacha20)
/* =================================================================== /* ===================================================================
* uint64_t fm_physics(double *bodies, uint64_t n, uint64_t steps) * uint64_t fb_physics(double *bodies, uint64_t n, uint64_t steps)
* *
* Direct-summation N-body gravity, O(n^2) per step, double precision. * Direct-summation N-body gravity, O(n^2) per step, double precision.
* Layout per body, 8 doubles (64 bytes, one cache line): * Layout per body, 8 doubles (64 bytes, one cache line):
@@ -682,7 +682,7 @@ FN_END(fm_chacha20)
* so this exercises the divide/sqrt unit the way physics code actually does. * so this exercises the divide/sqrt unit the way physics code actually does.
* Returns a checksum bit-cast from the final velocity sum. * Returns a checksum bit-cast from the final velocity sum.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_physics) FN_BEGIN(fb_physics)
cbz x1, .Lph_zero cbz x1, .Lph_zero
cbz x2, .Lph_zero cbz x2, .Lph_zero
@@ -807,7 +807,7 @@ FN_BEGIN(fm_physics)
.Lph_zero: .Lph_zero:
mov x0, xzr mov x0, xzr
ret ret
FN_END(fm_physics) FN_END(fb_physics)
.p2align 4 .p2align 4
.Lph_consts: .Lph_consts:
@@ -817,7 +817,7 @@ FN_END(fm_physics)
/* =================================================================== /* ===================================================================
* uint64_t fm_sort(uint32_t *a, uint64_t n) * uint64_t fb_sort(uint32_t *a, uint64_t n)
* *
* In-place heapsort. Chosen over quicksort because it needs no recursion or * In-place heapsort. Chosen over quicksort because it needs no recursion or
* explicit stack, yet is aggressively branch-unpredictable and touches memory * explicit stack, yet is aggressively branch-unpredictable and touches memory
@@ -826,7 +826,7 @@ FN_END(fm_physics)
* *
* Returns an order-sensitive checksum, which also verifies the sort. * Returns an order-sensitive checksum, which also verifies the sort.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_sort) FN_BEGIN(fb_sort)
cmp x1, #2 cmp x1, #2
b.lo .Lst_trivial b.lo .Lst_trivial
@@ -925,11 +925,11 @@ FN_BEGIN(fm_sort)
.Lst_sift_done: .Lst_sift_done:
ret ret
FN_END(fm_sort) FN_END(fb_sort)
/* =================================================================== /* ===================================================================
* uint64_t fm_chase(void **ptrs, uint64_t steps) * uint64_t fb_chase(void **ptrs, uint64_t steps)
* *
* Pointer chase around a randomised cycle. Every load depends on the previous * Pointer chase around a randomised cycle. Every load depends on the previous
* one, so nothing can be prefetched, overlapped or reordered - this measures * one, so nothing can be prefetched, overlapped or reordered - this measures
@@ -937,7 +937,7 @@ FN_END(fm_sort)
* hardest thing for a wide out-of-order core to hide. It is the truest * hardest thing for a wide out-of-order core to hide. It is the truest
* "single-threaded" test in the suite. * "single-threaded" test in the suite.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_chase) FN_BEGIN(fb_chase)
cbz x1, .Lch_zero cbz x1, .Lch_zero
mov x2, x0 /* p = ptrs */ mov x2, x0 /* p = ptrs */
mov x3, x1 mov x3, x1
@@ -953,7 +953,7 @@ FN_BEGIN(fm_chase)
.Lch_zero: .Lch_zero:
mov x0, xzr mov x0, xzr
ret ret
FN_END(fm_chase) FN_END(fb_chase)
#if defined(__ELF__) #if defined(__ELF__)
+35 -35
View File
@@ -1,7 +1,7 @@
/* /*
* fossmark_i386.S - x86 32-bit (i386) CPU benchmark kernels * fossbench_i386.S - x86 32-bit (i386) CPU benchmark kernels
* *
* The i386 counterpart to fossmark_x86_64.S. Same nine routines, same * The i386 counterpart to fossbench_x86_64.S. Same nine routines, same
* contract: each is a pure function of its arguments, contains no syscalls, * contract: each is a pure function of its arguments, contains no syscalls,
* no libc calls and no external data relocations, so it assembles and runs * no libc calls and no external data relocations, so it assembles and runs
* unmodified under the plain i386 SysV (cdecl) ABI on Linux. * unmodified under the plain i386 SysV (cdecl) ABI on Linux.
@@ -60,7 +60,7 @@
/* =================================================================== /* ===================================================================
* uint64_t fm_int_math(uint64_t iters) [ [esp+4]:[esp+8] = iters ] * uint64_t fb_int_math(uint64_t iters) [ [esp+4]:[esp+8] = iters ]
* *
* Four independent multiply-accumulate chains (a,b,c,d), each carried as a * Four independent multiply-accumulate chains (a,b,c,d), each carried as a
* hi:lo stack-frame pair since there's no register space left to hold four * hi:lo stack-frame pair since there's no register space left to hold four
@@ -73,7 +73,7 @@
* hi/lo and does two native 32-bit `div`s - the same trick used to fix the * hi/lo and does two native 32-bit `div`s - the same trick used to fix the
* C fallback's __udivdi3 calls, now built directly into the kernel. * C fallback's __udivdi3 calls, now built directly into the kernel.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_int_math) FN_BEGIN(fb_int_math)
push ebp push ebp
mov ebp, esp mov ebp, esp
push ebx push ebx
@@ -314,11 +314,11 @@ FN_BEGIN(fm_int_math)
pop ebx pop ebx
pop ebp pop ebp
ret ret
FN_END(fm_int_math) FN_END(fb_int_math)
/* =================================================================== /* ===================================================================
* uint64_t fm_fp_math(uint64_t iters) [ [esp+4]:[esp+8] = iters ] * uint64_t fb_fp_math(uint64_t iters) [ [esp+4]:[esp+8] = iters ]
* *
* Double-precision scalar FP, genuine SSE2 throughout (Pentium 4's SSE2 unit * Double-precision scalar FP, genuine SSE2 throughout (Pentium 4's SSE2 unit
* handles mulsd/addsd/minsd/maxsd/divsd/sqrtsd natively - there is no libm * handles mulsd/addsd/minsd/maxsd/divsd/sqrtsd natively - there is no libm
@@ -332,7 +332,7 @@ FN_END(fm_int_math)
* u64 (edx:eax), matching the uint64_t return type - NOT via the x87 * u64 (edx:eax), matching the uint64_t return type - NOT via the x87
* ST(0) that a `double` return would use. * ST(0) that a `double` return would use.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_fp_math) FN_BEGIN(fb_fp_math)
push ebp push ebp
mov ebp, esp mov ebp, esp
sub esp, 8 sub esp, 8
@@ -401,7 +401,7 @@ FN_BEGIN(fm_fp_math)
xor edx, edx xor edx, edx
leave leave
ret ret
FN_END(fm_fp_math) FN_END(fb_fp_math)
.p2align 4 .p2align 4
.Lfp_a_init: .Lfp_a_init:
@@ -426,7 +426,7 @@ FN_END(fm_fp_math)
/* =================================================================== /* ===================================================================
* uint64_t fm_primes(uint64_t limit, uint8_t *sieve) * uint64_t fb_primes(uint64_t limit, uint8_t *sieve)
* [ [ebp+8]:[ebp+12]=limit, [ebp+16]=sieve ] * [ [ebp+8]:[ebp+12]=limit, [ebp+16]=sieve ]
* *
* Sieve of Eratosthenes over [0, limit). Only the low dword of `limit` is * Sieve of Eratosthenes over [0, limit). Only the low dword of `limit` is
@@ -436,7 +436,7 @@ FN_END(fm_fp_math)
* Clears its own scratch (32 bytes/iteration via SSE2), then sieves. Returns * Clears its own scratch (32 bytes/iteration via SSE2), then sieves. Returns
* the prime count in eax (edx=0: the count is always far under 2^32). * the prime count in eax (edx=0: the count is always far under 2^32).
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_primes) FN_BEGIN(fb_primes)
push ebp push ebp
mov ebp, esp mov ebp, esp
push ebx push ebx
@@ -522,11 +522,11 @@ FN_BEGIN(fm_primes)
pop ebx pop ebx
pop ebp pop ebp
ret ret
FN_END(fm_primes) FN_END(fb_primes)
/* =================================================================== /* ===================================================================
* uint64_t fm_simd(uint64_t iters, void *buf) * uint64_t fb_simd(uint64_t iters, void *buf)
* [ [ebp+8]:[ebp+12]=iters, [ebp+16]=buf ] * [ [ebp+8]:[ebp+12]=iters, [ebp+16]=buf ]
* *
* "Extended instructions": SSE2, architecturally mandatory baseline for this * "Extended instructions": SSE2, architecturally mandatory baseline for this
@@ -542,7 +542,7 @@ FN_END(fm_primes)
* *
* buf must be at least 96 bytes. Returns a checksum (edx=0). * buf must be at least 96 bytes. Returns a checksum (edx=0).
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_simd) FN_BEGIN(fb_simd)
push ebp push ebp
mov ebp, esp mov ebp, esp
push esi push esi
@@ -645,16 +645,16 @@ FN_BEGIN(fm_simd)
pop esi pop esi
pop ebp pop ebp
ret ret
FN_END(fm_simd) FN_END(fb_simd)
/* =================================================================== /* ===================================================================
* uint64_t fm_compress(const uint8_t *src, uint64_t len, uint32_t *ht) * uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht)
* [ [ebp+8]=src, [ebp+12]:[ebp+16]=len, [ebp+20]=ht ] * [ [ebp+8]=src, [ebp+12]:[ebp+16]=len, [ebp+20]=ht ]
* *
* The LZ77 fast-match inner loop (LZ4-style): hash the next 4 bytes, probe a * The LZ77 fast-match inner loop (LZ4-style): hash the next 4 bytes, probe a
* single-entry-per-bucket table, verify, then extend. Only the low dword of * single-entry-per-bucket table, verify, then extend. Only the low dword of
* `len` is used (a buffer size - see fm_primes). This one has the tightest * `len` is used (a buffer size - see fb_primes). This one has the tightest
* register budget in the file: ip, ref, end and the match length all need * register budget in the file: ip, ref, end and the match length all need
* to be live across the byte-by-byte extend loop, which leaves nothing to * to be live across the byte-by-byte extend loop, which leaves nothing to
* hold ht/src/anchor/mflimit/outsize in registers too, so those live on the * hold ht/src/anchor/mflimit/outsize in registers too, so those live on the
@@ -664,7 +664,7 @@ FN_END(fm_simd)
* ht must hold 1<<16 uint32_t (256 KiB); this routine clears it itself. * ht must hold 1<<16 uint32_t (256 KiB); this routine clears it itself.
* Returns the encoded size in bytes (edx=0). * Returns the encoded size in bytes (edx=0).
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_compress) FN_BEGIN(fb_compress)
push ebp push ebp
mov ebp, esp mov ebp, esp
push ebx push ebx
@@ -788,11 +788,11 @@ FN_BEGIN(fm_compress)
pop ebx pop ebx
pop ebp pop ebp
ret ret
FN_END(fm_compress) FN_END(fb_compress)
/* rotate each 32-bit lane of v left by n, via shift-left + shift-right + or. /* rotate each 32-bit lane of v left by n, via shift-left + shift-right + or.
* xmm7 is scratch (dedicated - see fm_chacha20's register map below). */ * xmm7 is scratch (dedicated - see fb_chacha20's register map below). */
.macro ROL32_I386 v, n .macro ROL32_I386 v, n
movdqa xmm7, \v movdqa xmm7, \v
pslld \v, \n pslld \v, \n
@@ -817,22 +817,22 @@ FN_END(fm_compress)
.endm .endm
/* =================================================================== /* ===================================================================
* uint64_t fm_chacha20(uint8_t *buf, uint64_t len, * uint64_t fb_chacha20(uint8_t *buf, uint64_t len,
* const uint8_t key[32], uint64_t passes) * const uint8_t key[32], uint64_t passes)
* [ [ebp+8]=buf, [ebp+12]:[ebp+16]=len, [ebp+20]=key, [ebp+24]:[ebp+28]=passes ] * [ [ebp+8]=buf, [ebp+12]:[ebp+16]=len, [ebp+20]=key, [ebp+24]:[ebp+28]=passes ]
* *
* ChaCha20, SSE2, four 128-bit state rows - same 32-bit-mode register * ChaCha20, SSE2, four 128-bit state rows - same 32-bit-mode register
* squeeze as fm_simd: xmm0-3 are the working state (rows A-D, mutated every * squeeze as fb_simd: xmm0-3 are the working state (rows A-D, mutated every
* round), xmm4-6 pin the constant/key base rows (re-copied into xmm0-2 each * round), xmm4-6 pin the constant/key base rows (re-copied into xmm0-2 each
* block) and xmm7 is the ROL32 scratch - that is all eight xmm registers * block) and xmm7 is the ROL32 scratch - that is all eight xmm registers
* with none left over, so unlike the amd64 file, the pre-round state (needed * with none left over, so unlike the amd64 file, the pre-round state (needed
* for the feed-forward add) and the running keystream checksum live on the * for the feed-forward add) and the running keystream checksum live on the
* stack instead of in xmm8-13. * stack instead of in xmm8-13.
* *
* len is rounded down to a multiple of 64 (buffer size - see fm_primes: low * len is rounded down to a multiple of 64 (buffer size - see fb_primes: low
* dword only). Returns a checksum of the keystream output (edx=0). * dword only). Returns a checksum of the keystream output (edx=0).
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_chacha20) FN_BEGIN(fb_chacha20)
push ebp push ebp
mov ebp, esp mov ebp, esp
push ebx push ebx
@@ -970,7 +970,7 @@ FN_BEGIN(fm_chacha20)
pop ebx pop ebx
pop ebp pop ebp
ret ret
FN_END(fm_chacha20) FN_END(fb_chacha20)
.p2align 4 .p2align 4
.Lcc_sigma: .Lcc_sigma:
@@ -978,20 +978,20 @@ FN_END(fm_chacha20)
/* =================================================================== /* ===================================================================
* uint64_t fm_physics(double *bodies, uint64_t n, uint64_t steps) * uint64_t fb_physics(double *bodies, uint64_t n, uint64_t steps)
* [ [ebp+8]=bodies, [ebp+12]:[ebp+16]=n, [ebp+20]:[ebp+24]=steps ] * [ [ebp+8]=bodies, [ebp+12]:[ebp+16]=n, [ebp+20]:[ebp+24]=steps ]
* *
* Direct-summation N-body gravity, O(n^2) per step, double precision, real * Direct-summation N-body gravity, O(n^2) per step, double precision, real
* sqrtsd+divsd (not an rsqrt estimate). Layout per body, 8 doubles (64 * sqrtsd+divsd (not an rsqrt estimate). Layout per body, 8 doubles (64
* bytes): [x y z mass vx vy vz pad]. `n` uses only its low dword (a body * bytes): [x y z mass vx vy vz pad]. `n` uses only its low dword (a body
* count - see fm_primes). xmm0-2 hold body i's position for the whole inner * count - see fb_primes). xmm0-2 hold body i's position for the whole inner
* loop, xmm3-5 accumulate its acceleration, and xmm6-7 are the only scratch * loop, xmm3-5 accumulate its acceleration, and xmm6-7 are the only scratch
* left - not enough to hold dx/dy/dz simultaneously alongside r/1/r/m-over-r3, * left - not enough to hold dx/dy/dz simultaneously alongside r/1/r/m-over-r3,
* so the three deltas spill to three stack doubles between being computed * so the three deltas spill to three stack doubles between being computed
* and being used in the final ax+=dx*q step. Returns a velocity checksum * and being used in the final ax+=dx*q step. Returns a velocity checksum
* (edx=0). * (edx=0).
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_physics) FN_BEGIN(fb_physics)
push ebp push ebp
mov ebp, esp mov ebp, esp
push ebx push ebx
@@ -1158,7 +1158,7 @@ FN_BEGIN(fm_physics)
pop ebx pop ebx
pop ebp pop ebp
ret ret
FN_END(fm_physics) FN_END(fb_physics)
.p2align 4 .p2align 4
.Lph_dt: .Lph_dt:
@@ -1170,11 +1170,11 @@ FN_END(fm_physics)
/* =================================================================== /* ===================================================================
* uint64_t fm_sort(uint32_t *a, uint64_t n) [ [ebp+8]=a, [ebp+12]:[ebp+16]=n ] * uint64_t fb_sort(uint32_t *a, uint64_t n) [ [ebp+8]=a, [ebp+12]:[ebp+16]=n ]
* *
* In-place heapsort, same shape as the amd64 file: no recursion, an * In-place heapsort, same shape as the amd64 file: no recursion, an
* order-sensitive checksum that doubles as a correctness check. `n` uses * order-sensitive checksum that doubles as a correctness check. `n` uses
* only its low dword (an element count - see fm_primes). The internal * only its low dword (an element count - see fb_primes). The internal
* siftdown is reached with `call`/`ret` sharing this function's own frame * siftdown is reached with `call`/`ret` sharing this function's own frame
* (no separate prologue) exactly like the amd64 version; it borrows ebx and * (no separate prologue) exactly like the amd64 version; it borrows ebx and
* esi as scratch for the duration of one call via push/pop, since i386 has * esi as scratch for the duration of one call via push/pop, since i386 has
@@ -1183,7 +1183,7 @@ FN_END(fm_physics)
* has no 64-bit rotate) - test_kernels.c only requires it be deterministic * has no 64-bit rotate) - test_kernels.c only requires it be deterministic
* and permutation-sensitive, which this is. * and permutation-sensitive, which this is.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_sort) FN_BEGIN(fb_sort)
push ebp push ebp
mov ebp, esp mov ebp, esp
push ebx push ebx
@@ -1300,11 +1300,11 @@ FN_BEGIN(fm_sort)
pop esi pop esi
pop ebx pop ebx
ret ret
FN_END(fm_sort) FN_END(fb_sort)
/* =================================================================== /* ===================================================================
* uint64_t fm_chase(void **ptrs, uint64_t steps) * uint64_t fb_chase(void **ptrs, uint64_t steps)
* [ [esp+4]=ptrs, [esp+8]:[esp+12]=steps ] * [ [esp+4]=ptrs, [esp+8]:[esp+12]=steps ]
* *
* Pointer chase around a randomised cycle. Every load depends on the * Pointer chase around a randomised cycle. Every load depends on the
@@ -1314,7 +1314,7 @@ FN_END(fm_sort)
* kernel here it has no prologue - args stay at their original [esp+N] * kernel here it has no prologue - args stay at their original [esp+N]
* offsets since esp never moves. * offsets since esp never moves.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_chase) FN_BEGIN(fb_chase)
mov ecx, [esp+8] mov ecx, [esp+8]
or ecx, [esp+12] or ecx, [esp+12]
jz .Lch_zero jz .Lch_zero
@@ -1336,7 +1336,7 @@ FN_BEGIN(fm_chase)
xor eax, eax xor eax, eax
xor edx, edx xor edx, edx
ret ret
FN_END(fm_chase) FN_END(fb_chase)
#if defined(__ELF__) #if defined(__ELF__)
+27 -27
View File
@@ -20,7 +20,7 @@ static uint32_t rotl32(uint32_t x, unsigned n)
return (x << n) | (x >> (32 - n)); return (x << n) | (x >> (32 - n));
} }
uint64_t fm_int_math(uint64_t iters) uint64_t fb_int_math(uint64_t iters)
{ {
uint64_t a = 0x9e3779b97f4a7c15ULL, b = 0xbf58476d1ce4e5b9ULL; uint64_t a = 0x9e3779b97f4a7c15ULL, b = 0xbf58476d1ce4e5b9ULL;
uint64_t c = 0x94d049bb133111ebULL, d = 0x2545f4914f6cdd1dULL; uint64_t c = 0x94d049bb133111ebULL, d = 0x2545f4914f6cdd1dULL;
@@ -35,7 +35,7 @@ uint64_t fm_int_math(uint64_t iters)
return a ^ b ^ c ^ d; return a ^ b ^ c ^ d;
} }
uint64_t fm_fp_math(uint64_t iters) uint64_t fb_fp_math(uint64_t iters)
{ {
double a = 1.5, b = 2.5, c = 3.5, d = .5, out; double a = 1.5, b = 2.5, c = 3.5, d = .5, out;
uint64_t bits, i; uint64_t bits, i;
@@ -52,7 +52,7 @@ uint64_t fm_fp_math(uint64_t iters)
return bits; return bits;
} }
uint64_t fm_primes(uint64_t limit, uint8_t *sieve) uint64_t fb_primes(uint64_t limit, uint8_t *sieve)
{ {
uint64_t i, j, count = 0; uint64_t i, j, count = 0;
if (limit < 2) return 0; if (limit < 2) return 0;
@@ -65,7 +65,7 @@ uint64_t fm_primes(uint64_t limit, uint8_t *sieve)
} }
#if !defined(__powerpc64__) #if !defined(__powerpc64__)
static uint64_t fm_simd_scalar(uint64_t iters, void *memory) static uint64_t fb_simd_scalar(uint64_t iters, void *memory)
{ {
uint32_t *v = (uint32_t *)memory; uint32_t *v = (uint32_t *)memory;
uint32_t a[8]; uint64_t i; unsigned j; uint32_t sum = 0; uint32_t a[8]; uint64_t i; unsigned j; uint32_t sum = 0;
@@ -83,11 +83,11 @@ static uint64_t fm_simd_scalar(uint64_t iters, void *memory)
/* The PowerPC 970 in every iMac G5 implements AltiVec. Using GCC's vector /* The PowerPC 970 in every iMac G5 implements AltiVec. Using GCC's vector
* type here lets the compiler handle whichever PPC64 ELF ABI the system uses; * type here lets the compiler handle whichever PPC64 ELF ABI the system uses;
* both PPC64 ABIs differ from the PPC32 assembly convention below. */ * both PPC64 ABIs differ from the PPC32 assembly convention below. */
typedef uint32_t fm_vec_u32 __attribute__((vector_size(16))); typedef uint32_t fb_vec_u32 __attribute__((vector_size(16)));
uint64_t fm_simd(uint64_t iters, void *memory) uint64_t fb_simd(uint64_t iters, void *memory)
{ {
fm_vec_u32 a, b; fb_vec_u32 a, b;
uint32_t *v = (uint32_t *)memory; uint32_t *v = (uint32_t *)memory;
uint32_t sum = 0; uint32_t sum = 0;
uint64_t i; uint64_t i;
@@ -110,13 +110,13 @@ uint64_t fm_simd(uint64_t iters, void *memory)
return sum; return sum;
} }
#else #else
/* These are kept in fossmark_ppc32_ext.S so this translation unit, and thus /* These are kept in fossbench_ppc32_ext.S so this translation unit, and thus
* the executable's default code path, only requires baseline PPC32. */ * the executable's default code path, only requires baseline PPC32. */
extern void fm_simd_ps_kernel(uint64_t iters, void *memory); extern void fb_simd_ps_kernel(uint64_t iters, void *memory);
extern void fm_simd_vsx_kernel(uint64_t iters, void *memory); extern void fb_simd_vsx_kernel(uint64_t iters, void *memory);
extern void fm_simd_altivec_kernel(uint64_t iters, void *memory); extern void fb_simd_altivec_kernel(uint64_t iters, void *memory);
typedef void (*fm_simd_kernel)(uint64_t, void *); typedef void (*fb_simd_kernel)(uint64_t, void *);
static int device_is_nintendo(void) static int device_is_nintendo(void)
{ {
@@ -145,7 +145,7 @@ static int device_is_nintendo(void)
#endif #endif
} }
static fm_simd_kernel detect_simd_kernel(void) static fb_simd_kernel detect_simd_kernel(void)
{ {
/* Linux exposes these in AT_HWCAP on both 32- and 64-bit PowerPC. /* Linux exposes these in AT_HWCAP on both 32- and 64-bit PowerPC.
* Spell out the ABI values instead of depending on kernel-only headers. */ * Spell out the ABI values instead of depending on kernel-only headers. */
@@ -155,23 +155,23 @@ static fm_simd_kernel detect_simd_kernel(void)
const unsigned long has_vsx = 0x00000080UL; const unsigned long has_vsx = 0x00000080UL;
if (device_is_nintendo()) if (device_is_nintendo())
return fm_simd_ps_kernel; return fb_simd_ps_kernel;
if (hwcap & has_vsx) if (hwcap & has_vsx)
return fm_simd_vsx_kernel; return fb_simd_vsx_kernel;
if (hwcap & has_altivec) if (hwcap & has_altivec)
return fm_simd_altivec_kernel; return fb_simd_altivec_kernel;
#else #else
if (device_is_nintendo()) if (device_is_nintendo())
return fm_simd_ps_kernel; return fb_simd_ps_kernel;
#endif #endif
return NULL; return NULL;
} }
uint64_t fm_simd(uint64_t iters, void *memory) uint64_t fb_simd(uint64_t iters, void *memory)
{ {
static fm_simd_kernel kernel; static fb_simd_kernel kernel;
static int detected; static int detected;
fm_simd_kernel selected; fb_simd_kernel selected;
uint32_t *v = (uint32_t *)memory; uint32_t *v = (uint32_t *)memory;
uint32_t sum = 0; uint32_t sum = 0;
unsigned j; unsigned j;
@@ -179,13 +179,13 @@ uint64_t fm_simd(uint64_t iters, void *memory)
if (!iters) if (!iters)
return 0; return 0;
if (!__atomic_load_n(&detected, __ATOMIC_ACQUIRE)) { if (!__atomic_load_n(&detected, __ATOMIC_ACQUIRE)) {
fm_simd_kernel found = detect_simd_kernel(); fb_simd_kernel found = detect_simd_kernel();
__atomic_store_n(&kernel, found, __ATOMIC_RELAXED); __atomic_store_n(&kernel, found, __ATOMIC_RELAXED);
__atomic_store_n(&detected, 1, __ATOMIC_RELEASE); __atomic_store_n(&detected, 1, __ATOMIC_RELEASE);
} }
selected = __atomic_load_n(&kernel, __ATOMIC_RELAXED); selected = __atomic_load_n(&kernel, __ATOMIC_RELAXED);
if (selected == NULL) if (selected == NULL)
return fm_simd_scalar(iters, memory); return fb_simd_scalar(iters, memory);
selected(iters, memory); selected(iters, memory);
for (j = 0; j < 8; j++) for (j = 0; j < 8; j++)
@@ -199,7 +199,7 @@ static uint32_t load32_native(const uint8_t *p)
uint32_t v; memcpy(&v, p, sizeof v); return v; uint32_t v; memcpy(&v, p, sizeof v); return v;
} }
uint64_t fm_compress(const uint8_t *src, uint64_t len, uint32_t *ht) uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht)
{ {
uint64_t ip = 0, anchor = 0, out = 0, ref, ml, lit; uint64_t ip = 0, anchor = 0, out = 0, ref, ml, lit;
memset(ht, 0, (size_t)(1U << 16) * sizeof *ht); memset(ht, 0, (size_t)(1U << 16) * sizeof *ht);
@@ -225,7 +225,7 @@ static void store32le(uint8_t *p, uint32_t v)
p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24);
} }
#define QR(a,b,c,d) do { a+=b; d=rotl32(d^a,16); c+=d; b=rotl32(b^c,12); a+=b; d=rotl32(d^a,8); c+=d; b=rotl32(b^c,7); } while (0) #define QR(a,b,c,d) do { a+=b; d=rotl32(d^a,16); c+=d; b=rotl32(b^c,12); a+=b; d=rotl32(d^a,8); c+=d; b=rotl32(b^c,7); } while (0)
uint64_t fm_chacha20(uint8_t *buf, uint64_t len, const uint8_t key[32], uint64_t passes) uint64_t fb_chacha20(uint8_t *buf, uint64_t len, const uint8_t key[32], uint64_t passes)
{ {
static const uint32_t sigma[4] = {0x61707865,0x3320646e,0x79622d32,0x6b206574}; static const uint32_t sigma[4] = {0x61707865,0x3320646e,0x79622d32,0x6b206574};
uint32_t base[16], x[16], counter = 0, checksum = 0; uint64_t pass, off; int i, r; uint32_t base[16], x[16], counter = 0, checksum = 0; uint64_t pass, off; int i, r;
@@ -241,7 +241,7 @@ uint64_t fm_chacha20(uint8_t *buf, uint64_t len, const uint8_t key[32], uint64_t
} }
#undef QR #undef QR
uint64_t fm_physics(double *b, uint64_t n, uint64_t steps) uint64_t fb_physics(double *b, uint64_t n, uint64_t steps)
{ {
uint64_t s,i,j,bits; double sum=0; uint64_t s,i,j,bits; double sum=0;
if (!n || !steps) return 0; if (!n || !steps) return 0;
@@ -253,7 +253,7 @@ uint64_t fm_physics(double *b, uint64_t n, uint64_t steps)
} }
static void sift(uint32_t *a, uint64_t root, uint64_t end) { for (;;) { uint64_t c=root*2+1; uint32_t t; if(c>=end)return; if(c+1<end&&a[c+1]>a[c])c++; if(a[root]>=a[c])return; t=a[root];a[root]=a[c];a[c]=t;root=c; } } static void sift(uint32_t *a, uint64_t root, uint64_t end) { for (;;) { uint64_t c=root*2+1; uint32_t t; if(c>=end)return; if(c+1<end&&a[c+1]>a[c])c++; if(a[root]>=a[c])return; t=a[root];a[root]=a[c];a[c]=t;root=c; } }
uint64_t fm_sort(uint32_t *a, uint64_t n) uint64_t fb_sort(uint32_t *a, uint64_t n)
{ {
uint64_t i,end,sum=0; uint32_t t; if(n<2)return n?a[0]:0; uint64_t i,end,sum=0; uint32_t t; if(n<2)return n?a[0]:0;
for (i = n / 2; i; i--) for (i = n / 2; i; i--)
@@ -265,7 +265,7 @@ uint64_t fm_sort(uint32_t *a, uint64_t n)
for(i=0;i<n;i++){sum=(sum>>7)|(sum<<57);sum^=a[i];sum+=a[i];} return sum; for(i=0;i<n;i++){sum=(sum>>7)|(sum<<57);sum^=a[i];sum+=a[i];} return sum;
} }
uint64_t fm_chase(void **ptrs, uint64_t steps) uint64_t fb_chase(void **ptrs, uint64_t steps)
{ {
void **p=ptrs; uint64_t i; if(!steps)return 0; for(i=0;i<steps;i++)p=(void **)*p; return (uint64_t)((uintptr_t)p-(uintptr_t)ptrs); void **p=ptrs; uint64_t i; if(!steps)return 0; for(i=0;i<steps;i++)p=(void **)*p; return (uint64_t)((uintptr_t)p-(uintptr_t)ptrs);
} }
@@ -1,13 +1,13 @@
/* Optional PPC32 extended-instruction kernels. No instruction in this file /* Optional PPC32 extended-instruction kernels. No instruction in this file
* is reached until fossmark_ppc32.c has checked the device tree or AT_HWCAP. * is reached until fossbench_ppc32.c has checked the device tree or AT_HWCAP.
* Arguments use the PPC32 ABI: iters in r3:r4 and memory in r5. */ * Arguments use the PPC32 ABI: iters in r3:r4 and memory in r5. */
.text .text
.align 2 .align 2
.globl fm_simd_ps_kernel .globl fb_simd_ps_kernel
.type fm_simd_ps_kernel,@function .type fb_simd_ps_kernel,@function
fm_simd_ps_kernel: fb_simd_ps_kernel:
/* Raw encodings keep this buildable with standard Linux binutils, whose /* Raw encodings keep this buildable with standard Linux binutils, whose
* opcode tables do not always name Nintendo's Gekko/Broadway extension. */ * opcode tables do not always name Nintendo's Gekko/Broadway extension. */
.long 0xe0050000 /* psq_l f0,0(r5),0,0 */ .long 0xe0050000 /* psq_l f0,0(r5),0,0 */
@@ -23,11 +23,11 @@ fm_simd_ps_kernel:
.long 0xf0050000 /* psq_st f0,0(r5),0,0 */ .long 0xf0050000 /* psq_st f0,0(r5),0,0 */
.long 0xf0250008 /* psq_st f1,8(r5),0,0 */ .long 0xf0250008 /* psq_st f1,8(r5),0,0 */
blr blr
.size fm_simd_ps_kernel,.-fm_simd_ps_kernel .size fb_simd_ps_kernel,.-fb_simd_ps_kernel
.globl fm_simd_vsx_kernel .globl fb_simd_vsx_kernel
.type fm_simd_vsx_kernel,@function .type fb_simd_vsx_kernel,@function
fm_simd_vsx_kernel: fb_simd_vsx_kernel:
.machine power7 .machine power7
li 6,0 li 6,0
lxvw4x 0,6,5 lxvw4x 0,6,5
@@ -46,11 +46,11 @@ fm_simd_vsx_kernel:
li 6,16 li 6,16
stxvw4x 1,6,5 stxvw4x 1,6,5
blr blr
.size fm_simd_vsx_kernel,.-fm_simd_vsx_kernel .size fb_simd_vsx_kernel,.-fb_simd_vsx_kernel
.globl fm_simd_altivec_kernel .globl fb_simd_altivec_kernel
.type fm_simd_altivec_kernel,@function .type fb_simd_altivec_kernel,@function
fm_simd_altivec_kernel: fb_simd_altivec_kernel:
.machine altivec .machine altivec
li 6,0 li 6,0
lvx 0,6,5 lvx 0,6,5
@@ -69,6 +69,6 @@ fm_simd_altivec_kernel:
li 6,16 li 6,16
stvx 1,6,5 stvx 1,6,5
blr blr
.size fm_simd_altivec_kernel,.-fm_simd_altivec_kernel .size fb_simd_altivec_kernel,.-fb_simd_altivec_kernel
.section .note.GNU-stack,"",@progbits .section .note.GNU-stack,"",@progbits
@@ -1,7 +1,7 @@
/* /*
* fossmark_x86_64.S - x86-64 (AMD64) CPU benchmark kernels * fossbench_x86_64.S - x86-64 (AMD64) CPU benchmark kernels
* *
* The AMD64 counterpart to fossmark.S. Same nine routines, same contract: each * The AMD64 counterpart to fossbench.S. Same nine routines, same contract: each
* is a pure function of its arguments under the System V AMD64 ABI, contains no * is a pure function of its arguments under the System V AMD64 ABI, contains no
* syscalls, no libc calls and no external data relocations, so it assembles and * syscalls, no libc calls and no external data relocations, so it assembles and
* runs on Linux (ELF), macOS (Mach-O) and the BSDs. The portable C driver in * runs on Linux (ELF), macOS (Mach-O) and the BSDs. The portable C driver in
@@ -51,13 +51,13 @@
.text .text
/* =================================================================== /* ===================================================================
* uint64_t fm_int_math(uint64_t iters) [rdi = iters] * uint64_t fb_int_math(uint64_t iters) [rdi = iters]
* *
* Four independent multiply-accumulate chains for instruction-level * Four independent multiply-accumulate chains for instruction-level
* parallelism, mixed with the long-latency serialising ops (mul/div) and * parallelism, mixed with the long-latency serialising ops (mul/div) and
* bit-manipulation. Returns a checksum so nothing can be elided. * bit-manipulation. Returns a checksum so nothing can be elided.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_int_math) FN_BEGIN(fb_int_math)
test rdi, rdi test rdi, rdi
jz .Lim_zero jz .Lim_zero
@@ -138,17 +138,17 @@ FN_BEGIN(fm_int_math)
.Lim_zero: .Lim_zero:
xor eax, eax xor eax, eax
ret ret
FN_END(fm_int_math) FN_END(fb_int_math)
/* =================================================================== /* ===================================================================
* uint64_t fm_fp_math(uint64_t iters) [rdi = iters] * uint64_t fb_fp_math(uint64_t iters) [rdi = iters]
* *
* Double-precision scalar FP. Four multiply-add chains for the pipelined * Double-precision scalar FP. Four multiply-add chains for the pipelined
* path; sqrtsd and divsd for the non-pipelined divide/sqrt unit that usually * path; sqrtsd and divsd for the non-pipelined divide/sqrt unit that usually
* separates cores. Returns the result bit-cast to u64. * separates cores. Returns the result bit-cast to u64.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_fp_math) FN_BEGIN(fb_fp_math)
test rdi, rdi test rdi, rdi
jz .Lfp_zero jz .Lfp_zero
@@ -215,7 +215,7 @@ FN_BEGIN(fm_fp_math)
.Lfp_zero: .Lfp_zero:
xor eax, eax xor eax, eax
ret ret
FN_END(fm_fp_math) FN_END(fb_fp_math)
.p2align 4 .p2align 4
.Lfp_consts: .Lfp_consts:
@@ -231,14 +231,14 @@ FN_END(fm_fp_math)
/* =================================================================== /* ===================================================================
* uint64_t fm_primes(uint64_t limit, uint8_t *sieve) [rdi, rsi] * uint64_t fb_primes(uint64_t limit, uint8_t *sieve) [rdi, rsi]
* *
* Sieve of Eratosthenes over [0, limit). The routine clears the caller's * Sieve of Eratosthenes over [0, limit). The routine clears the caller's
* scratch itself, so the clearing pass counts as measured work. Strided stores * scratch itself, so the clearing pass counts as measured work. Strided stores
* over a buffer larger than L1 make this a memory-hierarchy test too. Returns * over a buffer larger than L1 make this a memory-hierarchy test too. Returns
* the prime count. * the prime count.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_primes) FN_BEGIN(fb_primes)
cmp rdi, 2 cmp rdi, 2
jb .Lpr_none jb .Lpr_none
@@ -311,11 +311,11 @@ FN_BEGIN(fm_primes)
.Lpr_none: .Lpr_none:
xor eax, eax xor eax, eax
ret ret
FN_END(fm_primes) FN_END(fb_primes)
/* =================================================================== /* ===================================================================
* uint64_t fm_simd(uint64_t iters, void *buf) [rdi = iters, rsi = buf] * uint64_t fb_simd(uint64_t iters, void *buf) [rdi = iters, rsi = buf]
* *
* "Extended instructions": the SSE2 unit, which is architecturally mandatory * "Extended instructions": the SSE2 unit, which is architecturally mandatory
* on x86-64 and therefore safe without runtime feature detection. Packed * on x86-64 and therefore safe without runtime feature detection. Packed
@@ -326,7 +326,7 @@ FN_END(fm_primes)
* *
* buf must be at least 128 bytes. Returns a checksum. * buf must be at least 128 bytes. Returns a checksum.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_simd) FN_BEGIN(fb_simd)
test rdi, rdi test rdi, rdi
jz .Lsd_zero jz .Lsd_zero
@@ -429,11 +429,11 @@ FN_BEGIN(fm_simd)
.Lsd_zero: .Lsd_zero:
xor eax, eax xor eax, eax
ret ret
FN_END(fm_simd) FN_END(fb_simd)
/* =================================================================== /* ===================================================================
* uint64_t fm_compress(const uint8_t *src, uint64_t len, uint32_t *ht) * uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht)
* [rdi, rsi, rdx] * [rdi, rsi, rdx]
* *
* The match-finding inner loop of an LZ77 compressor (the LZ4 fast strategy): * The match-finding inner loop of an LZ77 compressor (the LZ4 fast strategy):
@@ -443,7 +443,7 @@ FN_END(fm_simd)
* ht must hold 1<<16 uint32_t (256 KiB); this routine clears it itself. * ht must hold 1<<16 uint32_t (256 KiB); this routine clears it itself.
* Returns the encoded size in bytes. * Returns the encoded size in bytes.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_compress) FN_BEGIN(fb_compress)
push rbp push rbp
push rbx push rbx
push r12 push r12
@@ -556,11 +556,11 @@ FN_BEGIN(fm_compress)
pop rbx pop rbx
pop rbp pop rbp
ret ret
FN_END(fm_compress) FN_END(fb_compress)
/* =================================================================== /* ===================================================================
* uint64_t fm_chacha20(uint8_t *buf, uint64_t len, * uint64_t fb_chacha20(uint8_t *buf, uint64_t len,
* const uint8_t key[32], uint64_t rounds) * const uint8_t key[32], uint64_t rounds)
* [rdi, rsi, rdx, rcx] * [rdi, rsi, rdx, rcx]
* *
@@ -595,7 +595,7 @@ FN_END(fm_compress)
pxor b, c ;\ pxor b, c ;\
ROL32(b, 7) ROL32(b, 7)
FN_BEGIN(fm_chacha20) FN_BEGIN(fb_chacha20)
and rsi, -64 /* whole 64-byte blocks only */ and rsi, -64 /* whole 64-byte blocks only */
jz .Lcc_zero jz .Lcc_zero
test rcx, rcx test rcx, rcx
@@ -693,7 +693,7 @@ FN_BEGIN(fm_chacha20)
.Lcc_zero: .Lcc_zero:
xor eax, eax xor eax, eax
ret ret
FN_END(fm_chacha20) FN_END(fb_chacha20)
.p2align 4 .p2align 4
.Lcc_sigma: .Lcc_sigma:
@@ -701,7 +701,7 @@ FN_END(fm_chacha20)
/* =================================================================== /* ===================================================================
* uint64_t fm_physics(double *bodies, uint64_t n, uint64_t steps) * uint64_t fb_physics(double *bodies, uint64_t n, uint64_t steps)
* [rdi, rsi, rdx] * [rdi, rsi, rdx]
* *
* Direct-summation N-body gravity, O(n^2) per step, double precision. * Direct-summation N-body gravity, O(n^2) per step, double precision.
@@ -709,7 +709,7 @@ FN_END(fm_chacha20)
* The 1/sqrt is a real sqrtsd+divsd (not the rsqrt estimate), exercising the * The 1/sqrt is a real sqrtsd+divsd (not the rsqrt estimate), exercising the
* divide/sqrt unit the way physics code does. Returns a velocity checksum. * divide/sqrt unit the way physics code does. Returns a velocity checksum.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_physics) FN_BEGIN(fb_physics)
test rsi, rsi test rsi, rsi
jz .Lph_zero jz .Lph_zero
test rdx, rdx test rdx, rdx
@@ -847,7 +847,7 @@ FN_BEGIN(fm_physics)
.Lph_zero: .Lph_zero:
xor eax, eax xor eax, eax
ret ret
FN_END(fm_physics) FN_END(fb_physics)
.p2align 4 .p2align 4
.Lph_dt: .Lph_dt:
@@ -859,7 +859,7 @@ FN_END(fm_physics)
/* =================================================================== /* ===================================================================
* uint64_t fm_sort(uint32_t *a, uint64_t n) [rdi = a, rsi = n] * uint64_t fb_sort(uint32_t *a, uint64_t n) [rdi = a, rsi = n]
* *
* In-place heapsort: no recursion or explicit stack, aggressively * In-place heapsort: no recursion or explicit stack, aggressively
* branch-unpredictable, with scattered memory access - it stresses the branch * branch-unpredictable, with scattered memory access - it stresses the branch
@@ -869,7 +869,7 @@ FN_END(fm_physics)
* Uses only caller-saved registers, so no prologue is needed; the internal * Uses only caller-saved registers, so no prologue is needed; the internal
* siftdown is reached with `call` (contract below). * siftdown is reached with `call` (contract below).
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_sort) FN_BEGIN(fb_sort)
cmp rsi, 2 cmp rsi, 2
jb .Lst_trivial jb .Lst_trivial
@@ -961,18 +961,18 @@ FN_BEGIN(fm_sort)
.Lst_sift_done: .Lst_sift_done:
ret ret
FN_END(fm_sort) FN_END(fb_sort)
/* =================================================================== /* ===================================================================
* uint64_t fm_chase(void **ptrs, uint64_t steps) [rdi = ptrs, rsi = steps] * uint64_t fb_chase(void **ptrs, uint64_t steps) [rdi = ptrs, rsi = steps]
* *
* Pointer chase around a randomised cycle. Every load depends on the previous * Pointer chase around a randomised cycle. Every load depends on the previous
* one, so nothing can be prefetched, overlapped or reordered - this measures * one, so nothing can be prefetched, overlapped or reordered - this measures
* the pure serial latency of the memory hierarchy. The truest single-threaded * the pure serial latency of the memory hierarchy. The truest single-threaded
* test in the suite. * test in the suite.
* =================================================================== */ * =================================================================== */
FN_BEGIN(fm_chase) FN_BEGIN(fb_chase)
test rsi, rsi test rsi, rsi
jz .Lch_zero jz .Lch_zero
mov rax, rdi /* p = ptrs */ mov rax, rdi /* p = ptrs */
@@ -989,7 +989,7 @@ FN_BEGIN(fm_chase)
.Lch_zero: .Lch_zero:
xor eax, eax xor eax, eax
ret ret
FN_END(fm_chase) FN_END(fb_chase)
#if defined(__ELF__) #if defined(__ELF__)
+154 -96
View File
@@ -1,18 +1,18 @@
/* /*
* fossmark - a multi-core AArch64 CPU benchmark * fossbench - a multi-core AArch64 CPU benchmark
* *
* This file is the portable driver: it owns everything the assembly kernels * This file is the portable driver: it owns everything the assembly kernels
* deliberately do not (timing, memory, I/O, scoring). The kernels in * deliberately do not (timing, memory, I/O, scoring). The kernels in
* fossmark.S are pure computation and identical on every OS; only this file * fossbench.S are pure computation and identical on every OS; only this file
* knows what an operating system is. * knows what an operating system is.
* *
* Every workload is run twice: once on a single core, and once on all available * Every workload is run twice: once on a single core, and once on all available
* cores at once - one identical copy of the kernel per core, each with its own * cores at once - one identical copy of the kernel per core, each with its own
* private buffers, so the machine is driven to 100%% and the rate is whole-machine * private buffers, so the machine is driven to 100%% and the rate is whole-machine
* throughput. From these two passes fossmark reports two composite scores, a * throughput. From these two passes fossbench reports two composite scores, a
* SINGLECORE and a MULTICORE, from the same tests and the same weights. * SINGLECORE and a MULTICORE, from the same tests and the same weights.
* *
* Build: cc -O2 -pthread main.c fossmark.S -o fossmark -lm * Build: cc -O2 -pthread main.c fossbench.S -o fossbench -lm
*/ */
#include <stdio.h> #include <stdio.h>
@@ -38,51 +38,51 @@
# include <mach/mach_time.h> # include <mach/mach_time.h>
#endif #endif
/* Change this at build time with -DFM_API_BASE_URL=\"https://host\". */ /* Change this at build time with -DFB_API_BASE_URL=\"https://host\". */
#ifndef FM_API_BASE_URL #ifndef FB_API_BASE_URL
# define FM_API_BASE_URL "https://fossbench.net" # define FB_API_BASE_URL "https://fossbench.net"
#endif #endif
#define FM_VERSION "0.1.4" #define FB_VERSION "0.1.4"
/* ---------- platform identification (for the banner only) ---------- */ /* ---------- platform identification (for the banner only) ---------- */
#if defined(_WIN32) #if defined(_WIN32)
# define FM_OS "Windows" # define FB_OS "Windows"
#elif defined(__APPLE__) #elif defined(__APPLE__)
# define FM_OS "macOS" # define FB_OS "macOS"
#elif defined(__linux__) #elif defined(__linux__)
# define FM_OS "Linux" # define FB_OS "Linux"
#else #else
# define FM_OS "POSIX" # define FB_OS "POSIX"
#endif #endif
#if defined(__aarch64__) || defined(_M_ARM64) #if defined(__aarch64__) || defined(_M_ARM64)
# define FM_ARCH "ARM64" # define FB_ARCH "ARM64"
# define D_INT "64-bit ALU: madd, umulh, udiv, bitops" # define D_INT "64-bit ALU: madd, umulh, udiv, bitops"
# define D_FP "double: fmadd, fdiv, fsqrt" # define D_FP "double: fmadd, fdiv, fsqrt"
# define D_SIMD "NEON ASIMD: 128-bit integer + float" # define D_SIMD "NEON ASIMD: 128-bit integer + float"
#elif defined(__x86_64__) || defined(_M_X64) #elif defined(__x86_64__) || defined(_M_X64)
# define FM_ARCH "x86-64" # define FB_ARCH "x86-64"
# define D_INT "64-bit ALU: imul, mul, div, bitops" # define D_INT "64-bit ALU: imul, mul, div, bitops"
# define D_FP "double: mulsd/addsd, divsd, sqrtsd" # define D_FP "double: mulsd/addsd, divsd, sqrtsd"
# define D_SIMD "SSE2: 128-bit integer + float" # define D_SIMD "SSE2: 128-bit integer + float"
#elif defined(__i386__) || defined(_M_IX86) #elif defined(__i386__) || defined(_M_IX86)
# define FM_ARCH "x86 32-bit" # define FB_ARCH "x86 32-bit"
# define D_INT "Pentium 4 integer ALU and software 64-bit arithmetic" # define D_INT "Pentium 4 integer ALU and software 64-bit arithmetic"
# define D_FP "x87 scalar double-precision floating point" # define D_FP "x87 scalar double-precision floating point"
# define D_SIMD "SSE2: 128-bit integer vectors" # define D_SIMD "SSE2: 128-bit integer vectors"
#elif defined(__powerpc64__) #elif defined(__powerpc64__)
# define FM_ARCH "PowerPC 64-bit big-endian" # define FB_ARCH "PowerPC 64-bit big-endian"
# define D_INT "64-bit PowerPC integer ALU" # define D_INT "64-bit PowerPC integer ALU"
# define D_FP "PowerPC scalar double-precision floating point" # define D_FP "PowerPC scalar double-precision floating point"
# define D_SIMD "AltiVec: 128-bit integer vectors (PowerPC 970)" # define D_SIMD "AltiVec: 128-bit integer vectors (PowerPC 970)"
#elif defined(__powerpc__) #elif defined(__powerpc__)
# define FM_ARCH "PowerPC 32-bit big-endian" # define FB_ARCH "PowerPC 32-bit big-endian"
# define D_INT "PPC32 integer ALU and software 64-bit arithmetic" # define D_INT "PPC32 integer ALU and software 64-bit arithmetic"
# define D_FP "PowerPC scalar double-precision floating point" # define D_FP "PowerPC scalar double-precision floating point"
# define D_SIMD "runtime-selected PS, VSX, AltiVec, or scalar" # define D_SIMD "runtime-selected PS, VSX, AltiVec, or scalar"
#else #else
# define FM_ARCH "unknown" # define FB_ARCH "unknown"
# define D_INT "64-bit integer ALU" # define D_INT "64-bit integer ALU"
# define D_FP "double-precision FP" # define D_FP "double-precision FP"
# define D_SIMD "128-bit SIMD: integer + float" # define D_SIMD "128-bit SIMD: integer + float"
@@ -124,16 +124,16 @@ static double now_seconds(void)
/* ---------- the assembly kernels ---------- */ /* ---------- the assembly kernels ---------- */
extern uint64_t fm_int_math(uint64_t iters); extern uint64_t fb_int_math(uint64_t iters);
extern uint64_t fm_fp_math(uint64_t iters); extern uint64_t fb_fp_math(uint64_t iters);
extern uint64_t fm_primes(uint64_t limit, uint8_t *sieve); extern uint64_t fb_primes(uint64_t limit, uint8_t *sieve);
extern uint64_t fm_simd(uint64_t iters, void *buf); extern uint64_t fb_simd(uint64_t iters, void *buf);
extern uint64_t fm_compress(const uint8_t *src, uint64_t len, uint32_t *ht); extern uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht);
extern uint64_t fm_chacha20(uint8_t *buf, uint64_t len, extern uint64_t fb_chacha20(uint8_t *buf, uint64_t len,
const uint8_t key[32], uint64_t rounds); const uint8_t key[32], uint64_t rounds);
extern uint64_t fm_physics(double *bodies, uint64_t n, uint64_t steps); extern uint64_t fb_physics(double *bodies, uint64_t n, uint64_t steps);
extern uint64_t fm_sort(uint32_t *a, uint64_t n); extern uint64_t fb_sort(uint32_t *a, uint64_t n);
extern uint64_t fm_chase(void **ptrs, uint64_t steps); extern uint64_t fb_chase(void **ptrs, uint64_t steps);
/* ---------- tuning ---------- */ /* ---------- tuning ---------- */
@@ -166,51 +166,51 @@ extern uint64_t fm_chase(void **ptrs, uint64_t steps);
* The overall score is a WEIGHTED geometric mean of each test's rate expressed * The overall score is a WEIGHTED geometric mean of each test's rate expressed
* relative to a reference machine. Two knobs per test: * relative to a reference machine. Two knobs per test:
* *
* FM_REF_* the reference rate (this machine's measured rate). A machine * FB_REF_* the reference rate (this machine's measured rate). A machine
* matching the reference scores FM_TARGET_SCORE on that test. * matching the reference scores FB_TARGET_SCORE on that test.
* FM_WEIGHT_* how much that test counts toward the overall, by its * FB_WEIGHT_* how much that test counts toward the overall, by its
* influence on everyday user experience. Weights are relative: * influence on everyday user experience. Weights are relative:
* only their ratios matter, so they need not sum to anything - * only their ratios matter, so they need not sum to anything -
* the code normalises by their sum. (They happen to sum to 100 * the code normalises by their sum. (They happen to sum to 100
* here, so each reads as a percent.) * here, so each reads as a percent.)
* *
* Per-test score: S_i = FM_TARGET_SCORE * (rate_i / FM_REF_i) * Per-test score: S_i = FB_TARGET_SCORE * (rate_i / FB_REF_i)
* Overall score: Overall = FM_TARGET_SCORE * * Overall score: Overall = FB_TARGET_SCORE *
* exp( Sum(w_i * ln(rate_i/FM_REF_i)) / Sum(w_i) ) * exp( Sum(w_i * ln(rate_i/FB_REF_i)) / Sum(w_i) )
* *
* On the reference machine every ratio is 1, so every S_i and the overall come * On the reference machine every ratio is 1, so every S_i and the overall come
* out to exactly FM_TARGET_SCORE, regardless of the weights. Scaling is linear * out to exactly FB_TARGET_SCORE, regardless of the weights. Scaling is linear
* in performance, so far slower machines fall well below (half as fast -> half * in performance, so far slower machines fall well below (half as fast -> half
* the score) and faster future machines rise above. * the score) and faster future machines rise above.
*/ */
#define FM_TARGET_SCORE 10000.0 /* reference-machine overall */ #define FB_TARGET_SCORE 10000.0 /* reference-machine overall */
/* Reference rates: this machine, in each test's native unit (see tests[]). */ /* Reference rates: this machine, in each test's native unit (see tests[]). */
#define FM_REF_INT 3086.0 /* Mops/s */ #define FB_REF_INT 3086.0 /* Mops/s */
#define FM_REF_FP 1682.0 /* Mops/s */ #define FB_REF_FP 1682.0 /* Mops/s */
#define FM_REF_PRIMES 812.0 /* Mcand/s */ #define FB_REF_PRIMES 812.0 /* Mcand/s */
#define FM_REF_SIMD 6576.0 /* Mops/s */ #define FB_REF_SIMD 6576.0 /* Mops/s */
#define FM_REF_COMPRESS 674.0 /* MB/s */ #define FB_REF_COMPRESS 674.0 /* MB/s */
#define FM_REF_CRYPTO 406.0 /* MB/s */ #define FB_REF_CRYPTO 406.0 /* MB/s */
#define FM_REF_PHYSICS 631.0 /* Mpair/s */ #define FB_REF_PHYSICS 631.0 /* Mpair/s */
#define FM_REF_SORT 363.0 /* Mkey-cmp/s*/ #define FB_REF_SORT 363.0 /* Mkey-cmp/s*/
#define FM_REF_CHASE 79.0 /* Mhop/s (scoring); shown as ns/access */ #define FB_REF_CHASE 79.0 /* Mhop/s (scoring); shown as ns/access */
/* Weights: influence on day-to-day, common-workload user experience. /* Weights: influence on day-to-day, common-workload user experience.
* Rationale: integer/general-purpose code and memory-latency-bound * Rationale: integer/general-purpose code and memory-latency-bound
* responsiveness dominate everyday use; specialised FP/physics matter least. * responsiveness dominate everyday use; specialised FP/physics matter least.
* Roughly an 80/20 integer-vs-FP split, in the spirit of Geekbench 6's * Roughly an 80/20 integer-vs-FP split, in the spirit of Geekbench 6's
* weighted, integer-dominant methodology. Retune freely. */ * weighted, integer-dominant methodology. Retune freely. */
#define FM_WEIGHT_INT 20.0 /* general-purpose ALU: everything */ #define FB_WEIGHT_INT 20.0 /* general-purpose ALU: everything */
#define FM_WEIGHT_CHASE 16.0 /* memory latency: responsiveness */ #define FB_WEIGHT_CHASE 16.0 /* memory latency: responsiveness */
#define FM_WEIGHT_COMPRESS 14.0 /* web, storage, RAM compression */ #define FB_WEIGHT_COMPRESS 14.0 /* web, storage, RAM compression */
#define FM_WEIGHT_SORT 12.0 /* general data-structure work */ #define FB_WEIGHT_SORT 12.0 /* general data-structure work */
#define FM_WEIGHT_SIMD 11.0 /* codecs, mem/string ops, parsing */ #define FB_WEIGHT_SIMD 11.0 /* codecs, mem/string ops, parsing */
#define FM_WEIGHT_FP 9.0 /* spreadsheets, app/media math */ #define FB_WEIGHT_FP 9.0 /* spreadsheets, app/media math */
#define FM_WEIGHT_CRYPTO 8.0 /* TLS, disk encryption (small frac) */ #define FB_WEIGHT_CRYPTO 8.0 /* TLS, disk encryption (small frac) */
#define FM_WEIGHT_PRIMES 6.0 /* synthetic ALU+memory proxy */ #define FB_WEIGHT_PRIMES 6.0 /* synthetic ALU+memory proxy */
#define FM_WEIGHT_PHYSICS 4.0 /* niche simulation/games */ #define FB_WEIGHT_PHYSICS 4.0 /* niche simulation/games */
/* ---------- deterministic PRNG (splitmix64) ---------- */ /* ---------- deterministic PRNG (splitmix64) ---------- */
@@ -238,7 +238,7 @@ static void *xalloc(size_t n)
p = NULL; p = NULL;
#endif #endif
if (!p) { if (!p) {
fprintf(stderr, "fossmark: out of memory (%zu bytes)\n", n); fprintf(stderr, "fossbench: out of memory (%zu bytes)\n", n);
exit(1); exit(1);
} }
return p; return p;
@@ -331,8 +331,8 @@ static void detect_system_info(struct system_info *info)
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
info->cpu_threads = g_ncores; info->cpu_threads = g_ncores;
info->cpu_cores = g_ncores; info->cpu_cores = g_ncores;
strncpy(info->cpu, FM_ARCH, sizeof(info->cpu) - 1); strncpy(info->cpu, FB_ARCH, sizeof(info->cpu) - 1);
strncpy(info->operating_system, FM_OS, sizeof(info->operating_system) - 1); strncpy(info->operating_system, FB_OS, sizeof(info->operating_system) - 1);
#if defined(__clang__) #if defined(__clang__)
snprintf(info->compiler, sizeof(info->compiler), "Clang %s", __clang_version__); snprintf(info->compiler, sizeof(info->compiler), "Clang %s", __clang_version__);
#elif defined(__GNUC__) #elif defined(__GNUC__)
@@ -366,7 +366,7 @@ static void detect_system_info(struct system_info *info)
if (!colon) continue; if (!colon) continue;
*colon++ = '\0'; trim(line); trim(colon); *colon++ = '\0'; trim(line); trim(colon);
if ((!strcmp(line, "model name") || !strcmp(line, "Processor") || if ((!strcmp(line, "model name") || !strcmp(line, "Processor") ||
!strcmp(line, "cpu")) && info->cpu[0] && !strcmp(info->cpu, FM_ARCH)) !strcmp(line, "cpu")) && info->cpu[0] && !strcmp(info->cpu, FB_ARCH))
strncpy(info->cpu, colon, sizeof(info->cpu) - 1); strncpy(info->cpu, colon, sizeof(info->cpu) - 1);
else if (!strcmp(line, "Hardware") && cpuinfo_hardware[0] == '\0') else if (!strcmp(line, "Hardware") && cpuinfo_hardware[0] == '\0')
strncpy(cpuinfo_hardware, colon, sizeof cpuinfo_hardware - 1); strncpy(cpuinfo_hardware, colon, sizeof cpuinfo_hardware - 1);
@@ -420,7 +420,7 @@ static void detect_system_info(struct system_info *info)
size_t model_n = sizeof(info->model); size_t model_n = sizeof(info->model);
int cores = 0; size_t cn = sizeof(cores); int cores = 0; size_t cn = sizeof(cores);
if (sysctlbyname("machdep.cpu.brand_string", info->cpu, &n, NULL, 0) != 0) if (sysctlbyname("machdep.cpu.brand_string", info->cpu, &n, NULL, 0) != 0)
strncpy(info->cpu, FM_ARCH, sizeof info->cpu - 1); strncpy(info->cpu, FB_ARCH, sizeof info->cpu - 1);
sysctlbyname("hw.model", info->model, &model_n, NULL, 0); sysctlbyname("hw.model", info->model, &model_n, NULL, 0);
if (sysctlbyname("hw.physicalcpu", &cores, &cn, NULL, 0) == 0) info->cpu_cores = cores; if (sysctlbyname("hw.physicalcpu", &cores, &cn, NULL, 0) == 0) info->cpu_cores = cores;
if (sysctlbyname("hw.memsize", &mem, &mn, NULL, 0) == 0) info->memory_mb = (long)(mem / 1024 / 1024); if (sysctlbyname("hw.memsize", &mem, &mn, NULL, 0) == 0) info->memory_mb = (long)(mem / 1024 / 1024);
@@ -582,18 +582,18 @@ struct test {
static uint64_t run_int(uint64_t n, struct workspace *ws) static uint64_t run_int(uint64_t n, struct workspace *ws)
{ {
(void)ws; (void)ws;
return fm_int_math(n * 100000); return fb_int_math(n * 100000);
} }
static uint64_t run_fp(uint64_t n, struct workspace *ws) static uint64_t run_fp(uint64_t n, struct workspace *ws)
{ {
(void)ws; (void)ws;
return fm_fp_math(n * 100000); return fb_fp_math(n * 100000);
} }
static uint64_t run_primes(uint64_t n, struct workspace *ws) static uint64_t run_primes(uint64_t n, struct workspace *ws)
{ {
uint64_t c = 0; uint64_t c = 0;
for (uint64_t i = 0; i < n; i++) for (uint64_t i = 0; i < n; i++)
c += fm_primes(PRIME_LIMIT, ws->sieve); c += fb_primes(PRIME_LIMIT, ws->sieve);
return c; return c;
} }
static uint64_t run_simd(uint64_t n, struct workspace *ws) static uint64_t run_simd(uint64_t n, struct workspace *ws)
@@ -601,25 +601,25 @@ static uint64_t run_simd(uint64_t n, struct workspace *ws)
/* The kernel is allowed to use its scratch as an accumulator. Restore it /* The kernel is allowed to use its scratch as an accumulator. Restore it
* before every timed run so calibration and repeats see identical input. */ * before every timed run so calibration and repeats see identical input. */
memcpy(ws->simd_buf, g_simd_src, SIMD_BUF); memcpy(ws->simd_buf, g_simd_src, SIMD_BUF);
return fm_simd(n * 100000, ws->simd_buf); return fb_simd(n * 100000, ws->simd_buf);
} }
static uint64_t run_compress(uint64_t n, struct workspace *ws) static uint64_t run_compress(uint64_t n, struct workspace *ws)
{ {
uint64_t c = 0; uint64_t c = 0;
for (uint64_t i = 0; i < n; i++) for (uint64_t i = 0; i < n; i++)
c += fm_compress(g_corpus, COMPRESS_LEN, ws->ht); c += fb_compress(g_corpus, COMPRESS_LEN, ws->ht);
return c; return c;
} }
static uint64_t run_crypto(uint64_t n, struct workspace *ws) static uint64_t run_crypto(uint64_t n, struct workspace *ws)
{ {
return fm_chacha20(ws->cipher_buf, CIPHER_LEN, g_key, n); return fb_chacha20(ws->cipher_buf, CIPHER_LEN, g_key, n);
} }
static uint64_t run_physics(uint64_t n, struct workspace *ws) static uint64_t run_physics(uint64_t n, struct workspace *ws)
{ {
/* restore initial conditions: the integrator mutates the bodies, so /* restore initial conditions: the integrator mutates the bodies, so
* a re-run must start from the same state to be reproducible */ * a re-run must start from the same state to be reproducible */
memcpy(ws->bodies, g_bodies_src, NBODY_N * 8 * sizeof(double)); memcpy(ws->bodies, g_bodies_src, NBODY_N * 8 * sizeof(double));
return fm_physics(ws->bodies, NBODY_N, n); return fb_physics(ws->bodies, NBODY_N, n);
} }
static uint64_t run_sort(uint64_t n, struct workspace *ws) static uint64_t run_sort(uint64_t n, struct workspace *ws)
{ {
@@ -628,43 +628,43 @@ static uint64_t run_sort(uint64_t n, struct workspace *ws)
/* restore the pristine data: sorting an already-sorted array /* restore the pristine data: sorting an already-sorted array
* would measure the best case, not the real one */ * would measure the best case, not the real one */
memcpy(ws->sort_work, g_sort_src, SORT_N * sizeof(uint32_t)); memcpy(ws->sort_work, g_sort_src, SORT_N * sizeof(uint32_t));
c ^= fm_sort(ws->sort_work, SORT_N); c ^= fb_sort(ws->sort_work, SORT_N);
} }
return c; return c;
} }
static uint64_t run_chase(uint64_t n, struct workspace *ws) static uint64_t run_chase(uint64_t n, struct workspace *ws)
{ {
return fm_chase(ws->chase, n * 1000000); return fb_chase(ws->chase, n * 1000000);
} }
static const struct test tests[] = { static const struct test tests[] = {
{ "Integer Math", D_INT, { "Integer Math", D_INT,
run_int, 20, 100000.0 * 24, "Mops/s", run_int, 20, 100000.0 * 24, "Mops/s",
FM_REF_INT, FM_WEIGHT_INT }, FB_REF_INT, FB_WEIGHT_INT },
{ "Floating Point Math", D_FP, { "Floating Point Math", D_FP,
run_fp, 20, 100000.0 * 20, "Mops/s", run_fp, 20, 100000.0 * 20, "Mops/s",
FM_REF_FP, FM_WEIGHT_FP }, FB_REF_FP, FB_WEIGHT_FP },
{ "Prime Numbers", "sieve of Eratosthenes to 2M", { "Prime Numbers", "sieve of Eratosthenes to 2M",
run_primes, 1, (double)PRIME_LIMIT, "Mcand/s", run_primes, 1, (double)PRIME_LIMIT, "Mcand/s",
FM_REF_PRIMES, FM_WEIGHT_PRIMES }, FB_REF_PRIMES, FB_WEIGHT_PRIMES },
{ "Extended Instructions",D_SIMD, { "Extended Instructions",D_SIMD,
run_simd, 10, 100000.0 * 32, "Mops/s", run_simd, 10, 100000.0 * 32, "Mops/s",
FM_REF_SIMD, FM_WEIGHT_SIMD }, FB_REF_SIMD, FB_WEIGHT_SIMD },
{ "Compression", "LZ77 match finder, 4 MiB corpus", { "Compression", "LZ77 match finder, 4 MiB corpus",
run_compress, 1, (double)COMPRESS_LEN, "MB/s", run_compress, 1, (double)COMPRESS_LEN, "MB/s",
FM_REF_COMPRESS, FM_WEIGHT_COMPRESS }, FB_REF_COMPRESS, FB_WEIGHT_COMPRESS },
{ "Encryption", "ChaCha20, 20 rounds, 1 MiB", { "Encryption", "ChaCha20, 20 rounds, 1 MiB",
run_crypto, 4, (double)CIPHER_LEN, "MB/s", run_crypto, 4, (double)CIPHER_LEN, "MB/s",
FM_REF_CRYPTO, FM_WEIGHT_CRYPTO }, FB_REF_CRYPTO, FB_WEIGHT_CRYPTO },
{ "Physics", "512-body direct-sum gravity", { "Physics", "512-body direct-sum gravity",
run_physics, 4, (double)NBODY_N * NBODY_N, "Mpair/s", run_physics, 4, (double)NBODY_N * NBODY_N, "Mpair/s",
FM_REF_PHYSICS, FM_WEIGHT_PHYSICS }, FB_REF_PHYSICS, FB_WEIGHT_PHYSICS },
{ "Sorting", "heapsort, 1M uint32", { "Sorting", "heapsort, 1M uint32",
run_sort, 1, (double)SORT_N * 20, "Mkey-cmp/s", run_sort, 1, (double)SORT_N * 20, "Mkey-cmp/s",
FM_REF_SORT, FM_WEIGHT_SORT }, FB_REF_SORT, FB_WEIGHT_SORT },
{ "Memory Latency", CHASE_DETAIL, { "Memory Latency", CHASE_DETAIL,
run_chase, 1, 1000000.0, "ns/access", run_chase, 1, 1000000.0, "ns/access",
FM_REF_CHASE, FM_WEIGHT_CHASE }, FB_REF_CHASE, FB_WEIGHT_CHASE },
}; };
#define NTESTS (sizeof(tests) / sizeof(tests[0])) #define NTESTS (sizeof(tests) / sizeof(tests[0]))
@@ -773,7 +773,7 @@ static struct result run_test(const struct test *t, int threads)
if (c != checksum) { if (c != checksum) {
fprintf(stderr, fprintf(stderr,
"fossmark: %s is non-deterministic " "fossbench: %s is non-deterministic "
"(checksum %llu != %llu)\n", t->name, "(checksum %llu != %llu)\n", t->name,
(unsigned long long)c, (unsigned long long)c,
(unsigned long long)checksum); (unsigned long long)checksum);
@@ -791,7 +791,7 @@ static struct result run_test(const struct test *t, int threads)
* the same wall-clock window, so the machine's rate is their sum */ * the same wall-clock window, so the machine's rate is their sum */
r.rate = ((double)threads * (double)n * t->work_per_n) / best / 1e6; r.rate = ((double)threads * (double)n * t->work_per_n) / best / 1e6;
/* normalise against the reference machine: this is the per-test score */ /* normalise against the reference machine: this is the per-test score */
r.score = FM_TARGET_SCORE * (r.rate / t->ref_rate); r.score = FB_TARGET_SCORE * (r.rate / t->ref_rate);
return r; return r;
} }
@@ -835,11 +835,12 @@ static void json_escape(const char *src, char *dst, size_t cap)
#if !defined(_WIN32) #if !defined(_WIN32)
static int upload_results(const struct system_info *info, double score, static int upload_results(const struct system_info *info, double score,
uint64_t duration_ms) uint64_t duration_ms, const char *token)
{ {
char host[256], port[16], path[512], payload[2048], request[4096]; char host[256], port[16], path[512], payload[2048], request[4096];
char auth_header[600];
char cpu[512], model[512], os[512], compiler[256], response[512]; char cpu[512], model[512], os[512], compiler[256], response[512];
const char *base = FM_API_BASE_URL, *p, *slash, *colon; const char *base = FB_API_BASE_URL, *p, *slash, *colon;
struct addrinfo hints, *addresses = NULL, *a; struct addrinfo hints, *addresses = NULL, *a;
SSL_CTX *tls_ctx = NULL; SSL_CTX *tls_ctx = NULL;
SSL *tls = NULL; SSL *tls = NULL;
@@ -874,17 +875,29 @@ static int upload_results(const struct system_info *info, double score,
json_escape(info->model, model, sizeof(model)); json_escape(info->model, model, sizeof(model));
json_escape(info->operating_system, os, sizeof(os)); json_escape(info->operating_system, os, sizeof(os));
json_escape(info->compiler, compiler, sizeof(compiler)); json_escape(info->compiler, compiler, sizeof(compiler));
/* "fossmark_version" is the API's field name, fixed by the server
* contract; it does not track this client's own product name. */
payload_len = snprintf(payload, sizeof(payload), payload_len = snprintf(payload, sizeof(payload),
"{\"cpu\":\"%s\",\"model\":\"%s\",\"cpu_cores\":%ld,\"cpu_threads\":%ld," "{\"cpu\":\"%s\",\"model\":\"%s\",\"cpu_cores\":%ld,\"cpu_threads\":%ld,"
"\"memory_mb\":%ld,\"operating_system\":\"%s\",\"compiler\":\"%s\"," "\"memory_mb\":%ld,\"operating_system\":\"%s\",\"compiler\":\"%s\","
"\"fossmark_version\":\"%s\",\"score\":%.2f,\"duration_ms\":%llu}", "\"fossmark_version\":\"%s\",\"score\":%.2f,\"duration_ms\":%llu}",
cpu, model, info->cpu_cores, info->cpu_threads, info->memory_mb, os, compiler, cpu, model, info->cpu_cores, info->cpu_threads, info->memory_mb, os, compiler,
FM_VERSION, score, (unsigned long long)duration_ms); FB_VERSION, score, (unsigned long long)duration_ms);
if (payload_len < 0 || (size_t)payload_len >= sizeof(payload)) return 0; if (payload_len < 0 || (size_t)payload_len >= sizeof(payload)) return 0;
auth_header[0] = '\0';
if (token && token[0]) {
int n = snprintf(auth_header, sizeof(auth_header),
"Authorization: Bearer %s\r\n", token);
if (n < 0 || (size_t)n >= sizeof(auth_header)) {
fprintf(stderr, " upload error: API token too long\n");
return 0;
}
}
request_len = snprintf(request, sizeof(request), request_len = snprintf(request, sizeof(request),
"POST %s HTTP/1.1\r\nHost: %s:%s\r\nContent-Type: application/json\r\n" "POST %s HTTP/1.1\r\nHost: %s:%s\r\nContent-Type: application/json\r\n"
"Content-Length: %d\r\nConnection: close\r\n\r\n%s", "Content-Length: %d\r\nConnection: close\r\n%s\r\n%s",
path, host, port, payload_len, payload); path, host, port, payload_len, auth_header, payload);
if (request_len < 0 || (size_t)request_len >= sizeof(request)) return 0; if (request_len < 0 || (size_t)request_len >= sizeof(request)) return 0;
memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC;
@@ -931,8 +944,19 @@ static int upload_results(const struct system_info *info, double score,
if (tls) { SSL_shutdown(tls); SSL_free(tls); } if (tls) { SSL_shutdown(tls); SSL_free(tls); }
if (tls_ctx) SSL_CTX_free(tls_ctx); if (tls_ctx) SSL_CTX_free(tls_ctx);
close(fd); close(fd);
if (status == 401) {
fprintf(stderr, " upload failed: API token was rejected (HTTP 401)\n");
return 0;
}
if (status == 422) {
fprintf(stderr, " upload failed: server rejected the submission as invalid (HTTP 422)\n");
return 0;
}
if (status < 200 || status >= 300) { fprintf(stderr, " upload failed: server returned HTTP %d\n", status); return 0; } if (status < 200 || status >= 300) { fprintf(stderr, " upload failed: server returned HTTP %d\n", status); return 0; }
printf(" Results uploaded successfully (HTTP %d).\n", status); if (token)
printf(" Results uploaded and published to your profile (HTTP %d).\n", status);
else
printf(" Results uploaded, pending administrator review (HTTP %d).\n", status);
return 1; return 1;
upload_failed: upload_failed:
@@ -948,13 +972,13 @@ upload_failed:
static void print_header(const struct system_info *info) static void print_header(const struct system_info *info)
{ {
printf("\n"); printf("\n");
printf(" fossbench %s - multi-core CPU benchmark\n", FM_VERSION); printf(" fossbench %s - multi-core CPU benchmark\n", FB_VERSION);
printf(" ------------------------------------------------------------------\n"); printf(" ------------------------------------------------------------------\n");
printf(" CPU: %s\n", info->cpu); printf(" CPU: %s\n", info->cpu);
printf(" model: %s\n", info->model[0] ? info->model : "unknown"); printf(" model: %s\n", info->model[0] ? info->model : "unknown");
printf(" cores: %ld physical / %ld threads\n", info->cpu_cores, info->cpu_threads); printf(" cores: %ld physical / %ld threads\n", info->cpu_cores, info->cpu_threads);
printf(" memory: %ld MB\n", info->memory_mb); printf(" memory: %ld MB\n", info->memory_mb);
printf(" OS: %s (%s)\n", info->operating_system, FM_ARCH); printf(" OS: %s (%s)\n", info->operating_system, FB_ARCH);
printf(" compiler: %s\n", info->compiler); printf(" compiler: %s\n", info->compiler);
printf("\n"); printf("\n");
printf(" %-24s %12s %-11s %8s %9s\n", printf(" %-24s %12s %-11s %8s %9s\n",
@@ -972,18 +996,31 @@ int main(int argc, char **argv)
double benchmark_started, multicore_score, singlecore_score; double benchmark_started, multicore_score, singlecore_score;
uint64_t duration_ms; uint64_t duration_ms;
int verbose = 0; int verbose = 0;
int upload_mode = 0; /* 0 = ask, 1 = force upload, 2 = force no upload */
size_t i; size_t i;
for (i = 1; i < (size_t)argc; i++) { for (i = 1; i < (size_t)argc; i++) {
if (strcmp(argv[i], "-v") == 0 || if (strcmp(argv[i], "-v") == 0 ||
strcmp(argv[i], "--verbose") == 0) { strcmp(argv[i], "--verbose") == 0) {
verbose = 1; verbose = 1;
} else if (strcmp(argv[i], "--upload") == 0) {
if (upload_mode == 2) {
fprintf(stderr, "fossbench: --upload conflicts with --noupload\n");
return 1;
}
upload_mode = 1;
} else if (strcmp(argv[i], "--noupload") == 0) {
if (upload_mode == 1) {
fprintf(stderr, "fossbench: --noupload conflicts with --upload\n");
return 1;
}
upload_mode = 2;
} else if (strcmp(argv[i], "-h") == 0 || } else if (strcmp(argv[i], "-h") == 0 ||
strcmp(argv[i], "--help") == 0) { strcmp(argv[i], "--help") == 0) {
printf("usage: %s [-v|--verbose]\n", argv[0]); printf("usage: %s [-v|--verbose] [--upload|--noupload]\n", argv[0]);
return 0; return 0;
} else { } else {
fprintf(stderr, "fossmark: unknown option '%s'\n", fprintf(stderr, "fossbench: unknown option '%s'\n",
argv[i]); argv[i]);
return 1; return 1;
} }
@@ -1041,9 +1078,9 @@ int main(int argc, char **argv)
/* /*
* Two composite scores, each the WEIGHTED geometric mean of the per-test * Two composite scores, each the WEIGHTED geometric mean of the per-test
* scores from one pass. Per-test scores are already normalised so the * scores from one pass. Per-test scores are already normalised so the
* single-thread reference machine reads FM_TARGET_SCORE. Geometric rather * single-thread reference machine reads FB_TARGET_SCORE. Geometric rather
* than arithmetic so no single test dominates; weighted so tests count in * than arithmetic so no single test dominates; weighted so tests count in
* proportion to their influence on everyday use (the FM_WEIGHT_* config). * proportion to their influence on everyday use (the FB_WEIGHT_* config).
* The two passes share tests and weights, so MULTICORE / SINGLECORE is a * The two passes share tests and weights, so MULTICORE / SINGLECORE is a
* clean read of how much the machine gains from all its cores. * clean read of how much the machine gains from all its cores.
*/ */
@@ -1058,18 +1095,39 @@ int main(int argc, char **argv)
teardown(); teardown();
{ {
/* the token is read from the environment only: it is never echoed
* back, so it never appears in argv, shell history, or process
* listings from a command-line flag */
const char *token = getenv("FOSSBENCH_TOKEN");
int do_upload;
if (token && token[0] == '\0')
token = NULL;
if (upload_mode == 1) {
do_upload = 1;
} else if (upload_mode == 2) {
do_upload = 0;
printf(" Result was not uploaded.\n");
} else {
char answer[16]; char answer[16];
printf(" Upload this result to %s? [y/N] ", FM_API_BASE_URL); if (token)
printf(" Upload this result to %s using your API token? [y/N] ", FB_API_BASE_URL);
else
printf(" Upload this result to %s? [y/N] ", FB_API_BASE_URL);
fflush(stdout); fflush(stdout);
if (fgets(answer, sizeof(answer), stdin) && do_upload = fgets(answer, sizeof(answer), stdin) &&
(answer[0] == 'y' || answer[0] == 'Y')) { (answer[0] == 'y' || answer[0] == 'Y');
if (!do_upload)
printf(" Result was not uploaded.\n");
}
if (do_upload) {
#if defined(_WIN32) #if defined(_WIN32)
fprintf(stderr, " Upload is not yet supported on Windows.\n"); fprintf(stderr, " Upload is not yet supported on Windows.\n");
#else #else
upload_results(&system_info, multicore_score, duration_ms); upload_results(&system_info, multicore_score, duration_ms, token);
#endif #endif
} else {
printf(" Result was not uploaded.\n");
} }
} }
return 0; return 0;
+51 -51
View File
@@ -1,5 +1,5 @@
/* /*
* test_kernels.c - correctness checks for the fossmark assembly kernels * test_kernels.c - correctness checks for the fossbench assembly kernels
* *
* The benchmark's own best-of-N run guards against non-determinism, but a * The benchmark's own best-of-N run guards against non-determinism, but a
* kernel can be perfectly deterministic and still wrong. This file is the * kernel can be perfectly deterministic and still wrong. This file is the
@@ -13,7 +13,7 @@
* matter how many copies run at once; a hidden global or a reentrancy bug would * matter how many copies run at once; a hidden global or a reentrancy bug would
* survive a single-threaded run but fail here. * survive a single-threaded run but fail here.
* *
* Build: cc -O2 -pthread test_kernels.c fossmark.S -o test_kernels -lm * Build: cc -O2 -pthread test_kernels.c fossbench.S -o test_kernels -lm
* Exit status is 0 iff every check passes. * Exit status is 0 iff every check passes.
*/ */
@@ -26,36 +26,36 @@
#include <pthread.h> #include <pthread.h>
#include <unistd.h> #include <unistd.h>
extern uint64_t fm_int_math(uint64_t iters); extern uint64_t fb_int_math(uint64_t iters);
extern uint64_t fm_fp_math(uint64_t iters); extern uint64_t fb_fp_math(uint64_t iters);
extern uint64_t fm_primes(uint64_t limit, uint8_t *sieve); extern uint64_t fb_primes(uint64_t limit, uint8_t *sieve);
extern uint64_t fm_simd(uint64_t iters, void *buf); extern uint64_t fb_simd(uint64_t iters, void *buf);
extern uint64_t fm_compress(const uint8_t *src, uint64_t len, uint32_t *ht); extern uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht);
extern uint64_t fm_chacha20(uint8_t *buf, uint64_t len, extern uint64_t fb_chacha20(uint8_t *buf, uint64_t len,
const uint8_t key[32], uint64_t rounds); const uint8_t key[32], uint64_t rounds);
extern uint64_t fm_physics(double *bodies, uint64_t n, uint64_t steps); extern uint64_t fb_physics(double *bodies, uint64_t n, uint64_t steps);
extern uint64_t fm_sort(uint32_t *a, uint64_t n); extern uint64_t fb_sort(uint32_t *a, uint64_t n);
extern uint64_t fm_chase(void **ptrs, uint64_t steps); extern uint64_t fb_chase(void **ptrs, uint64_t steps);
static int failures = 0; static int failures = 0;
static int checks = 0; static int checks = 0;
/* /*
* Concurrency plumbing. Each check runs on every core at once; the counters and * Concurrency plumbing. Each check runs on every core at once; the counters and
* stdout are shared, so ok()/note() serialise on this lock. `fm_primary` is set * stdout are shared, so ok()/note() serialise on this lock. `fb_primary` is set
* on exactly one thread per check (the one running on the main thread): it owns * on exactly one thread per check (the one running on the main thread): it owns
* the human-readable output so the "[ ok ]" lines and diagnostics appear once, * the human-readable output so the "[ ok ]" lines and diagnostics appear once,
* not once per core. Every thread still evaluates every assertion, so a failure * not once per core. Every thread still evaluates every assertion, so a failure
* on any core - even a silent secondary - is reported and counted. * on any core - even a silent secondary - is reported and counted.
*/ */
static pthread_mutex_t io_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t io_lock = PTHREAD_MUTEX_INITIALIZER;
static __thread int fm_primary = 1; static __thread int fb_primary = 1;
static long fm_ncores = 1; static long fb_ncores = 1;
static void ok(const char *what, int cond) static void ok(const char *what, int cond)
{ {
pthread_mutex_lock(&io_lock); pthread_mutex_lock(&io_lock);
if (fm_primary) { if (fb_primary) {
checks++; checks++;
if (cond) { if (cond) {
printf(" [ ok ] %s\n", what); printf(" [ ok ] %s\n", what);
@@ -76,7 +76,7 @@ static void note(const char *fmt, ...)
{ {
va_list ap; va_list ap;
if (!fm_primary) if (!fb_primary)
return; return;
pthread_mutex_lock(&io_lock); pthread_mutex_lock(&io_lock);
va_start(ap, fmt); va_start(ap, fmt);
@@ -86,19 +86,19 @@ static void note(const char *fmt, ...)
} }
/* Run `check` on every core simultaneously. The main thread is the primary; /* Run `check` on every core simultaneously. The main thread is the primary;
* fm_ncores-1 workers run the same check as silent secondaries. */ * fb_ncores-1 workers run the same check as silent secondaries. */
static void *fm_worker(void *arg) static void *fb_worker(void *arg)
{ {
void (*check)(void) = *(void (**)(void))arg; void (*check)(void) = *(void (**)(void))arg;
fm_primary = 0; fb_primary = 0;
check(); check();
return NULL; return NULL;
} }
static void parallel(void (*check)(void)) static void parallel(void (*check)(void))
{ {
long extra = fm_ncores - 1; long extra = fb_ncores - 1;
pthread_t *th = NULL; pthread_t *th = NULL;
long i, spawned = 0; long i, spawned = 0;
@@ -107,7 +107,7 @@ static void parallel(void (*check)(void))
if (th) { if (th) {
for (i = 0; i < extra; i++) for (i = 0; i < extra; i++)
if (pthread_create(&th[spawned], NULL, if (pthread_create(&th[spawned], NULL,
fm_worker, &check) == 0) fb_worker, &check) == 0)
spawned++; spawned++;
} }
} }
@@ -186,39 +186,39 @@ static void check_int(void)
{ {
/* determinism and non-triviality: the checksum must be stable and /* determinism and non-triviality: the checksum must be stable and
* must actually change with the iteration count */ * must actually change with the iteration count */
uint64_t a = fm_int_math(1000); uint64_t a = fb_int_math(1000);
uint64_t b = fm_int_math(1000); uint64_t b = fb_int_math(1000);
uint64_t c = fm_int_math(2000); uint64_t c = fb_int_math(2000);
ok("int_math is deterministic", a == b); ok("int_math is deterministic", a == b);
ok("int_math depends on iters", a != c); ok("int_math depends on iters", a != c);
ok("int_math(0) is zero", fm_int_math(0) == 0); ok("int_math(0) is zero", fb_int_math(0) == 0);
} }
static void check_fp(void) static void check_fp(void)
{ {
uint64_t a = fm_fp_math(1000); uint64_t a = fb_fp_math(1000);
uint64_t b = fm_fp_math(1000); uint64_t b = fb_fp_math(1000);
double da; double da;
memcpy(&da, &a, sizeof da); memcpy(&da, &a, sizeof da);
ok("fp_math is deterministic", a == b); ok("fp_math is deterministic", a == b);
ok("fp_math result is finite", isfinite(da)); ok("fp_math result is finite", isfinite(da));
ok("fp_math(0) is zero", fm_fp_math(0) == 0); ok("fp_math(0) is zero", fb_fp_math(0) == 0);
} }
static void check_primes(void) static void check_primes(void)
{ {
enum { LIM = 1000000 }; enum { LIM = 1000000 };
uint8_t *sieve = malloc(LIM); uint8_t *sieve = malloc(LIM);
uint64_t got = fm_primes(LIM, sieve); uint64_t got = fb_primes(LIM, sieve);
uint64_t ref = ref_prime_count(LIM); uint64_t ref = ref_prime_count(LIM);
note(" primes < %d: got %llu, expected %llu\n", note(" primes < %d: got %llu, expected %llu\n",
LIM, (unsigned long long)got, (unsigned long long)ref); LIM, (unsigned long long)got, (unsigned long long)ref);
ok("primes matches reference sieve", got == ref); ok("primes matches reference sieve", got == ref);
ok("primes < 10 == 4", fm_primes(10, sieve) == 4); /* 2,3,5,7 */ ok("primes < 10 == 4", fb_primes(10, sieve) == 4); /* 2,3,5,7 */
ok("primes < 2 == 0", fm_primes(2, sieve) == 0); ok("primes < 2 == 0", fb_primes(2, sieve) == 0);
free(sieve); free(sieve);
} }
@@ -228,11 +228,11 @@ static void check_simd(void)
uint64_t a, b; uint64_t a, b;
memset(buf, 0xA5, 256); memset(buf, 0xA5, 256);
a = fm_simd(500, buf); a = fb_simd(500, buf);
memset(buf, 0xA5, 256); memset(buf, 0xA5, 256);
b = fm_simd(500, buf); b = fb_simd(500, buf);
ok("simd is deterministic", a == b); ok("simd is deterministic", a == b);
ok("simd(0) is zero", fm_simd(0, buf) == 0); ok("simd(0) is zero", fb_simd(0, buf) == 0);
free(buf); free(buf);
} }
@@ -255,18 +255,18 @@ static void check_compress(void)
src[i] = (uint8_t)(z ^ (z >> 31)); src[i] = (uint8_t)(z ^ (z >> 31));
} }
} }
incompressible = fm_compress(src, N, ht); incompressible = fb_compress(src, N, ht);
/* all-zero data is maximally compressible: it must shrink hugely */ /* all-zero data is maximally compressible: it must shrink hugely */
memset(src, 0, N); memset(src, 0, N);
compressible = fm_compress(src, N, ht); compressible = fb_compress(src, N, ht);
note(" 64KiB random -> %llu bytes, 64KiB zeros -> %llu bytes\n", note(" 64KiB random -> %llu bytes, 64KiB zeros -> %llu bytes\n",
(unsigned long long)incompressible, (unsigned long long)incompressible,
(unsigned long long)compressible); (unsigned long long)compressible);
ok("compress expands random data", incompressible >= N); ok("compress expands random data", incompressible >= N);
ok("compress shrinks constant data", compressible < N / 10); ok("compress shrinks constant data", compressible < N / 10);
ok("compress is deterministic", fm_compress(src, N, ht) == compressible); ok("compress is deterministic", fb_compress(src, N, ht) == compressible);
free(src); free(src);
free(ht); free(ht);
} }
@@ -308,7 +308,7 @@ static void check_crypto(void)
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
key[i] = (uint8_t)(i * 5 + 1); key[i] = (uint8_t)(i * 5 + 1);
memset(buf, 0, sizeof buf); /* zeros -> raw keystream */ memset(buf, 0, sizeof buf); /* zeros -> raw keystream */
fm_chacha20(buf, sizeof buf, key, 1); fb_chacha20(buf, sizeof buf, key, 1);
ref_chacha_block(ref0, key, 0, zero_nonce); ref_chacha_block(ref0, key, 0, zero_nonce);
ref_chacha_block(ref1, key, 1, zero_nonce); ref_chacha_block(ref1, key, 1, zero_nonce);
@@ -335,9 +335,9 @@ static void check_crypto(void)
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
k2[i] = (uint8_t)(i * 3); k2[i] = (uint8_t)(i * 3);
memcpy(work, plain, 128); memcpy(work, plain, 128);
fm_chacha20(work, 128, k2, 1); fb_chacha20(work, 128, k2, 1);
ok("chacha20 actually changes data", memcmp(work, plain, 128) != 0); ok("chacha20 actually changes data", memcmp(work, plain, 128) != 0);
fm_chacha20(work, 128, k2, 1); fb_chacha20(work, 128, k2, 1);
ok("chacha20 round-trips (XOR is involutive)", ok("chacha20 round-trips (XOR is involutive)",
memcmp(work, plain, 128) == 0); memcmp(work, plain, 128) == 0);
} }
@@ -353,7 +353,7 @@ static void check_physics(void)
bodies[0] = -1.0; bodies[3] = 1.0; /* body 0 at x=-1, mass 1 */ bodies[0] = -1.0; bodies[3] = 1.0; /* body 0 at x=-1, mass 1 */
bodies[8] = 1.0; bodies[11] = 1.0; /* body 1 at x=+1, mass 1 */ bodies[8] = 1.0; bodies[11] = 1.0; /* body 1 at x=+1, mass 1 */
fm_physics(bodies, 2, 200); fb_physics(bodies, 2, 200);
/* velocities must be equal and opposite (Newton's third law) */ /* velocities must be equal and opposite (Newton's third law) */
total_p = bodies[4] + bodies[12]; /* vx0 + vx1 */ total_p = bodies[4] + bodies[12]; /* vx0 + vx1 */
@@ -393,7 +393,7 @@ static void check_sort(void)
} }
memcpy(b, a, N * sizeof(uint32_t)); memcpy(b, a, N * sizeof(uint32_t));
s = fm_sort(a, N); s = fb_sort(a, N);
ok("sort produces sorted output", is_sorted(a, N)); ok("sort produces sorted output", is_sorted(a, N));
/* multiset is preserved: sort the reference with the C library and /* multiset is preserved: sort the reference with the C library and
@@ -404,12 +404,12 @@ static void check_sort(void)
/* already-sorted input stays sorted and gives the same checksum */ /* already-sorted input stays sorted and gives the same checksum */
{ {
uint64_t s2 = fm_sort(a, N); uint64_t s2 = fb_sort(a, N);
ok("sort is idempotent on sorted data", ok("sort is idempotent on sorted data",
is_sorted(a, N) && s2 == s); is_sorted(a, N) && s2 == s);
} }
ok("sort of empty array is zero", fm_sort(a, 0) == 0); ok("sort of empty array is zero", fb_sort(a, 0) == 0);
free(a); free(a);
free(b); free(b);
} }
@@ -425,25 +425,25 @@ static void check_chase(void)
nodes[2] = &nodes[3]; nodes[2] = &nodes[3];
nodes[3] = &nodes[0]; nodes[3] = &nodes[0];
/* 4 hops from &nodes[0] returns to &nodes[0]; fm_chase returns the /* 4 hops from &nodes[0] returns to &nodes[0]; fb_chase returns the
* final pointer minus the starting pointer, so a full loop gives 0 */ * final pointer minus the starting pointer, so a full loop gives 0 */
ok("chase completes a full cycle", fm_chase(nodes, 4) == 0); ok("chase completes a full cycle", fb_chase(nodes, 4) == 0);
ok("chase(0) is zero", fm_chase(nodes, 0) == 0); ok("chase(0) is zero", fb_chase(nodes, 0) == 0);
/* one hop lands on &nodes[1], i.e. one pointer-width past the start */ /* one hop lands on &nodes[1], i.e. one pointer-width past the start */
ok("chase single hop offset", ok("chase single hop offset",
fm_chase(nodes, 1) == (uint64_t)((char *)&nodes[1] - (char *)&nodes[0])); fb_chase(nodes, 1) == (uint64_t)((char *)&nodes[1] - (char *)&nodes[0]));
} }
int main(void) int main(void)
{ {
long n = sysconf(_SC_NPROCESSORS_ONLN); long n = sysconf(_SC_NPROCESSORS_ONLN);
fm_ncores = n > 0 ? n : 1; fb_ncores = n > 0 ? n : 1;
printf("\nfossmark kernel correctness tests\n"); printf("\nfossbench kernel correctness tests\n");
printf("=================================\n"); printf("=================================\n");
printf("running each check on %ld core%s in parallel\n\n", printf("running each check on %ld core%s in parallel\n\n",
fm_ncores, fm_ncores == 1 ? "" : "s"); fb_ncores, fb_ncores == 1 ? "" : "s");
printf("Integer Math:\n"); parallel(check_int); printf("Integer Math:\n"); parallel(check_int);
printf("Floating Point Math:\n"); parallel(check_fp); printf("Floating Point Math:\n"); parallel(check_fp);