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:
@@ -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
|
||||
* external data. Every routine is a pure function of its arguments under the
|
||||
@@ -51,14 +51,14 @@
|
||||
.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
|
||||
* parallelism, mixed with high-latency serialising ops (udiv/sdiv) and the
|
||||
* bit-manipulation instructions. Returns a checksum so the compiler and the
|
||||
* driver cannot elide the work.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_int_math)
|
||||
FN_BEGIN(fb_int_math)
|
||||
cbz x0, .Lim_zero
|
||||
|
||||
MOV64(x1, 0x9E3779B97F4A7C15) /* a */
|
||||
@@ -117,18 +117,18 @@ FN_BEGIN(fm_int_math)
|
||||
.Lim_zero:
|
||||
mov x0, xzr
|
||||
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
|
||||
* multiply-add path; fdiv and fsqrt cover the non-pipelined divide/sqrt unit,
|
||||
* which is usually the real differentiator between cores.
|
||||
* Returns the result bit-cast to u64.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_fp_math)
|
||||
FN_BEGIN(fb_fp_math)
|
||||
cbz x0, .Lfp_zero
|
||||
mov x6, x0
|
||||
|
||||
@@ -187,7 +187,7 @@ FN_BEGIN(fm_fp_math)
|
||||
.Lfp_zero:
|
||||
mov x0, xzr
|
||||
ret
|
||||
FN_END(fm_fp_math)
|
||||
FN_END(fb_fp_math)
|
||||
|
||||
.p2align 4
|
||||
.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
|
||||
* 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
|
||||
* test as much as an arithmetic one.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_primes)
|
||||
FN_BEGIN(fb_primes)
|
||||
cmp x0, #2
|
||||
b.lo .Lpr_none
|
||||
|
||||
@@ -280,11 +280,11 @@ FN_BEGIN(fm_primes)
|
||||
.Lpr_none:
|
||||
mov x0, xzr
|
||||
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
|
||||
* 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.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_simd)
|
||||
FN_BEGIN(fb_simd)
|
||||
cbz x0, .Lsd_zero
|
||||
mov x6, x0
|
||||
|
||||
@@ -394,7 +394,7 @@ FN_BEGIN(fm_simd)
|
||||
.Lsd_zero:
|
||||
mov x0, xzr
|
||||
ret
|
||||
FN_END(fm_simd)
|
||||
FN_END(fb_simd)
|
||||
|
||||
.p2align 4
|
||||
.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):
|
||||
* 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.
|
||||
* Returns the encoded size in bytes.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_compress)
|
||||
FN_BEGIN(fb_compress)
|
||||
stp x29, x30, [sp, #-96]!
|
||||
mov x29, sp
|
||||
stp x19, x20, [sp, #16]
|
||||
@@ -523,11 +523,11 @@ FN_BEGIN(fm_compress)
|
||||
ldp x19, x20, [sp, #16]
|
||||
ldp x29, x30, [sp], #96
|
||||
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)
|
||||
*
|
||||
* 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
|
||||
.endm
|
||||
|
||||
FN_BEGIN(fm_chacha20)
|
||||
FN_BEGIN(fb_chacha20)
|
||||
and x1, x1, #~63 /* whole 64-byte blocks only */
|
||||
cbz x1, .Lcc_zero
|
||||
cbz x3, .Lcc_zero
|
||||
@@ -664,7 +664,7 @@ FN_BEGIN(fm_chacha20)
|
||||
.Lcc_zero:
|
||||
mov x0, xzr
|
||||
ret
|
||||
FN_END(fm_chacha20)
|
||||
FN_END(fb_chacha20)
|
||||
|
||||
.p2align 4
|
||||
.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.
|
||||
* 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.
|
||||
* Returns a checksum bit-cast from the final velocity sum.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_physics)
|
||||
FN_BEGIN(fb_physics)
|
||||
cbz x1, .Lph_zero
|
||||
cbz x2, .Lph_zero
|
||||
|
||||
@@ -807,7 +807,7 @@ FN_BEGIN(fm_physics)
|
||||
.Lph_zero:
|
||||
mov x0, xzr
|
||||
ret
|
||||
FN_END(fm_physics)
|
||||
FN_END(fb_physics)
|
||||
|
||||
.p2align 4
|
||||
.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
|
||||
* 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.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_sort)
|
||||
FN_BEGIN(fb_sort)
|
||||
cmp x1, #2
|
||||
b.lo .Lst_trivial
|
||||
|
||||
@@ -925,11 +925,11 @@ FN_BEGIN(fm_sort)
|
||||
|
||||
.Lst_sift_done:
|
||||
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
|
||||
* 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
|
||||
* "single-threaded" test in the suite.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_chase)
|
||||
FN_BEGIN(fb_chase)
|
||||
cbz x1, .Lch_zero
|
||||
mov x2, x0 /* p = ptrs */
|
||||
mov x3, x1
|
||||
@@ -953,7 +953,7 @@ FN_BEGIN(fm_chase)
|
||||
.Lch_zero:
|
||||
mov x0, xzr
|
||||
ret
|
||||
FN_END(fm_chase)
|
||||
FN_END(fb_chase)
|
||||
|
||||
|
||||
#if defined(__ELF__)
|
||||
@@ -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,
|
||||
* no libc calls and no external data relocations, so it assembles and runs
|
||||
* 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
|
||||
* 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
|
||||
* C fallback's __udivdi3 calls, now built directly into the kernel.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_int_math)
|
||||
FN_BEGIN(fb_int_math)
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push ebx
|
||||
@@ -314,11 +314,11 @@ FN_BEGIN(fm_int_math)
|
||||
pop ebx
|
||||
pop ebp
|
||||
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
|
||||
* 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
|
||||
* ST(0) that a `double` return would use.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_fp_math)
|
||||
FN_BEGIN(fb_fp_math)
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
sub esp, 8
|
||||
@@ -401,7 +401,7 @@ FN_BEGIN(fm_fp_math)
|
||||
xor edx, edx
|
||||
leave
|
||||
ret
|
||||
FN_END(fm_fp_math)
|
||||
FN_END(fb_fp_math)
|
||||
|
||||
.p2align 4
|
||||
.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 ]
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
mov ebp, esp
|
||||
push ebx
|
||||
@@ -522,11 +522,11 @@ FN_BEGIN(fm_primes)
|
||||
pop ebx
|
||||
pop ebp
|
||||
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 ]
|
||||
*
|
||||
* "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).
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_simd)
|
||||
FN_BEGIN(fb_simd)
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push esi
|
||||
@@ -645,16 +645,16 @@ FN_BEGIN(fm_simd)
|
||||
pop esi
|
||||
pop ebp
|
||||
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 ]
|
||||
*
|
||||
* 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
|
||||
* `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
|
||||
* 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
|
||||
@@ -664,7 +664,7 @@ FN_END(fm_simd)
|
||||
* ht must hold 1<<16 uint32_t (256 KiB); this routine clears it itself.
|
||||
* Returns the encoded size in bytes (edx=0).
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_compress)
|
||||
FN_BEGIN(fb_compress)
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push ebx
|
||||
@@ -788,11 +788,11 @@ FN_BEGIN(fm_compress)
|
||||
pop ebx
|
||||
pop ebp
|
||||
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.
|
||||
* 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
|
||||
movdqa xmm7, \v
|
||||
pslld \v, \n
|
||||
@@ -817,22 +817,22 @@ FN_END(fm_compress)
|
||||
.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)
|
||||
* [ [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
|
||||
* 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
|
||||
* 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
|
||||
* for the feed-forward add) and the running keystream checksum live on the
|
||||
* 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).
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_chacha20)
|
||||
FN_BEGIN(fb_chacha20)
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push ebx
|
||||
@@ -970,7 +970,7 @@ FN_BEGIN(fm_chacha20)
|
||||
pop ebx
|
||||
pop ebp
|
||||
ret
|
||||
FN_END(fm_chacha20)
|
||||
FN_END(fb_chacha20)
|
||||
|
||||
.p2align 4
|
||||
.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 ]
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* and being used in the final ax+=dx*q step. Returns a velocity checksum
|
||||
* (edx=0).
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_physics)
|
||||
FN_BEGIN(fb_physics)
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push ebx
|
||||
@@ -1158,7 +1158,7 @@ FN_BEGIN(fm_physics)
|
||||
pop ebx
|
||||
pop ebp
|
||||
ret
|
||||
FN_END(fm_physics)
|
||||
FN_END(fb_physics)
|
||||
|
||||
.p2align 4
|
||||
.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
|
||||
* 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
|
||||
* (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
|
||||
@@ -1183,7 +1183,7 @@ FN_END(fm_physics)
|
||||
* has no 64-bit rotate) - test_kernels.c only requires it be deterministic
|
||||
* and permutation-sensitive, which this is.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_sort)
|
||||
FN_BEGIN(fb_sort)
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push ebx
|
||||
@@ -1300,11 +1300,11 @@ FN_BEGIN(fm_sort)
|
||||
pop esi
|
||||
pop ebx
|
||||
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 ]
|
||||
*
|
||||
* 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]
|
||||
* offsets since esp never moves.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_chase)
|
||||
FN_BEGIN(fb_chase)
|
||||
mov ecx, [esp+8]
|
||||
or ecx, [esp+12]
|
||||
jz .Lch_zero
|
||||
@@ -1336,7 +1336,7 @@ FN_BEGIN(fm_chase)
|
||||
xor eax, eax
|
||||
xor edx, edx
|
||||
ret
|
||||
FN_END(fm_chase)
|
||||
FN_END(fb_chase)
|
||||
|
||||
|
||||
#if defined(__ELF__)
|
||||
@@ -20,7 +20,7 @@ static uint32_t rotl32(uint32_t x, unsigned 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 c = 0x94d049bb133111ebULL, d = 0x2545f4914f6cdd1dULL;
|
||||
@@ -35,7 +35,7 @@ uint64_t fm_int_math(uint64_t iters)
|
||||
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;
|
||||
uint64_t bits, i;
|
||||
@@ -52,7 +52,7 @@ uint64_t fm_fp_math(uint64_t iters)
|
||||
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;
|
||||
if (limit < 2) return 0;
|
||||
@@ -65,7 +65,7 @@ uint64_t fm_primes(uint64_t limit, uint8_t *sieve)
|
||||
}
|
||||
|
||||
#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 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
|
||||
* type here lets the compiler handle whichever PPC64 ELF ABI the system uses;
|
||||
* 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 sum = 0;
|
||||
uint64_t i;
|
||||
@@ -110,13 +110,13 @@ uint64_t fm_simd(uint64_t iters, void *memory)
|
||||
return sum;
|
||||
}
|
||||
#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. */
|
||||
extern void fm_simd_ps_kernel(uint64_t iters, void *memory);
|
||||
extern void fm_simd_vsx_kernel(uint64_t iters, void *memory);
|
||||
extern void fm_simd_altivec_kernel(uint64_t iters, void *memory);
|
||||
extern void fb_simd_ps_kernel(uint64_t iters, void *memory);
|
||||
extern void fb_simd_vsx_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)
|
||||
{
|
||||
@@ -145,7 +145,7 @@ static int device_is_nintendo(void)
|
||||
#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.
|
||||
* 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;
|
||||
|
||||
if (device_is_nintendo())
|
||||
return fm_simd_ps_kernel;
|
||||
return fb_simd_ps_kernel;
|
||||
if (hwcap & has_vsx)
|
||||
return fm_simd_vsx_kernel;
|
||||
return fb_simd_vsx_kernel;
|
||||
if (hwcap & has_altivec)
|
||||
return fm_simd_altivec_kernel;
|
||||
return fb_simd_altivec_kernel;
|
||||
#else
|
||||
if (device_is_nintendo())
|
||||
return fm_simd_ps_kernel;
|
||||
return fb_simd_ps_kernel;
|
||||
#endif
|
||||
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;
|
||||
fm_simd_kernel selected;
|
||||
fb_simd_kernel selected;
|
||||
uint32_t *v = (uint32_t *)memory;
|
||||
uint32_t sum = 0;
|
||||
unsigned j;
|
||||
@@ -179,13 +179,13 @@ uint64_t fm_simd(uint64_t iters, void *memory)
|
||||
if (!iters)
|
||||
return 0;
|
||||
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(&detected, 1, __ATOMIC_RELEASE);
|
||||
}
|
||||
selected = __atomic_load_n(&kernel, __ATOMIC_RELAXED);
|
||||
if (selected == NULL)
|
||||
return fm_simd_scalar(iters, memory);
|
||||
return fb_simd_scalar(iters, memory);
|
||||
|
||||
selected(iters, memory);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
#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};
|
||||
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
|
||||
|
||||
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;
|
||||
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; } }
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
/* 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. */
|
||||
|
||||
.text
|
||||
.align 2
|
||||
|
||||
.globl fm_simd_ps_kernel
|
||||
.type fm_simd_ps_kernel,@function
|
||||
fm_simd_ps_kernel:
|
||||
.globl fb_simd_ps_kernel
|
||||
.type fb_simd_ps_kernel,@function
|
||||
fb_simd_ps_kernel:
|
||||
/* Raw encodings keep this buildable with standard Linux binutils, whose
|
||||
* opcode tables do not always name Nintendo's Gekko/Broadway extension. */
|
||||
.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 0xf0250008 /* psq_st f1,8(r5),0,0 */
|
||||
blr
|
||||
.size fm_simd_ps_kernel,.-fm_simd_ps_kernel
|
||||
.size fb_simd_ps_kernel,.-fb_simd_ps_kernel
|
||||
|
||||
.globl fm_simd_vsx_kernel
|
||||
.type fm_simd_vsx_kernel,@function
|
||||
fm_simd_vsx_kernel:
|
||||
.globl fb_simd_vsx_kernel
|
||||
.type fb_simd_vsx_kernel,@function
|
||||
fb_simd_vsx_kernel:
|
||||
.machine power7
|
||||
li 6,0
|
||||
lxvw4x 0,6,5
|
||||
@@ -46,11 +46,11 @@ fm_simd_vsx_kernel:
|
||||
li 6,16
|
||||
stxvw4x 1,6,5
|
||||
blr
|
||||
.size fm_simd_vsx_kernel,.-fm_simd_vsx_kernel
|
||||
.size fb_simd_vsx_kernel,.-fb_simd_vsx_kernel
|
||||
|
||||
.globl fm_simd_altivec_kernel
|
||||
.type fm_simd_altivec_kernel,@function
|
||||
fm_simd_altivec_kernel:
|
||||
.globl fb_simd_altivec_kernel
|
||||
.type fb_simd_altivec_kernel,@function
|
||||
fb_simd_altivec_kernel:
|
||||
.machine altivec
|
||||
li 6,0
|
||||
lvx 0,6,5
|
||||
@@ -69,6 +69,6 @@ fm_simd_altivec_kernel:
|
||||
li 6,16
|
||||
stvx 1,6,5
|
||||
blr
|
||||
.size fm_simd_altivec_kernel,.-fm_simd_altivec_kernel
|
||||
.size fb_simd_altivec_kernel,.-fb_simd_altivec_kernel
|
||||
|
||||
.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
|
||||
* 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
|
||||
@@ -51,13 +51,13 @@
|
||||
.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
|
||||
* parallelism, mixed with the long-latency serialising ops (mul/div) and
|
||||
* bit-manipulation. Returns a checksum so nothing can be elided.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_int_math)
|
||||
FN_BEGIN(fb_int_math)
|
||||
test rdi, rdi
|
||||
jz .Lim_zero
|
||||
|
||||
@@ -138,17 +138,17 @@ FN_BEGIN(fm_int_math)
|
||||
.Lim_zero:
|
||||
xor eax, eax
|
||||
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
|
||||
* path; sqrtsd and divsd for the non-pipelined divide/sqrt unit that usually
|
||||
* separates cores. Returns the result bit-cast to u64.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_fp_math)
|
||||
FN_BEGIN(fb_fp_math)
|
||||
test rdi, rdi
|
||||
jz .Lfp_zero
|
||||
|
||||
@@ -215,7 +215,7 @@ FN_BEGIN(fm_fp_math)
|
||||
.Lfp_zero:
|
||||
xor eax, eax
|
||||
ret
|
||||
FN_END(fm_fp_math)
|
||||
FN_END(fb_fp_math)
|
||||
|
||||
.p2align 4
|
||||
.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
|
||||
* 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
|
||||
* the prime count.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_primes)
|
||||
FN_BEGIN(fb_primes)
|
||||
cmp rdi, 2
|
||||
jb .Lpr_none
|
||||
|
||||
@@ -311,11 +311,11 @@ FN_BEGIN(fm_primes)
|
||||
.Lpr_none:
|
||||
xor eax, eax
|
||||
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
|
||||
* 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.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_simd)
|
||||
FN_BEGIN(fb_simd)
|
||||
test rdi, rdi
|
||||
jz .Lsd_zero
|
||||
|
||||
@@ -429,11 +429,11 @@ FN_BEGIN(fm_simd)
|
||||
.Lsd_zero:
|
||||
xor eax, eax
|
||||
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]
|
||||
*
|
||||
* 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.
|
||||
* Returns the encoded size in bytes.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_compress)
|
||||
FN_BEGIN(fb_compress)
|
||||
push rbp
|
||||
push rbx
|
||||
push r12
|
||||
@@ -556,11 +556,11 @@ FN_BEGIN(fm_compress)
|
||||
pop rbx
|
||||
pop rbp
|
||||
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)
|
||||
* [rdi, rsi, rdx, rcx]
|
||||
*
|
||||
@@ -595,7 +595,7 @@ FN_END(fm_compress)
|
||||
pxor b, c ;\
|
||||
ROL32(b, 7)
|
||||
|
||||
FN_BEGIN(fm_chacha20)
|
||||
FN_BEGIN(fb_chacha20)
|
||||
and rsi, -64 /* whole 64-byte blocks only */
|
||||
jz .Lcc_zero
|
||||
test rcx, rcx
|
||||
@@ -693,7 +693,7 @@ FN_BEGIN(fm_chacha20)
|
||||
.Lcc_zero:
|
||||
xor eax, eax
|
||||
ret
|
||||
FN_END(fm_chacha20)
|
||||
FN_END(fb_chacha20)
|
||||
|
||||
.p2align 4
|
||||
.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]
|
||||
*
|
||||
* 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
|
||||
* divide/sqrt unit the way physics code does. Returns a velocity checksum.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_physics)
|
||||
FN_BEGIN(fb_physics)
|
||||
test rsi, rsi
|
||||
jz .Lph_zero
|
||||
test rdx, rdx
|
||||
@@ -847,7 +847,7 @@ FN_BEGIN(fm_physics)
|
||||
.Lph_zero:
|
||||
xor eax, eax
|
||||
ret
|
||||
FN_END(fm_physics)
|
||||
FN_END(fb_physics)
|
||||
|
||||
.p2align 4
|
||||
.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
|
||||
* 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
|
||||
* siftdown is reached with `call` (contract below).
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_sort)
|
||||
FN_BEGIN(fb_sort)
|
||||
cmp rsi, 2
|
||||
jb .Lst_trivial
|
||||
|
||||
@@ -961,18 +961,18 @@ FN_BEGIN(fm_sort)
|
||||
|
||||
.Lst_sift_done:
|
||||
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
|
||||
* one, so nothing can be prefetched, overlapped or reordered - this measures
|
||||
* the pure serial latency of the memory hierarchy. The truest single-threaded
|
||||
* test in the suite.
|
||||
* =================================================================== */
|
||||
FN_BEGIN(fm_chase)
|
||||
FN_BEGIN(fb_chase)
|
||||
test rsi, rsi
|
||||
jz .Lch_zero
|
||||
mov rax, rdi /* p = ptrs */
|
||||
@@ -989,7 +989,7 @@ FN_BEGIN(fm_chase)
|
||||
.Lch_zero:
|
||||
xor eax, eax
|
||||
ret
|
||||
FN_END(fm_chase)
|
||||
FN_END(fb_chase)
|
||||
|
||||
|
||||
#if defined(__ELF__)
|
||||
+156
-98
@@ -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
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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>
|
||||
@@ -38,51 +38,51 @@
|
||||
# include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
/* Change this at build time with -DFM_API_BASE_URL=\"https://host\". */
|
||||
#ifndef FM_API_BASE_URL
|
||||
# define FM_API_BASE_URL "https://fossbench.net"
|
||||
/* Change this at build time with -DFB_API_BASE_URL=\"https://host\". */
|
||||
#ifndef FB_API_BASE_URL
|
||||
# define FB_API_BASE_URL "https://fossbench.net"
|
||||
#endif
|
||||
#define FM_VERSION "0.1.4"
|
||||
#define FB_VERSION "0.1.4"
|
||||
|
||||
/* ---------- platform identification (for the banner only) ---------- */
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define FM_OS "Windows"
|
||||
# define FB_OS "Windows"
|
||||
#elif defined(__APPLE__)
|
||||
# define FM_OS "macOS"
|
||||
# define FB_OS "macOS"
|
||||
#elif defined(__linux__)
|
||||
# define FM_OS "Linux"
|
||||
# define FB_OS "Linux"
|
||||
#else
|
||||
# define FM_OS "POSIX"
|
||||
# define FB_OS "POSIX"
|
||||
#endif
|
||||
|
||||
#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_FP "double: fmadd, fdiv, fsqrt"
|
||||
# define D_SIMD "NEON ASIMD: 128-bit integer + float"
|
||||
#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_FP "double: mulsd/addsd, divsd, sqrtsd"
|
||||
# define D_SIMD "SSE2: 128-bit integer + float"
|
||||
#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_FP "x87 scalar double-precision floating point"
|
||||
# define D_SIMD "SSE2: 128-bit integer vectors"
|
||||
#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_FP "PowerPC scalar double-precision floating point"
|
||||
# define D_SIMD "AltiVec: 128-bit integer vectors (PowerPC 970)"
|
||||
#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_FP "PowerPC scalar double-precision floating point"
|
||||
# define D_SIMD "runtime-selected PS, VSX, AltiVec, or scalar"
|
||||
#else
|
||||
# define FM_ARCH "unknown"
|
||||
# define FB_ARCH "unknown"
|
||||
# define D_INT "64-bit integer ALU"
|
||||
# define D_FP "double-precision FP"
|
||||
# define D_SIMD "128-bit SIMD: integer + float"
|
||||
@@ -124,16 +124,16 @@ static double now_seconds(void)
|
||||
|
||||
/* ---------- the assembly kernels ---------- */
|
||||
|
||||
extern uint64_t fm_int_math(uint64_t iters);
|
||||
extern uint64_t fm_fp_math(uint64_t iters);
|
||||
extern uint64_t fm_primes(uint64_t limit, uint8_t *sieve);
|
||||
extern uint64_t fm_simd(uint64_t iters, void *buf);
|
||||
extern uint64_t fm_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_int_math(uint64_t iters);
|
||||
extern uint64_t fb_fp_math(uint64_t iters);
|
||||
extern uint64_t fb_primes(uint64_t limit, uint8_t *sieve);
|
||||
extern uint64_t fb_simd(uint64_t iters, void *buf);
|
||||
extern uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht);
|
||||
extern uint64_t fb_chacha20(uint8_t *buf, uint64_t len,
|
||||
const uint8_t key[32], uint64_t rounds);
|
||||
extern uint64_t fm_physics(double *bodies, uint64_t n, uint64_t steps);
|
||||
extern uint64_t fm_sort(uint32_t *a, uint64_t n);
|
||||
extern uint64_t fm_chase(void **ptrs, uint64_t steps);
|
||||
extern uint64_t fb_physics(double *bodies, uint64_t n, uint64_t steps);
|
||||
extern uint64_t fb_sort(uint32_t *a, uint64_t n);
|
||||
extern uint64_t fb_chase(void **ptrs, uint64_t steps);
|
||||
|
||||
/* ---------- 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
|
||||
* relative to a reference machine. Two knobs per test:
|
||||
*
|
||||
* FM_REF_* the reference rate (this machine's measured rate). A machine
|
||||
* matching the reference scores FM_TARGET_SCORE on that test.
|
||||
* FM_WEIGHT_* how much that test counts toward the overall, by its
|
||||
* FB_REF_* the reference rate (this machine's measured rate). A machine
|
||||
* matching the reference scores FB_TARGET_SCORE on that test.
|
||||
* FB_WEIGHT_* how much that test counts toward the overall, by its
|
||||
* influence on everyday user experience. Weights are relative:
|
||||
* only their ratios matter, so they need not sum to anything -
|
||||
* the code normalises by their sum. (They happen to sum to 100
|
||||
* here, so each reads as a percent.)
|
||||
*
|
||||
* Per-test score: S_i = FM_TARGET_SCORE * (rate_i / FM_REF_i)
|
||||
* Overall score: Overall = FM_TARGET_SCORE *
|
||||
* exp( Sum(w_i * ln(rate_i/FM_REF_i)) / Sum(w_i) )
|
||||
* Per-test score: S_i = FB_TARGET_SCORE * (rate_i / FB_REF_i)
|
||||
* Overall score: Overall = FB_TARGET_SCORE *
|
||||
* 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
|
||||
* 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
|
||||
* 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[]). */
|
||||
#define FM_REF_INT 3086.0 /* Mops/s */
|
||||
#define FM_REF_FP 1682.0 /* Mops/s */
|
||||
#define FM_REF_PRIMES 812.0 /* Mcand/s */
|
||||
#define FM_REF_SIMD 6576.0 /* Mops/s */
|
||||
#define FM_REF_COMPRESS 674.0 /* MB/s */
|
||||
#define FM_REF_CRYPTO 406.0 /* MB/s */
|
||||
#define FM_REF_PHYSICS 631.0 /* Mpair/s */
|
||||
#define FM_REF_SORT 363.0 /* Mkey-cmp/s*/
|
||||
#define FM_REF_CHASE 79.0 /* Mhop/s (scoring); shown as ns/access */
|
||||
#define FB_REF_INT 3086.0 /* Mops/s */
|
||||
#define FB_REF_FP 1682.0 /* Mops/s */
|
||||
#define FB_REF_PRIMES 812.0 /* Mcand/s */
|
||||
#define FB_REF_SIMD 6576.0 /* Mops/s */
|
||||
#define FB_REF_COMPRESS 674.0 /* MB/s */
|
||||
#define FB_REF_CRYPTO 406.0 /* MB/s */
|
||||
#define FB_REF_PHYSICS 631.0 /* Mpair/s */
|
||||
#define FB_REF_SORT 363.0 /* Mkey-cmp/s*/
|
||||
#define FB_REF_CHASE 79.0 /* Mhop/s (scoring); shown as ns/access */
|
||||
|
||||
/* Weights: influence on day-to-day, common-workload user experience.
|
||||
* Rationale: integer/general-purpose code and memory-latency-bound
|
||||
* responsiveness dominate everyday use; specialised FP/physics matter least.
|
||||
* Roughly an 80/20 integer-vs-FP split, in the spirit of Geekbench 6's
|
||||
* weighted, integer-dominant methodology. Retune freely. */
|
||||
#define FM_WEIGHT_INT 20.0 /* general-purpose ALU: everything */
|
||||
#define FM_WEIGHT_CHASE 16.0 /* memory latency: responsiveness */
|
||||
#define FM_WEIGHT_COMPRESS 14.0 /* web, storage, RAM compression */
|
||||
#define FM_WEIGHT_SORT 12.0 /* general data-structure work */
|
||||
#define FM_WEIGHT_SIMD 11.0 /* codecs, mem/string ops, parsing */
|
||||
#define FM_WEIGHT_FP 9.0 /* spreadsheets, app/media math */
|
||||
#define FM_WEIGHT_CRYPTO 8.0 /* TLS, disk encryption (small frac) */
|
||||
#define FM_WEIGHT_PRIMES 6.0 /* synthetic ALU+memory proxy */
|
||||
#define FM_WEIGHT_PHYSICS 4.0 /* niche simulation/games */
|
||||
#define FB_WEIGHT_INT 20.0 /* general-purpose ALU: everything */
|
||||
#define FB_WEIGHT_CHASE 16.0 /* memory latency: responsiveness */
|
||||
#define FB_WEIGHT_COMPRESS 14.0 /* web, storage, RAM compression */
|
||||
#define FB_WEIGHT_SORT 12.0 /* general data-structure work */
|
||||
#define FB_WEIGHT_SIMD 11.0 /* codecs, mem/string ops, parsing */
|
||||
#define FB_WEIGHT_FP 9.0 /* spreadsheets, app/media math */
|
||||
#define FB_WEIGHT_CRYPTO 8.0 /* TLS, disk encryption (small frac) */
|
||||
#define FB_WEIGHT_PRIMES 6.0 /* synthetic ALU+memory proxy */
|
||||
#define FB_WEIGHT_PHYSICS 4.0 /* niche simulation/games */
|
||||
|
||||
/* ---------- deterministic PRNG (splitmix64) ---------- */
|
||||
|
||||
@@ -238,7 +238,7 @@ static void *xalloc(size_t n)
|
||||
p = NULL;
|
||||
#endif
|
||||
if (!p) {
|
||||
fprintf(stderr, "fossmark: out of memory (%zu bytes)\n", n);
|
||||
fprintf(stderr, "fossbench: out of memory (%zu bytes)\n", n);
|
||||
exit(1);
|
||||
}
|
||||
return p;
|
||||
@@ -331,8 +331,8 @@ static void detect_system_info(struct system_info *info)
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->cpu_threads = g_ncores;
|
||||
info->cpu_cores = g_ncores;
|
||||
strncpy(info->cpu, FM_ARCH, sizeof(info->cpu) - 1);
|
||||
strncpy(info->operating_system, FM_OS, sizeof(info->operating_system) - 1);
|
||||
strncpy(info->cpu, FB_ARCH, sizeof(info->cpu) - 1);
|
||||
strncpy(info->operating_system, FB_OS, sizeof(info->operating_system) - 1);
|
||||
#if defined(__clang__)
|
||||
snprintf(info->compiler, sizeof(info->compiler), "Clang %s", __clang_version__);
|
||||
#elif defined(__GNUC__)
|
||||
@@ -366,7 +366,7 @@ static void detect_system_info(struct system_info *info)
|
||||
if (!colon) continue;
|
||||
*colon++ = '\0'; trim(line); trim(colon);
|
||||
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);
|
||||
else if (!strcmp(line, "Hardware") && cpuinfo_hardware[0] == '\0')
|
||||
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);
|
||||
int cores = 0; size_t cn = sizeof(cores);
|
||||
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);
|
||||
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);
|
||||
@@ -582,18 +582,18 @@ struct test {
|
||||
static uint64_t run_int(uint64_t n, struct workspace *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)
|
||||
{
|
||||
(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)
|
||||
{
|
||||
uint64_t c = 0;
|
||||
for (uint64_t i = 0; i < n; i++)
|
||||
c += fm_primes(PRIME_LIMIT, ws->sieve);
|
||||
c += fb_primes(PRIME_LIMIT, ws->sieve);
|
||||
return c;
|
||||
}
|
||||
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
|
||||
* before every timed run so calibration and repeats see identical input. */
|
||||
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)
|
||||
{
|
||||
uint64_t c = 0;
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
/* restore initial conditions: the integrator mutates the bodies, so
|
||||
* a re-run must start from the same state to be reproducible */
|
||||
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)
|
||||
{
|
||||
@@ -628,43 +628,43 @@ static uint64_t run_sort(uint64_t n, struct workspace *ws)
|
||||
/* restore the pristine data: sorting an already-sorted array
|
||||
* would measure the best case, not the real one */
|
||||
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;
|
||||
}
|
||||
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[] = {
|
||||
{ "Integer Math", D_INT,
|
||||
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,
|
||||
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",
|
||||
run_primes, 1, (double)PRIME_LIMIT, "Mcand/s",
|
||||
FM_REF_PRIMES, FM_WEIGHT_PRIMES },
|
||||
FB_REF_PRIMES, FB_WEIGHT_PRIMES },
|
||||
{ "Extended Instructions",D_SIMD,
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
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,
|
||||
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]))
|
||||
@@ -773,7 +773,7 @@ static struct result run_test(const struct test *t, int threads)
|
||||
|
||||
if (c != checksum) {
|
||||
fprintf(stderr,
|
||||
"fossmark: %s is non-deterministic "
|
||||
"fossbench: %s is non-deterministic "
|
||||
"(checksum %llu != %llu)\n", t->name,
|
||||
(unsigned long long)c,
|
||||
(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 */
|
||||
r.rate = ((double)threads * (double)n * t->work_per_n) / best / 1e6;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -835,11 +835,12 @@ static void json_escape(const char *src, char *dst, size_t cap)
|
||||
|
||||
#if !defined(_WIN32)
|
||||
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 auth_header[600];
|
||||
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;
|
||||
SSL_CTX *tls_ctx = 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->operating_system, os, sizeof(os));
|
||||
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),
|
||||
"{\"cpu\":\"%s\",\"model\":\"%s\",\"cpu_cores\":%ld,\"cpu_threads\":%ld,"
|
||||
"\"memory_mb\":%ld,\"operating_system\":\"%s\",\"compiler\":\"%s\","
|
||||
"\"fossmark_version\":\"%s\",\"score\":%.2f,\"duration_ms\":%llu}",
|
||||
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;
|
||||
|
||||
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),
|
||||
"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",
|
||||
path, host, port, payload_len, payload);
|
||||
"Content-Length: %d\r\nConnection: close\r\n%s\r\n%s",
|
||||
path, host, port, payload_len, auth_header, payload);
|
||||
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;
|
||||
@@ -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_ctx) SSL_CTX_free(tls_ctx);
|
||||
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; }
|
||||
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;
|
||||
|
||||
upload_failed:
|
||||
@@ -948,13 +972,13 @@ upload_failed:
|
||||
static void print_header(const struct system_info *info)
|
||||
{
|
||||
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(" CPU: %s\n", info->cpu);
|
||||
printf(" model: %s\n", info->model[0] ? info->model : "unknown");
|
||||
printf(" cores: %ld physical / %ld threads\n", info->cpu_cores, info->cpu_threads);
|
||||
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("\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;
|
||||
uint64_t duration_ms;
|
||||
int verbose = 0;
|
||||
int upload_mode = 0; /* 0 = ask, 1 = force upload, 2 = force no upload */
|
||||
size_t i;
|
||||
|
||||
for (i = 1; i < (size_t)argc; i++) {
|
||||
if (strcmp(argv[i], "-v") == 0 ||
|
||||
strcmp(argv[i], "--verbose") == 0) {
|
||||
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 ||
|
||||
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;
|
||||
} else {
|
||||
fprintf(stderr, "fossmark: unknown option '%s'\n",
|
||||
fprintf(stderr, "fossbench: unknown option '%s'\n",
|
||||
argv[i]);
|
||||
return 1;
|
||||
}
|
||||
@@ -1041,9 +1078,9 @@ int main(int argc, char **argv)
|
||||
/*
|
||||
* Two composite scores, each the WEIGHTED geometric mean of the per-test
|
||||
* 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
|
||||
* 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
|
||||
* clean read of how much the machine gains from all its cores.
|
||||
*/
|
||||
@@ -1058,18 +1095,39 @@ int main(int argc, char **argv)
|
||||
teardown();
|
||||
|
||||
{
|
||||
char answer[16];
|
||||
printf(" Upload this result to %s? [y/N] ", FM_API_BASE_URL);
|
||||
fflush(stdout);
|
||||
if (fgets(answer, sizeof(answer), stdin) &&
|
||||
(answer[0] == 'y' || answer[0] == 'Y')) {
|
||||
/* 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];
|
||||
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);
|
||||
do_upload = fgets(answer, sizeof(answer), stdin) &&
|
||||
(answer[0] == 'y' || answer[0] == 'Y');
|
||||
if (!do_upload)
|
||||
printf(" Result was not uploaded.\n");
|
||||
}
|
||||
|
||||
if (do_upload) {
|
||||
#if defined(_WIN32)
|
||||
fprintf(stderr, " Upload is not yet supported on Windows.\n");
|
||||
#else
|
||||
upload_results(&system_info, multicore_score, duration_ms);
|
||||
upload_results(&system_info, multicore_score, duration_ms, token);
|
||||
#endif
|
||||
} else {
|
||||
printf(" Result was not uploaded.\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
+51
-51
@@ -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
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -26,36 +26,36 @@
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern uint64_t fm_int_math(uint64_t iters);
|
||||
extern uint64_t fm_fp_math(uint64_t iters);
|
||||
extern uint64_t fm_primes(uint64_t limit, uint8_t *sieve);
|
||||
extern uint64_t fm_simd(uint64_t iters, void *buf);
|
||||
extern uint64_t fm_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_int_math(uint64_t iters);
|
||||
extern uint64_t fb_fp_math(uint64_t iters);
|
||||
extern uint64_t fb_primes(uint64_t limit, uint8_t *sieve);
|
||||
extern uint64_t fb_simd(uint64_t iters, void *buf);
|
||||
extern uint64_t fb_compress(const uint8_t *src, uint64_t len, uint32_t *ht);
|
||||
extern uint64_t fb_chacha20(uint8_t *buf, uint64_t len,
|
||||
const uint8_t key[32], uint64_t rounds);
|
||||
extern uint64_t fm_physics(double *bodies, uint64_t n, uint64_t steps);
|
||||
extern uint64_t fm_sort(uint32_t *a, uint64_t n);
|
||||
extern uint64_t fm_chase(void **ptrs, uint64_t steps);
|
||||
extern uint64_t fb_physics(double *bodies, uint64_t n, uint64_t steps);
|
||||
extern uint64_t fb_sort(uint32_t *a, uint64_t n);
|
||||
extern uint64_t fb_chase(void **ptrs, uint64_t steps);
|
||||
|
||||
static int failures = 0;
|
||||
static int checks = 0;
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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
|
||||
* on any core - even a silent secondary - is reported and counted.
|
||||
*/
|
||||
static pthread_mutex_t io_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static __thread int fm_primary = 1;
|
||||
static long fm_ncores = 1;
|
||||
static __thread int fb_primary = 1;
|
||||
static long fb_ncores = 1;
|
||||
|
||||
static void ok(const char *what, int cond)
|
||||
{
|
||||
pthread_mutex_lock(&io_lock);
|
||||
if (fm_primary) {
|
||||
if (fb_primary) {
|
||||
checks++;
|
||||
if (cond) {
|
||||
printf(" [ ok ] %s\n", what);
|
||||
@@ -76,7 +76,7 @@ static void note(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!fm_primary)
|
||||
if (!fb_primary)
|
||||
return;
|
||||
pthread_mutex_lock(&io_lock);
|
||||
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;
|
||||
* fm_ncores-1 workers run the same check as silent secondaries. */
|
||||
static void *fm_worker(void *arg)
|
||||
* fb_ncores-1 workers run the same check as silent secondaries. */
|
||||
static void *fb_worker(void *arg)
|
||||
{
|
||||
void (*check)(void) = *(void (**)(void))arg;
|
||||
|
||||
fm_primary = 0;
|
||||
fb_primary = 0;
|
||||
check();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void parallel(void (*check)(void))
|
||||
{
|
||||
long extra = fm_ncores - 1;
|
||||
long extra = fb_ncores - 1;
|
||||
pthread_t *th = NULL;
|
||||
long i, spawned = 0;
|
||||
|
||||
@@ -107,7 +107,7 @@ static void parallel(void (*check)(void))
|
||||
if (th) {
|
||||
for (i = 0; i < extra; i++)
|
||||
if (pthread_create(&th[spawned], NULL,
|
||||
fm_worker, &check) == 0)
|
||||
fb_worker, &check) == 0)
|
||||
spawned++;
|
||||
}
|
||||
}
|
||||
@@ -186,39 +186,39 @@ static void check_int(void)
|
||||
{
|
||||
/* determinism and non-triviality: the checksum must be stable and
|
||||
* must actually change with the iteration count */
|
||||
uint64_t a = fm_int_math(1000);
|
||||
uint64_t b = fm_int_math(1000);
|
||||
uint64_t c = fm_int_math(2000);
|
||||
uint64_t a = fb_int_math(1000);
|
||||
uint64_t b = fb_int_math(1000);
|
||||
uint64_t c = fb_int_math(2000);
|
||||
|
||||
ok("int_math is deterministic", a == b);
|
||||
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)
|
||||
{
|
||||
uint64_t a = fm_fp_math(1000);
|
||||
uint64_t b = fm_fp_math(1000);
|
||||
uint64_t a = fb_fp_math(1000);
|
||||
uint64_t b = fb_fp_math(1000);
|
||||
double da;
|
||||
|
||||
memcpy(&da, &a, sizeof da);
|
||||
ok("fp_math is deterministic", a == b);
|
||||
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)
|
||||
{
|
||||
enum { LIM = 1000000 };
|
||||
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);
|
||||
|
||||
note(" primes < %d: got %llu, expected %llu\n",
|
||||
LIM, (unsigned long long)got, (unsigned long long)ref);
|
||||
ok("primes matches reference sieve", got == ref);
|
||||
ok("primes < 10 == 4", fm_primes(10, sieve) == 4); /* 2,3,5,7 */
|
||||
ok("primes < 2 == 0", fm_primes(2, sieve) == 0);
|
||||
ok("primes < 10 == 4", fb_primes(10, sieve) == 4); /* 2,3,5,7 */
|
||||
ok("primes < 2 == 0", fb_primes(2, sieve) == 0);
|
||||
free(sieve);
|
||||
}
|
||||
|
||||
@@ -228,11 +228,11 @@ static void check_simd(void)
|
||||
uint64_t a, b;
|
||||
|
||||
memset(buf, 0xA5, 256);
|
||||
a = fm_simd(500, buf);
|
||||
a = fb_simd(500, buf);
|
||||
memset(buf, 0xA5, 256);
|
||||
b = fm_simd(500, buf);
|
||||
b = fb_simd(500, buf);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -255,18 +255,18 @@ static void check_compress(void)
|
||||
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 */
|
||||
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",
|
||||
(unsigned long long)incompressible,
|
||||
(unsigned long long)compressible);
|
||||
ok("compress expands random data", incompressible >= N);
|
||||
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(ht);
|
||||
}
|
||||
@@ -308,7 +308,7 @@ static void check_crypto(void)
|
||||
for (i = 0; i < 32; i++)
|
||||
key[i] = (uint8_t)(i * 5 + 1);
|
||||
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(ref1, key, 1, zero_nonce);
|
||||
@@ -335,9 +335,9 @@ static void check_crypto(void)
|
||||
for (i = 0; i < 32; i++)
|
||||
k2[i] = (uint8_t)(i * 3);
|
||||
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);
|
||||
fm_chacha20(work, 128, k2, 1);
|
||||
fb_chacha20(work, 128, k2, 1);
|
||||
ok("chacha20 round-trips (XOR is involutive)",
|
||||
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[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) */
|
||||
total_p = bodies[4] + bodies[12]; /* vx0 + vx1 */
|
||||
@@ -393,7 +393,7 @@ static void check_sort(void)
|
||||
}
|
||||
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));
|
||||
|
||||
/* 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 */
|
||||
{
|
||||
uint64_t s2 = fm_sort(a, N);
|
||||
uint64_t s2 = fb_sort(a, N);
|
||||
ok("sort is idempotent on sorted data",
|
||||
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(b);
|
||||
}
|
||||
@@ -425,25 +425,25 @@ static void check_chase(void)
|
||||
nodes[2] = &nodes[3];
|
||||
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 */
|
||||
ok("chase completes a full cycle", fm_chase(nodes, 4) == 0);
|
||||
ok("chase(0) is zero", fm_chase(nodes, 0) == 0);
|
||||
ok("chase completes a full cycle", fb_chase(nodes, 4) == 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 */
|
||||
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)
|
||||
{
|
||||
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("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("Floating Point Math:\n"); parallel(check_fp);
|
||||
|
||||
Reference in New Issue
Block a user