mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-08-11 06:40:35 +00:00
Snapshot of previously-uncommitted local work so nothing is lost after the power outage. NOT reviewed for correctness — a WIP checkpoint, not a feature: - multi-language hachimoji encoders (c/cpp/fortran/julia/octave/r/scala/go/rust/coq) - formal Lean WIP (BraidTree, Eisenstein, HachimojiCapture, MathlibConnect, ModularFormBridge, ClusterManifold) + lakefile + E8Sidon edit - docs/, experiments/ (epyc oisc benches), deploy/, scripts, test scaffolding - .gitignore: exclude **/target/ and Coq build artifacts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
242 lines
8.8 KiB
C
242 lines
8.8 KiB
C
/*
|
||
* epyc_oisc_bench.c — EPYC 9645 Turin OISC throughput
|
||
*
|
||
* Three independent benchmarks:
|
||
* 1. Word SUBLEQ (int16, standard)
|
||
* 2. Cache-line SUBLEQ (AVX-512, each variable on its own cache line)
|
||
* 3. Ring dispatch (virtio/TLP batch model)
|
||
*
|
||
* Build: gcc -march=znver5 -O3 -flto -mavx512f -mavx512bw epyc_oisc_bench.c -o oisc_bench
|
||
* Run: perf stat ./oisc_bench
|
||
*/
|
||
|
||
#define _GNU_SOURCE
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <stdint.h>
|
||
#include <stdalign.h>
|
||
#include <time.h>
|
||
#include <immintrin.h>
|
||
|
||
#define HALT ((int16_t)0x8000)
|
||
#define WORDS 65536
|
||
|
||
static double now(void) {
|
||
struct timespec ts;
|
||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||
return ts.tv_sec + ts.tv_nsec * 1e-9;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════════════
|
||
* 1. WORD SUBLEQ
|
||
* ═══════════════════════════════════════════════════════════════ */
|
||
#define W_ONE 1 /* addr 1 = constant 1 */
|
||
#define W_CNT 2 /* addr 2 = counter (initialized to N) */
|
||
#define W_ACC 3 /* addr 3 = accumulator */
|
||
#define W_ZERO 0
|
||
|
||
static void build_word(int16_t *m, int N) {
|
||
memset(m, 0, WORDS * sizeof(int16_t));
|
||
m[W_ONE] = 1;
|
||
m[W_CNT] = N;
|
||
m[W_ACC] = 0;
|
||
|
||
/* Program at word 1024 */
|
||
int pc = 1024;
|
||
int looptop = pc;
|
||
|
||
/* I0: CNT--; if CNT <= 0 → HALT */
|
||
m[pc] = W_ONE; m[pc+1] = W_CNT; m[pc+2] = 0; /* 0 = patch later */
|
||
pc += 3;
|
||
|
||
/* I1: ZERO=0, always branch back */
|
||
m[pc] = W_ZERO; m[pc+1] = W_ZERO; m[pc+2] = looptop;
|
||
pc += 3;
|
||
|
||
int haltpc = pc;
|
||
m[pc] = 0; m[pc+1] = 0; m[pc+2] = HALT;
|
||
|
||
/* Patch I0's HALT target */
|
||
m[looptop + 2] = haltpc;
|
||
}
|
||
|
||
static int64_t run_word(int16_t *m, int pc) {
|
||
int64_t n = 0;
|
||
while (1) {
|
||
int16_t s = m[pc], d = m[pc+1], nx = m[pc+2];
|
||
if (nx == HALT) break;
|
||
m[d] -= m[s];
|
||
pc = (m[d] <= 0) ? nx : pc + 3;
|
||
n++;
|
||
}
|
||
return n;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════════════
|
||
* 2. CACHE-LINE SUBLEQ (AVX-512)
|
||
*
|
||
* Each variable lives on its own 64-byte cache line:
|
||
* CL_ONE = line 1 (word 32 = 1, rest = 0)
|
||
* CL_CNT = line 2 (word 64 = N, rest = 0)
|
||
* CL_ACC = line 3 (word 96 = 0, rest = 0)
|
||
* CL_ZERO = line 0 (all zeros)
|
||
*
|
||
* Program addresses refer to LINE indices (0-4095).
|
||
* Execution: cl_sub(&cm[src_line], &cm[dst_line]) (entire line subtract)
|
||
* Check: if first word of dst line <= 0 → branch
|
||
* ═══════════════════════════════════════════════════════════════ */
|
||
#define CL_ONE 1
|
||
#define CL_CNT 2
|
||
#define CL_ACC 3
|
||
#define CL_ZERO 0
|
||
|
||
typedef int16_t cl_line_t[32] __attribute__((aligned(64)));
|
||
|
||
static inline void cl_sub(cl_line_t *a, cl_line_t *b) {
|
||
__m512i va = _mm512_load_si512(a);
|
||
__m512i vb = _mm512_load_si512(b);
|
||
_mm512_store_si512(b, _mm512_sub_epi16(vb, va));
|
||
}
|
||
|
||
static void build_cl(cl_line_t *cm, int N) {
|
||
memset(cm, 0, 4096 * sizeof(cl_line_t));
|
||
|
||
/* Init cache lines */
|
||
((int16_t *)(&cm[CL_ONE]))[0] = 1;
|
||
((int16_t *)(&cm[CL_CNT]))[0] = N;
|
||
((int16_t *)(&cm[CL_ACC]))[0] = 0;
|
||
|
||
/* Program stored as int16 triples after the last cache line (word 131072+) */
|
||
/* We'll use word 131072 and up for program storage */
|
||
int16_t *prog = (int16_t *)&cm[2048]; /* after cache line 2048 = 128 KB */
|
||
int pc = 0;
|
||
int looptop = 0;
|
||
|
||
/* I0: CL_CNT -= CL_ONE; if CL_CNT[0] <= 0 → HALT */
|
||
prog[pc] = CL_ONE; prog[pc+1] = CL_CNT; prog[pc+2] = 0; /* patch later */
|
||
pc += 3;
|
||
|
||
/* I1: CL_ZERO = 0 → always branch back */
|
||
prog[pc] = CL_ZERO; prog[pc+1] = CL_ZERO; prog[pc+2] = looptop;
|
||
pc += 3;
|
||
|
||
int haltpc = pc;
|
||
prog[pc] = 0; prog[pc+1] = 0; prog[pc+2] = HALT;
|
||
|
||
prog[looptop + 2] = haltpc;
|
||
}
|
||
|
||
static int64_t run_cl(cl_line_t *cm, int entry) {
|
||
int16_t *prog = (int16_t *)&cm[2048];
|
||
int pc = entry;
|
||
int64_t n = 0;
|
||
while (1) {
|
||
int16_t s = prog[pc], d = prog[pc+1], nx = prog[pc+2];
|
||
if (nx == HALT) break;
|
||
cl_sub(&cm[s], &cm[d]);
|
||
pc = (((int16_t *)(&cm[d]))[0] <= 0) ? nx : pc + 3;
|
||
n++;
|
||
}
|
||
return n;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════════════
|
||
* 3. RING DISPATCH (virtio/TLP model)
|
||
*
|
||
* Pre-encoded instructions in a ring buffer (64B each, like PCIe TLPs).
|
||
* Each TLP = RingInstr { src, dst, nxt, pad_to_64B }.
|
||
* Processor just reads ring entries in sequence.
|
||
* ═══════════════════════════════════════════════════════════════ */
|
||
typedef struct __attribute__((aligned(64))) {
|
||
int16_t src;
|
||
int16_t dst;
|
||
int16_t nxt;
|
||
int16_t _pad[29];
|
||
} RingInstr;
|
||
|
||
static void build_ring(RingInstr *ring, int count) {
|
||
for (int i = 0; i < count; i++) {
|
||
ring[i].src = 3; /* ACC */
|
||
ring[i].dst = 4; /* WRK */
|
||
ring[i].nxt = (i + 1 < count) && (i % 100000 != 99999) ? (int16_t)(i + 1) : HALT;
|
||
memset(ring[i]._pad, 0, 58);
|
||
}
|
||
}
|
||
|
||
static int64_t run_ring(RingInstr *ring, int count, int16_t *mem) {
|
||
int64_t n = 0;
|
||
for (int i = 0; i < count; i++) {
|
||
if (ring[i].nxt == HALT) break;
|
||
mem[ring[i].dst] -= mem[ring[i].src];
|
||
n++;
|
||
}
|
||
return n;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════════════
|
||
* Main
|
||
* ═══════════════════════════════════════════════════════════════ */
|
||
int main(void) {
|
||
printf("=== EPYC 9645 Turin — OISC Cache-Line Benchmark ===\n\n");
|
||
|
||
FILE *f = fopen("/proc/cpuinfo", "r");
|
||
char buf[256];
|
||
if (f) {
|
||
while (fgets(buf, sizeof buf, f))
|
||
if (strstr(buf, "model name") || strstr(buf, "cache size")) {
|
||
buf[strcspn(buf, "\n")] = 0; printf(" %s\n", buf);
|
||
}
|
||
fclose(f);
|
||
}
|
||
|
||
int N = 30000;
|
||
int64_t total, tw, tc, tr;
|
||
double t0, t1, sw, sc, sr;
|
||
|
||
/* ──────────── WORD ──────────── */
|
||
printf("\n── 1. WORD SUBLEQ (%d iter × 2000 runs) ──\n", N);
|
||
int16_t *wm = aligned_alloc(64, WORDS * sizeof(int16_t));
|
||
build_word(wm, N);
|
||
run_word(wm, 1024);
|
||
build_word(wm, N);
|
||
t0 = now(); total = 0;
|
||
for (int i = 0; i < 2000; i++) { build_word(wm, N); total += run_word(wm, 1024); }
|
||
t1 = now(); tw = total; sw = tw / (t1 - t0) / 1e6;
|
||
printf(" %ld instr in %.4f s = %.2f M/s\n", (long)tw, t1 - t0, sw);
|
||
free(wm);
|
||
|
||
/* ──────────── CACHE LINE ──────────── */
|
||
printf("\n── 2. CACHE-LINE SUBLEQ (AVX-512, %d iter × 2000 runs) ──\n", N);
|
||
cl_line_t *cm = aligned_alloc(64, 4096 * sizeof(cl_line_t));
|
||
build_cl(cm, N);
|
||
run_cl(cm, 0);
|
||
build_cl(cm, N);
|
||
t0 = now(); total = 0;
|
||
for (int i = 0; i < 2000; i++) { build_cl(cm, N); total += run_cl(cm, 0); }
|
||
t1 = now(); tc = total; sc = tc / (t1 - t0) / 1e6;
|
||
printf(" %ld instr in %.4f s = %.2f M/s\n", (long)tc, t1 - t0, sc);
|
||
free(cm);
|
||
|
||
/* ──────────── RING ──────────── */
|
||
printf("\n── 3. RING DISPATCH (virtio/TLP, 65536 × 1000) ──\n");
|
||
RingInstr *ring = aligned_alloc(64, 65536 * sizeof(RingInstr));
|
||
int16_t *rm = aligned_alloc(64, 1024 * sizeof(int16_t));
|
||
memset(rm, 0, 1024 * sizeof(int16_t));
|
||
rm[3] = 0; rm[4] = 0;
|
||
build_ring(ring, 65536);
|
||
t0 = now(); total = 0;
|
||
for (int i = 0; i < 1000; i++) { total += run_ring(ring, 65536, rm); }
|
||
t1 = now(); tr = total; sr = tr / (t1 - t0) / 1e6;
|
||
printf(" %ld instr in %.4f s = %.2f M/s\n", (long)tr, t1 - t0, sr);
|
||
free(ring); free(rm);
|
||
|
||
/* ──────────── SUMMARY ──────────── */
|
||
printf("\n═══ SUMMARY ═══\n");
|
||
printf(" %-28s %9.2f M instr/s\n", "WORD SUBLEQ", sw);
|
||
printf(" %-28s %9.2f M instr/s\n", "CACHE-LINE (AVX-512)", sc);
|
||
printf(" %-28s %9.2f M instr/s\n", "RING DISPATCH", sr);
|
||
printf("\n Ratio CL/Word: %.2fx\n", sc / sw);
|
||
printf(" Ratio Ring/Word: %.2fx\n", sr / sw);
|
||
return 0;
|
||
}
|