weee
This commit is contained in:
@@ -2,17 +2,17 @@
|
||||
# Use make for the current computer, or a named target for another one.
|
||||
|
||||
CC ?= cc
|
||||
CFLAGS ?= -O2 -Wall -Wextra
|
||||
CFLAGS ?= -O3 -flto -funroll-loops -Wall -Wextra
|
||||
LDLIBS ?= -lm
|
||||
# Needed for the worker threads.
|
||||
PTHREAD := -pthread
|
||||
|
||||
DIST := dist
|
||||
DRIVER := src/main.c src/app/benchmark.c src/app/hw_detect.c
|
||||
DRIVER := src/main.c src/app/benchmark.c src/app/hw_detect.c src/kernels/fossbench-portable.c
|
||||
DRIVER_DEPS := src/app/benchmark.h src/app/hw_detect.h src/app/upload.c
|
||||
ASM_ARM64 := src/kernels/fossbench-arm64.S
|
||||
ASM_AMD64 := src/kernels/fossbench-amd64.S
|
||||
ASM_I386 := src/kernels/fossbench-i386.S
|
||||
SRC_I386 := src/kernels/fossbench-i386.c
|
||||
ASM_PPC32 := src/kernels/fossbench-ppc32be.S
|
||||
SRC_PPC32 := src/kernels/fossbench-ppc32be-chacha.c
|
||||
ASM_PPC64 := src/kernels/fossbench-ppc64be.S
|
||||
@@ -29,7 +29,7 @@ else ifneq (,$(filter x86_64 amd64,$(HOST_ARCH)))
|
||||
HOST_KERNEL := $(ASM_AMD64)
|
||||
else ifneq (,$(filter i386 i486 i586 i686 x86,$(HOST_ARCH)))
|
||||
HOST_ARCHNAME := i386
|
||||
HOST_KERNEL := $(ASM_I386)
|
||||
HOST_KERNEL := $(SRC_I386)
|
||||
else ifneq (,$(filter ppc64le powerpc64le,$(HOST_ARCH)))
|
||||
HOST_ARCHNAME := ppc64le
|
||||
HOST_KERNEL := $(ASM_PPC64LE)
|
||||
@@ -47,8 +47,8 @@ else
|
||||
$(error unsupported host architecture '$(HOST_ARCH)')
|
||||
endif
|
||||
ifeq ($(HOST_ARCHNAME),i386)
|
||||
# Keep the old i386 target simple and non-PIE.
|
||||
CFLAGS += -march=pentium4 -fno-pie
|
||||
# Keep the i386 target compatible with the original 80386 ISA.
|
||||
CFLAGS += -march=i386 -fno-pie
|
||||
LDFLAGS += -no-pie
|
||||
endif
|
||||
ifeq ($(HOST_ARCHNAME),ppc64be)
|
||||
@@ -157,8 +157,8 @@ $(DIST)/fossbench-linux-amd64: $(DRIVER) $(DRIVER_DEPS) $(ASM_AMD64) | $(DIST)
|
||||
$(CC_AMD64) $(CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS)
|
||||
@echo "built $@"
|
||||
|
||||
$(DIST)/fossbench-linux-i386: $(DRIVER) $(DRIVER_DEPS) $(ASM_I386) | $(DIST)
|
||||
$(CC_I386) -m32 -march=pentium4 -fno-pie -no-pie $(CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_I386) $(LDLIBS)
|
||||
$(DIST)/fossbench-linux-i386: $(DRIVER) $(DRIVER_DEPS) $(SRC_I386) | $(DIST)
|
||||
$(CC_I386) -m32 -march=i386 -fno-pie -no-pie $(CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_I386) $(LDLIBS)
|
||||
@echo "built $@"
|
||||
|
||||
$(DIST)/fossbench-linux-ppc32be: $(DRIVER) $(DRIVER_DEPS) $(ASM_PPC32) $(SRC_PPC32) | $(DIST)
|
||||
@@ -190,8 +190,8 @@ $(DIST)/fossbench-windows-amd64.exe: $(DRIVER) $(DRIVER_DEPS) $(ASM_AMD64) | $(D
|
||||
$(CC_WINDOWS_AMD64) $(CFLAGS) $(PTHREAD) -static -o $@ $(DRIVER) $(ASM_AMD64) -lm -lwinhttp -ladvapi32
|
||||
@echo "built $@"
|
||||
|
||||
$(DIST)/fossbench-windows-i386.exe: $(DRIVER) $(DRIVER_DEPS) $(ASM_I386) | $(DIST)
|
||||
$(CC_WINDOWS_I386) -march=pentium4 $(WINDOWS_I386_XP_CFLAGS) $(CFLAGS) -static $(WINDOWS_I386_XP_LDFLAGS) -o $@ $(DRIVER) $(ASM_I386) -lm -lwinhttp -ladvapi32
|
||||
$(DIST)/fossbench-windows-i386.exe: $(DRIVER) $(DRIVER_DEPS) $(SRC_I386) | $(DIST)
|
||||
$(CC_WINDOWS_I386) -march=i386 $(WINDOWS_I386_XP_CFLAGS) $(CFLAGS) -static $(WINDOWS_I386_XP_LDFLAGS) -o $@ $(DRIVER) $(SRC_I386) -lm -lwinhttp -ladvapi32
|
||||
@echo "built $@"
|
||||
|
||||
# Add a native rule if one was not already made above.
|
||||
|
||||
@@ -26,7 +26,7 @@ score weights. Version 0.1 and 0.2 scores are not comparable.
|
||||
|---|---|
|
||||
| ARM64 | ARMv8-A with NEON |
|
||||
| x86-64 | x86-64 with SSE2 |
|
||||
| x86 32-bit | i386 with SSE2 |
|
||||
| x86 32-bit | original i386 ISA (scalar fallback) |
|
||||
| PowerPC 32-bit, big-endian | scalar fallback with runtime-selected extensions |
|
||||
| PowerPC 64-bit, big-endian | PowerPC 970 with AltiVec |
|
||||
| PowerPC 32-bit, little-endian | scalar fallback with runtime-selected extensions |
|
||||
@@ -171,7 +171,7 @@ src/app/benchmark.h interface used by main.c
|
||||
src/app/upload.c API payload and network transport
|
||||
src/kernels/fossbench-arm64.S ARM64 kernels
|
||||
src/kernels/fossbench-amd64.S x86-64 kernels
|
||||
src/kernels/fossbench-i386.S i386 kernels
|
||||
src/kernels/fossbench-i386.c portable scalar i386 kernels
|
||||
src/kernels/fossbench-ppc32be.S 32-bit big-endian PowerPC kernels
|
||||
src/kernels/fossbench-ppc64be.S 64-bit big-endian PowerPC kernels
|
||||
src/kernels/fossbench-ppc32le.S 32-bit little-endian PowerPC kernels
|
||||
|
||||
+65
-20
@@ -38,7 +38,7 @@
|
||||
#ifndef FB_API_BASE_URL
|
||||
# define FB_API_BASE_URL "http://fossbench.net"
|
||||
#endif
|
||||
#define FB_VERSION "0.3.0"
|
||||
#define FB_VERSION "0.4.0"
|
||||
|
||||
/*
|
||||
* Native Integer Math and Memory Bandwidth are the only two *measured*
|
||||
@@ -70,9 +70,9 @@
|
||||
# define D_FP "double: mulsd/addsd, divsd, sqrtsd"
|
||||
# define D_SIMD "SSE2: 128-bit integer + float"
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
# define D_INT "Pentium 4 integer ALU and software 64-bit arithmetic"
|
||||
# define D_INT "i386 integer ALU and software 64-bit arithmetic"
|
||||
# define D_FP "x87 scalar double-precision floating point"
|
||||
# define D_SIMD "SSE2: 128-bit integer vectors"
|
||||
# define D_SIMD "scalar extended-instruction fallback"
|
||||
#elif defined(__powerpc64__)
|
||||
# define D_INT "64-bit PowerPC integer ALU"
|
||||
# define D_FP "PowerPC scalar double-precision floating point"
|
||||
@@ -132,6 +132,18 @@ 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);
|
||||
|
||||
/* GCC/Clang optimised C copies used for the REAL score. */
|
||||
extern uint64_t fb_c_int_math(uint64_t iters);
|
||||
extern uint64_t fb_c_fp_math(uint64_t iters);
|
||||
extern uint64_t fb_c_primes(uint64_t limit, uint8_t *sieve);
|
||||
extern uint64_t fb_c_simd(uint64_t iters, void *buf);
|
||||
extern uint64_t fb_c_compress(const uint8_t *src, uint64_t len, uint32_t *ht);
|
||||
extern uint64_t fb_c_chacha20(uint8_t *buf, uint64_t len,
|
||||
const uint8_t key[32], uint64_t rounds);
|
||||
extern uint64_t fb_c_physics(double *bodies, uint64_t n, uint64_t steps);
|
||||
extern uint64_t fb_c_sort(uint32_t *a, uint64_t n);
|
||||
extern uint64_t fb_c_chase(void **ptrs, uint64_t steps);
|
||||
|
||||
/* Test sizes and timing settings. */
|
||||
|
||||
#define PRIME_LIMIT (2u * 1000u * 1000u) /* Prime test size. */
|
||||
@@ -320,6 +332,7 @@ static uint8_t *g_cipher_src; /* pristine plaintext, copied per-core. */
|
||||
static uint8_t *g_simd_src; /* pristine NEON seed, copied per-core. */
|
||||
static double *g_bodies_src; /* pristine initial conditions. */
|
||||
static uint32_t *g_sort_src; /* pristine unsorted data. */
|
||||
static int g_c_backend; /* zero = RAW assembly, one = REAL C. */
|
||||
|
||||
|
||||
/* Synthesise a compressible corpus. */
|
||||
@@ -466,7 +479,7 @@ struct test {
|
||||
static uint64_t run_int(uint64_t n, struct workspace *ws)
|
||||
{
|
||||
(void)ws;
|
||||
return fb_int_math(n * 100000);
|
||||
return g_c_backend ? fb_c_int_math(n * 100000) : fb_int_math(n * 100000);
|
||||
}
|
||||
|
||||
FB_SCALAR_KERNEL
|
||||
@@ -492,37 +505,42 @@ static uint64_t run_int32(uint64_t n, struct workspace *ws)
|
||||
static uint64_t run_fp(uint64_t n, struct workspace *ws)
|
||||
{
|
||||
(void)ws;
|
||||
return fb_fp_math(n * 100000);
|
||||
return g_c_backend ? fb_c_fp_math(n * 100000) : 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 += fb_primes(PRIME_LIMIT, ws->sieve);
|
||||
c += g_c_backend ? fb_c_primes(PRIME_LIMIT, ws->sieve) :
|
||||
fb_primes(PRIME_LIMIT, ws->sieve);
|
||||
return c;
|
||||
}
|
||||
static uint64_t run_simd(uint64_t n, struct workspace *ws)
|
||||
{
|
||||
/* Reset the SIMD buffer before running. */
|
||||
memcpy(ws->simd_buf, g_simd_src, SIMD_BUF);
|
||||
return fb_simd(n * 100000, ws->simd_buf);
|
||||
return g_c_backend ? fb_c_simd(n * 100000, ws->simd_buf) :
|
||||
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 += fb_compress(g_corpus, COMPRESS_LEN, ws->ht);
|
||||
c += g_c_backend ? fb_c_compress(g_corpus, COMPRESS_LEN, ws->ht) :
|
||||
fb_compress(g_corpus, COMPRESS_LEN, ws->ht);
|
||||
return c;
|
||||
}
|
||||
static uint64_t run_crypto(uint64_t n, struct workspace *ws)
|
||||
{
|
||||
return fb_chacha20(ws->cipher_buf, CIPHER_LEN, g_key, n);
|
||||
return g_c_backend ? fb_c_chacha20(ws->cipher_buf, CIPHER_LEN, g_key, n) :
|
||||
fb_chacha20(ws->cipher_buf, CIPHER_LEN, g_key, n);
|
||||
}
|
||||
static uint64_t run_physics(uint64_t n, struct workspace *ws)
|
||||
{
|
||||
/* Reset the physics data before running. */
|
||||
memcpy(ws->bodies, g_bodies_src, NBODY_N * 8 * sizeof(double));
|
||||
return fb_physics(ws->bodies, NBODY_N, n);
|
||||
return g_c_backend ? fb_c_physics(ws->bodies, NBODY_N, n) :
|
||||
fb_physics(ws->bodies, NBODY_N, n);
|
||||
}
|
||||
static uint64_t run_sort(uint64_t n, struct workspace *ws)
|
||||
{
|
||||
@@ -530,13 +548,15 @@ static uint64_t run_sort(uint64_t n, struct workspace *ws)
|
||||
for (uint64_t i = 0; i < n; i++) {
|
||||
/* Reset the sort data before running. */
|
||||
memcpy(ws->sort_work, g_sort_src, SORT_N * sizeof(uint32_t));
|
||||
c ^= fb_sort(ws->sort_work, SORT_N);
|
||||
c ^= g_c_backend ? fb_c_sort(ws->sort_work, SORT_N) :
|
||||
fb_sort(ws->sort_work, SORT_N);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
static uint64_t run_chase(uint64_t n, struct workspace *ws)
|
||||
{
|
||||
return fb_chase(ws->chase, n * 1000000);
|
||||
return g_c_backend ? fb_c_chase(ws->chase, n * 1000000) :
|
||||
fb_chase(ws->chase, n * 1000000);
|
||||
}
|
||||
|
||||
FB_SCALAR_KERNEL
|
||||
@@ -751,6 +771,8 @@ static void print_header(const struct system_info *info)
|
||||
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(" caches: L1 %ld KB / L2 %ld KB / L3 %ld KB\n",
|
||||
info->l1_cache_kb, info->l2_cache_kb, info->l3_cache_kb);
|
||||
printf(" OS: %s (%s)\n", info->operating_system, hw_arch_name());
|
||||
printf(" kernel: %s\n", info->kernel[0] ? info->kernel : "unknown");
|
||||
printf(" compiler: %s\n", info->compiler);
|
||||
@@ -763,7 +785,8 @@ static void print_header(const struct system_info *info)
|
||||
|
||||
int fossbench_run(int verbose, int upload_mode, int system_check)
|
||||
{
|
||||
struct result multi[NTESTS], single[NTESTS];
|
||||
struct result raw_multi[NTESTS], raw_single[NTESTS];
|
||||
struct result real_multi[NTESTS], real_single[NTESTS];
|
||||
struct system_info system_info;
|
||||
struct background_metrics background;
|
||||
double benchmark_started;
|
||||
@@ -795,26 +818,47 @@ int fossbench_run(int verbose, int upload_mode, int system_check)
|
||||
|
||||
print_header(&system_info);
|
||||
|
||||
printf(" RAW score suite (architecture assembly)\n");
|
||||
g_c_backend = 0;
|
||||
for (i = 0; i < NTESTS; i++) {
|
||||
printf(" %-24s", tests[i].name);
|
||||
fflush(stdout);
|
||||
|
||||
/* Run every test with all cores and one core. */
|
||||
multi[i] = run_test(&tests[i], (int)g_ncores);
|
||||
single[i] = run_test(&tests[i], 1);
|
||||
raw_multi[i] = run_test(&tests[i], (int)g_ncores);
|
||||
raw_single[i] = run_test(&tests[i], 1);
|
||||
|
||||
printf(" %12.1f %-11s %7.2fs\n",
|
||||
display_metric(&tests[i], &multi[i]), tests[i].unit,
|
||||
multi[i].seconds);
|
||||
display_metric(&tests[i], &raw_multi[i]), tests[i].unit,
|
||||
raw_multi[i].seconds);
|
||||
if (verbose)
|
||||
printf(" %-24s %s\n"
|
||||
" %-24s 1-core: %.1f %s %ld-core: %.1f %s\n",
|
||||
"", tests[i].detail, "",
|
||||
display_metric(&tests[i], &single[i]), tests[i].unit,
|
||||
g_ncores, display_metric(&tests[i], &multi[i]), tests[i].unit);
|
||||
display_metric(&tests[i], &raw_single[i]), tests[i].unit,
|
||||
g_ncores, display_metric(&tests[i], &raw_multi[i]), tests[i].unit);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
printf(" --------------------------------------------------------------------------\n");
|
||||
printf(" REAL score suite (optimised portable C)\n");
|
||||
g_c_backend = 1;
|
||||
for (i = 0; i < NTESTS; i++) {
|
||||
printf(" %-24s", tests[i].name);
|
||||
fflush(stdout);
|
||||
real_multi[i] = run_test(&tests[i], (int)g_ncores);
|
||||
real_single[i] = run_test(&tests[i], 1);
|
||||
printf(" %12.1f %-11s %7.2fs\n",
|
||||
display_metric(&tests[i], &real_multi[i]), tests[i].unit,
|
||||
real_multi[i].seconds);
|
||||
if (verbose)
|
||||
printf(" %-24s %s\n"
|
||||
" %-24s 1-core: %.1f %s %ld-core: %.1f %s\n",
|
||||
"", tests[i].detail, "",
|
||||
display_metric(&tests[i], &real_single[i]), tests[i].unit,
|
||||
g_ncores, display_metric(&tests[i], &real_multi[i]), tests[i].unit);
|
||||
}
|
||||
|
||||
printf(" --------------------------------------------------------------------------\n");
|
||||
|
||||
duration_ms = (uint64_t)((now_seconds() - benchmark_started) * 1000.0);
|
||||
@@ -845,7 +889,8 @@ int fossbench_run(int verbose, int upload_mode, int system_check)
|
||||
#if defined(FB_NO_UPLOAD)
|
||||
fprintf(stderr, " Upload support is disabled in this build.\n");
|
||||
#else
|
||||
upload_results(&system_info, multi, single, duration_ms, &background);
|
||||
upload_results(&system_info, raw_multi, raw_single,
|
||||
real_multi, real_single, duration_ms, &background);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,39 @@ static int apple_m_name(const char *text, char *dst, size_t cap)
|
||||
|
||||
/* Linux device trees identify Apple Silicon by its SoC code. */
|
||||
#if defined(__linux__)
|
||||
static int read_first_property(const char *path, char *dst, size_t cap);
|
||||
|
||||
static long cache_size_kb(const char *text)
|
||||
{
|
||||
char *end;
|
||||
long value = strtol(text, &end, 10);
|
||||
if (value <= 0) return 0;
|
||||
while (*end && isspace((unsigned char)*end)) end++;
|
||||
if (*end == 'M' || *end == 'm') value *= 1024;
|
||||
return value;
|
||||
}
|
||||
|
||||
static void linux_detect_caches(struct system_info *info)
|
||||
{
|
||||
int index;
|
||||
for (index = 0; index < 32; index++) {
|
||||
char path[160], level_text[32], size_text[32];
|
||||
long level, kb;
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/devices/system/cpu/cpu0/cache/index%d/level", index);
|
||||
if (!read_first_property(path, level_text, sizeof level_text)) continue;
|
||||
snprintf(path, sizeof path,
|
||||
"/sys/devices/system/cpu/cpu0/cache/index%d/size", index);
|
||||
if (!read_first_property(path, size_text, sizeof size_text)) continue;
|
||||
level = strtol(level_text, NULL, 10);
|
||||
kb = cache_size_kb(size_text);
|
||||
/* L1 instruction and data caches are separate and should be added. */
|
||||
if (level == 1) info->l1_cache_kb += kb;
|
||||
else if (level == 2 && kb > info->l2_cache_kb) info->l2_cache_kb = kb;
|
||||
else if (level == 3 && kb > info->l3_cache_kb) info->l3_cache_kb = kb;
|
||||
}
|
||||
}
|
||||
|
||||
static int apple_m_name_from_soc(const char *text, char *dst, size_t cap)
|
||||
{
|
||||
static const struct { const char *soc, *name; } chips[] = {
|
||||
@@ -505,6 +538,7 @@ void hw_detect_system(struct system_info *info)
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
linux_detect_caches(info);
|
||||
{
|
||||
FILE *f = fopen("/etc/os-release", "r"); char line[512];
|
||||
if (f) { while (fgets(line, sizeof(line), f)) if (!strncmp(line, "PRETTY_NAME=", 12)) {
|
||||
@@ -540,6 +574,20 @@ void hw_detect_system(struct system_info *info)
|
||||
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);
|
||||
{
|
||||
uint64_t bytes = 0; size_t bytes_n = sizeof bytes;
|
||||
if (sysctlbyname("hw.l1dcachesize", &bytes, &bytes_n, NULL, 0) == 0)
|
||||
info->l1_cache_kb += (long)(bytes / 1024);
|
||||
bytes = 0; bytes_n = sizeof bytes;
|
||||
if (sysctlbyname("hw.l1icachesize", &bytes, &bytes_n, NULL, 0) == 0)
|
||||
info->l1_cache_kb += (long)(bytes / 1024);
|
||||
bytes = 0; bytes_n = sizeof bytes;
|
||||
if (sysctlbyname("hw.l2cachesize", &bytes, &bytes_n, NULL, 0) == 0)
|
||||
info->l2_cache_kb = (long)(bytes / 1024);
|
||||
bytes = 0; bytes_n = sizeof bytes;
|
||||
if (sysctlbyname("hw.l3cachesize", &bytes, &bytes_n, NULL, 0) == 0)
|
||||
info->l3_cache_kb = (long)(bytes / 1024);
|
||||
}
|
||||
}
|
||||
{
|
||||
char product[64] = ""; size_t pn = sizeof(product);
|
||||
@@ -550,6 +598,30 @@ void hw_detect_system(struct system_info *info)
|
||||
snprintf(info->kernel, sizeof(info->kernel), "%.62s %.64s", u.sysname, u.release);
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
{
|
||||
DWORD bytes = 0;
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION entries = NULL;
|
||||
if (!GetLogicalProcessorInformation(NULL, &bytes) &&
|
||||
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
entries = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(bytes);
|
||||
if (entries && GetLogicalProcessorInformation(entries, &bytes)) {
|
||||
DWORD i, count = bytes / sizeof *entries;
|
||||
long cores = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (entries[i].Relationship == RelationProcessorCore) cores++;
|
||||
else if (entries[i].Relationship == RelationCache) {
|
||||
CACHE_DESCRIPTOR c = entries[i].Cache;
|
||||
long kb = (long)(c.Size / 1024);
|
||||
if (c.Level == 1 && kb > info->l1_cache_kb) info->l1_cache_kb = kb;
|
||||
else if (c.Level == 2 && kb > info->l2_cache_kb) info->l2_cache_kb = kb;
|
||||
else if (c.Level == 3 && kb > info->l3_cache_kb) info->l3_cache_kb = kb;
|
||||
}
|
||||
}
|
||||
if (cores > 0) info->cpu_cores = cores;
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
}
|
||||
{
|
||||
HKEY key;
|
||||
char brand[sizeof info->cpu] = "";
|
||||
@@ -571,6 +643,19 @@ void hw_detect_system(struct system_info *info)
|
||||
if (cores > 0)
|
||||
info->cpu_cores = cores;
|
||||
}
|
||||
{
|
||||
HKEY key; char product[sizeof info->model] = "";
|
||||
DWORD type = 0, bytes = sizeof product;
|
||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
|
||||
"HARDWARE\\DESCRIPTION\\System\\BIOS", 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
|
||||
if (RegQueryValueExA(key, "SystemProductName", NULL, &type,
|
||||
(BYTE *)product, &bytes) == ERROR_SUCCESS && type == REG_SZ) {
|
||||
product[sizeof product - 1] = '\0'; trim(product);
|
||||
if (product[0]) snprintf(info->model, sizeof info->model, "%s", product);
|
||||
}
|
||||
RegCloseKey(key);
|
||||
}
|
||||
}
|
||||
{
|
||||
MEMORYSTATUSEX ms;
|
||||
ms.dwLength = sizeof(ms);
|
||||
@@ -617,4 +702,33 @@ void hw_detect_system(struct system_info *info)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
/* Portable POSIX fallbacks cover BSDs and other Unix systems where the
|
||||
* platform-specific branches above are unavailable. */
|
||||
#if !defined(_WIN32) && !defined(__linux__) && !defined(__APPLE__)
|
||||
{
|
||||
long pages = sysconf(_SC_PHYS_PAGES), page_size = sysconf(_SC_PAGESIZE);
|
||||
if (pages > 0 && page_size > 0) info->memory_mb = (pages / 1024) * (page_size / 1024);
|
||||
}
|
||||
# if defined(_SC_LEVEL1_DCACHE_SIZE)
|
||||
{ long v = sysconf(_SC_LEVEL1_DCACHE_SIZE); if (v > 0) info->l1_cache_kb += v / 1024; }
|
||||
# endif
|
||||
# if defined(_SC_LEVEL1_ICACHE_SIZE)
|
||||
{ long v = sysconf(_SC_LEVEL1_ICACHE_SIZE); if (v > 0) info->l1_cache_kb += v / 1024; }
|
||||
# endif
|
||||
# if defined(_SC_LEVEL2_CACHE_SIZE)
|
||||
{ long v = sysconf(_SC_LEVEL2_CACHE_SIZE); if (v > 0) info->l2_cache_kb = v / 1024; }
|
||||
# endif
|
||||
# if defined(_SC_LEVEL3_CACHE_SIZE)
|
||||
{ long v = sysconf(_SC_LEVEL3_CACHE_SIZE); if (v > 0) info->l3_cache_kb = v / 1024; }
|
||||
# endif
|
||||
#endif
|
||||
/* Never emit empty identity fields: these values are used by the website
|
||||
* for processor matching and display on platforms with sparse APIs. */
|
||||
if (!info->cpu[0]) snprintf(info->cpu, sizeof info->cpu, "%s", HW_ARCH);
|
||||
if (!info->model[0]) snprintf(info->model, sizeof info->model, "%s", info->cpu);
|
||||
if (!info->kernel[0]) snprintf(info->kernel, sizeof info->kernel, "%s", info->operating_system);
|
||||
if (info->memory_mb < 1) info->memory_mb = 1;
|
||||
if (info->l1_cache_kb < 1) info->l1_cache_kb = 1;
|
||||
if (info->l2_cache_kb < 1) info->l2_cache_kb = info->l1_cache_kb;
|
||||
if (info->l3_cache_kb < 1) info->l3_cache_kb = info->l2_cache_kb;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ struct system_info {
|
||||
long cpu_cores;
|
||||
long cpu_threads;
|
||||
long memory_mb;
|
||||
long l1_cache_kb;
|
||||
long l2_cache_kb;
|
||||
long l3_cache_kb;
|
||||
};
|
||||
|
||||
const char *hw_arch_name(void);
|
||||
|
||||
+50
-32
@@ -120,18 +120,52 @@ done:
|
||||
}
|
||||
#endif
|
||||
|
||||
static int upload_results(const struct system_info *info,
|
||||
static int append_result_tests(char *payload, size_t cap, size_t *used,
|
||||
const struct result *multi,
|
||||
const struct result *single, uint64_t duration_ms,
|
||||
const struct result *single)
|
||||
{
|
||||
static const char *ids[] = {
|
||||
"native_integer", "wide_integer", "floating_point", "primes",
|
||||
"extended_instructions", "compression", "encryption", "physics",
|
||||
"sorting", "memory_latency", "memory_bandwidth"
|
||||
};
|
||||
size_t i;
|
||||
for (i = 0; i < NTESTS; i++) {
|
||||
int n = snprintf(payload + *used, cap - *used,
|
||||
"%s{\"id\":\"%s\",\"name\":\"%s\",\"detail\":\"%s\",\"unit\":\"%s\","
|
||||
"\"start_iterations\":%llu,\"work_per_iteration\":%.17g,"
|
||||
"\"multicore\":{\"display_metric\":%.17g,\"rate\":%.17g,"
|
||||
"\"seconds\":%.17g,\"iterations\":%llu,\"threads\":%d,\"checksum\":\"%llu\"},"
|
||||
"\"singlecore\":{\"display_metric\":%.17g,\"rate\":%.17g,"
|
||||
"\"seconds\":%.17g,\"iterations\":%llu,\"threads\":%d,\"checksum\":\"%llu\"}}",
|
||||
i ? "," : "", ids[i], tests[i].name, tests[i].detail, tests[i].unit,
|
||||
(unsigned long long)tests[i].start_n, tests[i].work_per_n,
|
||||
display_metric(&tests[i], &multi[i]), multi[i].rate, multi[i].seconds,
|
||||
(unsigned long long)multi[i].iters, multi[i].threads,
|
||||
(unsigned long long)multi[i].checksum,
|
||||
display_metric(&tests[i], &single[i]), single[i].rate, single[i].seconds,
|
||||
(unsigned long long)single[i].iters, single[i].threads,
|
||||
(unsigned long long)single[i].checksum);
|
||||
if (n < 0 || (size_t)n >= cap - *used) return 0;
|
||||
*used += (size_t)n;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int upload_results(const struct system_info *info,
|
||||
const struct result *raw_multi,
|
||||
const struct result *raw_single,
|
||||
const struct result *real_multi,
|
||||
const struct result *real_single, uint64_t duration_ms,
|
||||
const struct background_metrics *background)
|
||||
{
|
||||
char host[256], port[16], path[512], payload[16384];
|
||||
char host[256], port[16], path[512], payload[32768];
|
||||
char auth_header[600], response_body[2048], claim_url[1024], result_url[1024];
|
||||
char cpu[512], model[512], os[512], compiler[256], kernel[256];
|
||||
const char *base = FB_API_BASE_URL, *p, *slash, *colon;
|
||||
int status = 0, payload_len;
|
||||
#if !defined(_WIN32)
|
||||
char request[20000], response[4096];
|
||||
char request[40000], response[4096];
|
||||
struct addrinfo hints, *addresses = NULL, *a;
|
||||
int fd = -1, request_len;
|
||||
#endif
|
||||
@@ -167,46 +201,30 @@ static int upload_results(const struct system_info *info,
|
||||
/* The server still calls this field fossmark_version. */
|
||||
payload_len = snprintf(payload, sizeof(payload),
|
||||
"{\"cpu\":\"%s\",\"model\":\"%s\",\"cpu_cores\":%ld,\"cpu_threads\":%ld,"
|
||||
"\"architecture\":\"%s\",\"l1_cache_kb\":%ld,\"l2_cache_kb\":%ld,\"l3_cache_kb\":%ld,"
|
||||
"\"memory_mb\":%ld,\"operating_system\":\"%s\",\"compiler\":\"%s\","
|
||||
"\"fossmark_version\":\"%s\",\"workload_suite\":\"fossbench-cpu-v1\","
|
||||
"\"fossmark_version\":\"%s\",\"workload_suite\":\"fossbench-cpu-v2\","
|
||||
"\"duration_ms\":%llu,\"score_details\":{"
|
||||
"\"minimum_test_seconds\":%.17g,\"repeats\":%d,"
|
||||
"\"system_environment\":{\"kernel\":\"%s\",\"sample_seconds\":%d,"
|
||||
"\"background_cpu_average_percent\":%.17g,\"background_cpu_peak_percent\":%.17g,"
|
||||
"\"available_memory_mb\":%ld,\"process_count\":%ld},\"tests\":[",
|
||||
cpu, model, info->cpu_cores, info->cpu_threads, info->memory_mb, os, compiler,
|
||||
"\"available_memory_mb\":%ld,\"process_count\":%ld},\"raw_tests\":[",
|
||||
cpu, model, info->cpu_cores, info->cpu_threads, hw_arch_name(),
|
||||
info->l1_cache_kb, info->l2_cache_kb, info->l3_cache_kb,
|
||||
info->memory_mb, os, compiler,
|
||||
FB_VERSION, (unsigned long long)duration_ms, MIN_SECONDS, REPEATS, kernel, background->samples,
|
||||
background->average_cpu_percent, background->peak_cpu_percent,
|
||||
background->available_memory_mb, background->process_count);
|
||||
if (payload_len < 0 || (size_t)payload_len >= sizeof(payload)) return 0;
|
||||
{
|
||||
size_t used = (size_t)payload_len;
|
||||
size_t i;
|
||||
for (i = 0; i < NTESTS; i++) {
|
||||
static const char *ids[] = {
|
||||
"native_integer", "wide_integer", "floating_point", "primes",
|
||||
"extended_instructions", "compression", "encryption", "physics",
|
||||
"sorting", "memory_latency", "memory_bandwidth"
|
||||
};
|
||||
int n = snprintf(payload + used, sizeof(payload) - used,
|
||||
"%s{\"id\":\"%s\",\"name\":\"%s\",\"detail\":\"%s\",\"unit\":\"%s\","
|
||||
"\"start_iterations\":%llu,\"work_per_iteration\":%.17g,"
|
||||
"\"multicore\":{\"display_metric\":%.17g,\"rate\":%.17g,"
|
||||
"\"seconds\":%.17g,\"iterations\":%llu,\"threads\":%d,\"checksum\":\"%llu\"},"
|
||||
"\"singlecore\":{\"display_metric\":%.17g,\"rate\":%.17g,"
|
||||
"\"seconds\":%.17g,\"iterations\":%llu,\"threads\":%d,\"checksum\":\"%llu\"}}",
|
||||
i ? "," : "", ids[i], tests[i].name, tests[i].detail, tests[i].unit,
|
||||
(unsigned long long)tests[i].start_n, tests[i].work_per_n,
|
||||
display_metric(&tests[i], &multi[i]), multi[i].rate,
|
||||
multi[i].seconds, (unsigned long long)multi[i].iters, multi[i].threads,
|
||||
(unsigned long long)multi[i].checksum,
|
||||
display_metric(&tests[i], &single[i]), single[i].rate,
|
||||
single[i].seconds, (unsigned long long)single[i].iters, single[i].threads,
|
||||
(unsigned long long)single[i].checksum);
|
||||
if (n < 0 || (size_t)n >= sizeof(payload) - used) return 0;
|
||||
int n;
|
||||
if (!append_result_tests(payload, sizeof payload, &used, raw_multi, raw_single)) return 0;
|
||||
n = snprintf(payload + used, sizeof payload - used, "],\"real_tests\":[");
|
||||
if (n < 0 || (size_t)n >= sizeof payload - used) return 0;
|
||||
used += (size_t)n;
|
||||
}
|
||||
if (used + 3 >= sizeof(payload)) return 0;
|
||||
if (!append_result_tests(payload, sizeof payload, &used, real_multi, real_single)) return 0;
|
||||
if (used + 3 >= sizeof payload) return 0;
|
||||
memcpy(payload + used, "]}}", 4);
|
||||
payload_len = (int)(used + 3);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/* Portable scalar backend for the baseline 32-bit x86 target. */
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static uint32_t rotl32(uint32_t x, unsigned n)
|
||||
{
|
||||
return (x << n) | (x >> (32 - n));
|
||||
}
|
||||
|
||||
uint64_t fb_int_math(uint64_t iters)
|
||||
{
|
||||
uint64_t a=0x9e3779b97f4a7c15ULL,b=0xbf58476d1ce4e5b9ULL;
|
||||
uint64_t c=0x94d049bb133111ebULL,d=0x2545f4914f6cdd1dULL,i;
|
||||
if (!iters) return 0;
|
||||
for (i=0;i<iters;i++) {
|
||||
a=a*0xdeadbeefU+b; b=b*0xdeadbeefU+c;
|
||||
c=c*0xdeadbeefU+d; d=d*0xdeadbeefU+a;
|
||||
a^=c>>29; b^=d<<17; c^=(a>>31)|(a<<33); d^=b>>7;
|
||||
a+=c/0xdeadbeefU; b+=d/0xdeadbeefU;
|
||||
}
|
||||
return a^b^c^d;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!iters) return 0;
|
||||
for (i=0;i<iters;i++) {
|
||||
a=fmin(a*1.0625+.0009765625,2.0);
|
||||
b=fmin(b*1.0625+.0009765625,2.0);
|
||||
c=fmin(c*1.0625+.0009765625,2.0)+sqrt(a);
|
||||
d=fmax(fabs(fmin(d*1.0625+.0009765625,2.0)+sqrt(b)),1.0);
|
||||
a+=1.0/(c+1.0); b+=1.0/(d+1.0);
|
||||
}
|
||||
out=a+b+c+d; memcpy(&bits,&out,sizeof bits); return bits;
|
||||
}
|
||||
|
||||
uint64_t fb_primes(uint64_t limit, uint8_t *sieve)
|
||||
{
|
||||
uint64_t i,j,count=0;
|
||||
if (limit<2) return 0;
|
||||
memset(sieve,0,(size_t)limit); sieve[0]=sieve[1]=1;
|
||||
for (i=2;i<=(limit-1)/i;i++)
|
||||
if (!sieve[i]) for (j=i*i;j<limit;j+=i) sieve[j]=1;
|
||||
for (i=2;i<limit;i++) count+=!sieve[i];
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t fb_simd(uint64_t iters, void *memory)
|
||||
{
|
||||
uint32_t *v=(uint32_t *)memory,a[8],sum=0; uint64_t i; unsigned j;
|
||||
if (!iters) return 0;
|
||||
memcpy(a,v,sizeof a);
|
||||
for (i=0;i<iters;i++)
|
||||
for (j=0;j<8;j++) a[j]=rotl32(a[j]+a[(j+1)&7]*(j+3),(j+5)&31);
|
||||
for (j=0;j<8;j++) sum^=a[j];
|
||||
memcpy(v,a,sizeof a); return sum;
|
||||
}
|
||||
|
||||
static uint32_t load32_native(const uint8_t *p)
|
||||
{
|
||||
uint32_t v; memcpy(&v,p,sizeof v); return v;
|
||||
}
|
||||
|
||||
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);
|
||||
if (len<16) return len+1;
|
||||
while (ip<len-12) {
|
||||
uint32_t seq=load32_native(src+ip);
|
||||
uint32_t h=(uint32_t)(seq*2654435761U)>>16;
|
||||
ref=ht[h]; ht[h]=(uint32_t)ip;
|
||||
if (ref>=ip||ip-ref>=65536||load32_native(src+ref)!=seq) { ip++; continue; }
|
||||
for (ml=4;ip+ml<len&&src[ip+ml]==src[ref+ml];ml++) {}
|
||||
lit=ip-anchor; out+=lit+3+(lit>=15)+(ml>=19); ip+=ml; anchor=ip;
|
||||
}
|
||||
return out+(len-anchor)+1;
|
||||
}
|
||||
|
||||
static uint32_t load32le(const uint8_t *p)
|
||||
{
|
||||
return (uint32_t)p[0]|(uint32_t)p[1]<<8|(uint32_t)p[2]<<16|(uint32_t)p[3]<<24;
|
||||
}
|
||||
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 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;
|
||||
len&=~(uint64_t)63; if (!len||!passes) return 0;
|
||||
memcpy(base,sigma,16); for(i=0;i<8;i++) base[4+i]=load32le(key+4*i);
|
||||
base[13]=base[14]=base[15]=0;
|
||||
for(pass=0;pass<passes;pass++) for(off=0;off<len;off+=64) {
|
||||
base[12]=counter++; memcpy(x,base,sizeof x);
|
||||
for(r=0;r<10;r++) {
|
||||
QR(x[0],x[4],x[8],x[12]); QR(x[1],x[5],x[9],x[13]);
|
||||
QR(x[2],x[6],x[10],x[14]); QR(x[3],x[7],x[11],x[15]);
|
||||
QR(x[0],x[5],x[10],x[15]); QR(x[1],x[6],x[11],x[12]);
|
||||
QR(x[2],x[7],x[8],x[13]); QR(x[3],x[4],x[9],x[14]);
|
||||
}
|
||||
for(i=0;i<16;i++) { uint32_t k=x[i]+base[i]; uint8_t t[4];
|
||||
store32le(t,k); buf[off+4*i]^=t[0]; buf[off+4*i+1]^=t[1];
|
||||
buf[off+4*i+2]^=t[2]; buf[off+4*i+3]^=t[3]; checksum^=k; }
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
#undef QR
|
||||
|
||||
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;
|
||||
for(s=0;s<steps;s++) {
|
||||
for(i=0;i<n;i++) { double ax=0,ay=0,az=0;
|
||||
for(j=0;j<n;j++) { double dx=b[8*j]-b[8*i],dy=b[8*j+1]-b[8*i+1],dz=b[8*j+2]-b[8*i+2]; double q=1.0/sqrt(dx*dx+dy*dy+dz*dz+.0625); q=q*q*q*b[8*j+3]; ax+=dx*q; ay+=dy*q; az+=dz*q; }
|
||||
b[8*i+4]+=ax*.0078125; b[8*i+5]+=ay*.0078125; b[8*i+6]+=az*.0078125;
|
||||
}
|
||||
for(i=0;i<n;i++) { b[8*i]+=b[8*i+4]*.0078125; b[8*i+1]+=b[8*i+5]*.0078125; b[8*i+2]+=b[8*i+6]*.0078125; }
|
||||
}
|
||||
for(i=0;i<n;i++) sum+=b[8*i+4]+b[8*i+5]+b[8*i+6];
|
||||
memcpy(&bits,&sum,sizeof bits); return bits;
|
||||
}
|
||||
|
||||
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 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--) sift(a,i-1,n);
|
||||
for(end=n-1;end;end--) { t=a[0];a[0]=a[end];a[end]=t;sift(a,0,end); }
|
||||
for(i=0;i<n;i++){sum=(sum>>7)|(sum<<57);sum^=a[i];sum+=a[i];} return sum;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Compiler-optimised C implementation of the benchmark kernels.
|
||||
*
|
||||
* Keep this translation unit separate from the assembly backend so release
|
||||
* builds can apply their strongest safe optimisation settings to it. The
|
||||
* implementation is shared with the portable i386 backend; symbol renaming
|
||||
* lets both backends live in the same executable.
|
||||
*/
|
||||
#define fb_int_math fb_c_int_math
|
||||
#define fb_fp_math fb_c_fp_math
|
||||
#define fb_primes fb_c_primes
|
||||
#define fb_simd fb_c_simd
|
||||
#define fb_compress fb_c_compress
|
||||
#define fb_chacha20 fb_c_chacha20
|
||||
#define fb_physics fb_c_physics
|
||||
#define fb_sort fb_c_sort
|
||||
#define fb_chase fb_c_chase
|
||||
|
||||
#include "fossbench-i386.c"
|
||||
Reference in New Issue
Block a user