tests : check the arch DT3 vec_dot is bit-identical to the generic
Calls the actual ggml_vec_dot_dt3_q8_0_generic symbol against the dispatched vec_dot and requires memcmp-equal floats. Blocks exercise the three regions, the 79/80 and 119/120 boundaries, non-trivial qh bytes (would expose a vectorization reading their padding 5th digit), and scales of both and mixed signs; y reaches the full q8_0 range. Mutation-checked: flipping one bit of a digit blend mask in the AVX-512 kernel makes 94 of the 96 reps fail.
This commit is contained in:
@@ -479,6 +479,89 @@ static int test_vec_dot(const ggml_type_traits_cpu * qfns_cpu) {
|
||||
return num_failed;
|
||||
}
|
||||
|
||||
// the scalar reference implementation, always compiled into ggml-cpu; on
|
||||
// architectures without a native kernel the dispatched vec_dot IS this
|
||||
// function and the parity test below passes trivially
|
||||
extern "C" void ggml_vec_dot_dt3_q8_0_generic(int n, float * s, size_t bs, const void * vx, size_t bx, const void * vy, size_t by, int nrc);
|
||||
|
||||
// the dispatched (possibly vectorized) vec_dot must match the generic scalar
|
||||
// implementation exactly — the actual function is called, not a re-derivation
|
||||
// of it. Blocks exercise all three regions, the 79/80 and 119/120 boundaries,
|
||||
// non-trivial qh bytes (would expose reading their padding 5th digit), and
|
||||
// scales of both and mixed signs.
|
||||
static int test_vec_dot_arch_parity(const ggml_type_traits_cpu * qfns_cpu) {
|
||||
int num_failed = 0;
|
||||
|
||||
const auto * vdot_traits = ggml_get_type_traits_cpu(qfns_cpu->vec_dot_type);
|
||||
|
||||
const int nblocks = 3;
|
||||
const int n = nblocks*QK_DT3;
|
||||
|
||||
const float scale_cases[][2] = {
|
||||
{ 1.0f, 0.25f },
|
||||
{ 0.5f, -0.125f }, // negative second plane
|
||||
{-2.0f, 0.75f }, // negative first plane
|
||||
{-0.75f, -0.0625f }, // both negative
|
||||
{ 0.0f, 1.0f }, // dead first plane
|
||||
};
|
||||
const int n_scale_cases = (int)(sizeof(scale_cases)/sizeof(scale_cases[0]));
|
||||
|
||||
// pattern 0: fully random trits
|
||||
// pattern 1: zero everywhere except elements 120..127 (qh-only)
|
||||
// pattern 2: single +1/-1 walking over the region boundaries
|
||||
const int boundary_pos[] = { 0, 79, 80, 119, 120, 127 };
|
||||
|
||||
for (int rep = 0; rep < 96; ++rep) {
|
||||
std::vector<uint8_t> xq(nblocks*DT3_BLOCK_SIZE);
|
||||
|
||||
for (int i = 0; i < nblocks; ++i) {
|
||||
int8_t t1[QK_DT3] = {0};
|
||||
int8_t t2[QK_DT3] = {0};
|
||||
|
||||
const int pattern = rep % 3;
|
||||
if (pattern == 0) {
|
||||
for (int j = 0; j < QK_DT3; ++j) {
|
||||
t1[j] = rng_trit();
|
||||
t2[j] = rng_trit();
|
||||
}
|
||||
} else if (pattern == 1) {
|
||||
for (int j = 120; j < QK_DT3; ++j) {
|
||||
t1[j] = rng_trit();
|
||||
t2[j] = rng_trit();
|
||||
}
|
||||
} else {
|
||||
const int pos = boundary_pos[rep/3 % 6];
|
||||
t1[pos] = (rep & 1) ? 1 : -1;
|
||||
t2[QK_DT3 - 1 - pos] = (rep & 1) ? -1 : 1;
|
||||
}
|
||||
|
||||
const float * sc = scale_cases[(rep + i) % n_scale_cases];
|
||||
ref_pack_block(t1, sc[0], t2, sc[1], &xq[i*DT3_BLOCK_SIZE]);
|
||||
}
|
||||
|
||||
std::vector<float> y(n);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
// reach the full q8_0 range, both signs
|
||||
y[j] = 127.0f*sinf(0.7f*(float)(j + 13*rep)) + 0.5f*cosf((float)j);
|
||||
}
|
||||
std::vector<uint8_t> yq(ggml_row_size(qfns_cpu->vec_dot_type, n));
|
||||
vdot_traits->from_float(y.data(), yq.data(), n);
|
||||
|
||||
float res_arch = INFINITY;
|
||||
float res_generic = -INFINITY;
|
||||
qfns_cpu->vec_dot(n, &res_arch, 0, xq.data(), 0, yq.data(), 0, 1);
|
||||
ggml_vec_dot_dt3_q8_0_generic(n, &res_generic, 0, xq.data(), 0, yq.data(), 0, 1);
|
||||
|
||||
if (memcmp(&res_arch, &res_generic, sizeof(float)) != 0) {
|
||||
printf("FAILED: vec_dot arch parity rep %d: arch %.9g != generic %.9g\n",
|
||||
rep, res_arch, res_generic);
|
||||
num_failed++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_failed;
|
||||
}
|
||||
|
||||
// --dequant IN.bin OUT.f32 : dequantize raw DT3 blocks, for parity checks
|
||||
// against external packers (ternaria's Rust pack_dt3)
|
||||
static int run_dequant_file(const char * in_path, const char * out_path) {
|
||||
@@ -541,6 +624,7 @@ int main(int argc, char * argv[]) {
|
||||
num_failed += test_roundtrip(qfns);
|
||||
num_failed += test_quantize_pack_parity(qfns_cpu);
|
||||
num_failed += test_vec_dot(qfns_cpu);
|
||||
num_failed += test_vec_dot_arch_parity(qfns_cpu);
|
||||
|
||||
printf("%d tests failed\n", num_failed);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user