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
+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
* 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__)
+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,
* 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__)
+27 -27
View File
@@ -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
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
* 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
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
* 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);