// Compare ggml_vec_dot_nvfp4_q8_0 (arch path) against ggml_vec_dot_nvfp4_q8_0_generic on random data. #include #include #include #include #include #include typedef struct { uint8_t d[4]; uint8_t qs[32]; } block_nvfp4; // 64 elems: 4 UE4M3 scales + 32 nibble bytes typedef struct { _Float16 d; int8_t qs[32]; } block_q8_0; // 32 elems void ggml_vec_dot_nvfp4_q8_0(int n, float * s, size_t bs, const void * vx, size_t bx, const void * vy, size_t by, int nrc); void ggml_vec_dot_nvfp4_q8_0_generic(int n, float * s, size_t bs, const void * vx, size_t bx, const void * vy, size_t by, int nrc); static uint32_t rng_state = 12345; static uint32_t rnd(void) { rng_state = rng_state * 1664525u + 1013904223u; return rng_state >> 8; } void ggml_cpu_init(void); int main(void) { ggml_cpu_init(); int fails = 0; for (int trial = 0; trial < 20; ++trial) { const int n = 64 * (1 + (trial * 37) % 200); const int nb = n / 64; block_nvfp4 * w = aligned_alloc(64, sizeof(block_nvfp4) * nb); block_q8_0 * a = aligned_alloc(64, sizeof(block_q8_0) * (n / 32)); for (int i = 0; i < nb; ++i) { for (int s = 0; s < 4; ++s) w[i].d[s] = (uint8_t)(rnd() % 127); // avoid NaN code 0x7F for (int j = 0; j < 32; ++j) w[i].qs[j] = (uint8_t)rnd(); } for (int i = 0; i < n / 32; ++i) { float amax = 0.f; float xs[32]; for (int j = 0; j < 32; ++j) { xs[j] = ((int)(rnd() % 20001) - 10000) / 1000.0f; if (fabsf(xs[j]) > amax) amax = fabsf(xs[j]); } float d = amax / 127.0f; float id = d ? 1.0f / d : 0.0f; a[i].d = (_Float16) d; for (int j = 0; j < 32; ++j) a[i].qs[j] = (int8_t) lrintf(xs[j] * id); } float r_gen = 0.f, r_arch = 0.f; ggml_vec_dot_nvfp4_q8_0_generic(n, &r_gen, 0, w, 0, a, 0, 1); ggml_vec_dot_nvfp4_q8_0(n, &r_arch, 0, w, 0, a, 0, 1); double rel = fabs((double) r_arch - r_gen) / (fabs((double) r_gen) + 1e-9); printf("n=%6d generic=% .6f arch=% .6f rel=%.3e %s\n", n, r_gen, r_arch, rel, rel < 1e-5 ? "OK" : "FAIL"); if (rel >= 1e-5) fails++; free(w); free(a); } printf("%s (%d failures)\n", fails ? "FAILED" : "PASSED", fails); return fails != 0; }