From 187f46b4fa1fe358a802832a0c19dffd7759a0e2 Mon Sep 17 00:00:00 2001 From: ivanfioravanti Date: Tue, 20 Oct 2026 07:45:39 +0200 Subject: [PATCH] ds4-bench: per-step decode logits hash and dump DS4_BENCH_DECODE_LOGITS_HASH=1 prints an FNV-1a hash of every decode step's full logits, outside the timed region, to compare arms bit for bit; DS4_BENCH_DECODE_LOGITS_DUMP=path also appends the raw F32 logits for KL checks. The encoder timeline keeps longer kernel names. --- ds4_bench.c | 19 +++++++++++++++++++ ds4_metal.m | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/ds4_bench.c b/ds4_bench.c index 44eb49dd..f493ac28 100644 --- a/ds4_bench.c +++ b/ds4_bench.c @@ -964,6 +964,25 @@ int main(int argc, char **argv) { } #endif const double token_t1 = bench_now_sec(); + /* Diagnostic: DS4_BENCH_DECODE_LOGITS_HASH prints an FNV-1a hash of + * every decode step's full logits, to compare arms bit for bit. */ + if (!speculative && getenv("DS4_BENCH_DECODE_LOGITS_HASH")) { + const int vocab = ds4_engine_vocab_size(engine); + float *lg = vocab > 0 ? malloc((size_t)vocab * sizeof(lg[0])) : NULL; + if (lg && ds4_session_copy_logits(session, lg, vocab) == vocab) { + uint64_t h = 1469598103934665603ull; + const unsigned char *b = (const unsigned char *)lg; + for (size_t k = 0; k < (size_t)vocab * sizeof(lg[0]); k++) h = (h ^ b[k]) * 1099511628211ull; + fprintf(stderr, "ds4-bench: decode-hash frontier=%d step=%d %016llx\n", + frontier, gen_done, (unsigned long long)h); + const char *dump = getenv("DS4_BENCH_DECODE_LOGITS_DUMP"); + if (dump) { + FILE *fp = fopen(dump, "ab"); + if (fp) { fwrite(lg, sizeof(lg[0]), (size_t)vocab, fp); fclose(fp); } + } + } + free(lg); + } int cycle_tokens = 0; for (int j = 0; j < ntok && gen_done < cfg.gen_tokens; j++) { if (toks[j] == eos) { diff --git a/ds4_metal.m b/ds4_metal.m index 765eda44..0be24b2e 100644 --- a/ds4_metal.m +++ b/ds4_metal.m @@ -65,7 +65,7 @@ static BOOL g_batch_has_work; * timeline (durations and the gaps between them) without ending command * buffers. Pass boundaries add a little serialization, so absolute numbers * are an upper bound; the anatomy is what this exists for. Off by default. */ -#define DS4_TIMELINE_NAME_MAX 80 +#define DS4_TIMELINE_NAME_MAX 400 #define DS4_TIMELINE_SAMPLES_PER_BUFFER 4096 typedef struct { uintptr_t caller;