Skip to content

Commit 9f00c22

Browse files
committed
[ExecuTorch][WebGPU] Test coverage for the f16-accumulate steel GEMM
Pull Request resolved: #20801 Adds golden coverage for the f16-accumulate (`pwdqf16acc`) steel GEMM, which runs under `WGPU_BACKEND_STEEL_F16ACC` when `group_size % BK == 0`. Key changes: - `test_quantized_linear.py` / `test_webgpu_native.cpp` — add `pwdqf16acc` (96x2048x256, K=2048) and `pwdqf16acc_down` (128x8192x2048, deep-K worst case) under `#ifdef WGPU_BACKEND_STEEL_F16ACC`, goldened against the fp64 dequant-matmul truth. f16 accumulation error grows with K, so the tolerances are wider than the f16-multiply `steel_f16` (2.3e-4) and the deep-K shape gets the loosest gate; perplexity (the kernel diff) is the primary quality bar and this catches gross bit/index bugs. Co-authored-with: Claude Code. ghstack-source-id: 401515200 @exported-using-ghexport Differential Revision: [D111163651](https://our.internmc.facebook.com/intern/diff/D111163651/)
1 parent 953f569 commit 9f00c22

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

backends/webgpu/test/ops/test_quantized_linear.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ class Q4gswConfig:
7777
# there). Same fp64 golden regardless of which kernel runs.
7878
Q4gswConfig("pwdq_gs64", 96, 2048, 256, group_size=64), # pwdq, non-32 group
7979
Q4gswConfig("pwdq_gs8", 96, 2048, 256, group_size=8), # steel_half fallback
80+
# pwdqf16acc (f16-accumulate) runs when the enable_f16_accumulate_gemm runtime
81+
# spec is set and gs % BK == 0 (perplexity-gated; see the kernel diff). Same
82+
# .pte as the f32 configs -- only the accumulator dtype differs -- goldened at a
83+
# looser f16-accumulate tol in the native test; deep-K stresses the worst case.
84+
Q4gswConfig("pwdqf16acc", 96, 2048, 256), # f16-accumulate steel (runtime)
85+
Q4gswConfig("pwdqf16acc_down", 128, 8192, 2048), # deep-K f16-accum worst case
8086
Q4gswConfig("gate_proj_pf", 128, 2048, 8192), # gate/up prefill (shmem via N)
8187
Q4gswConfig("down_proj_pf", 128, 8192, 2048), # down prefill (shmem via K)
8288
Q4gswConfig("shmem_edge", 130, 4096, 2056), # partial 32-tile bounds

backends/webgpu/test/test_webgpu_native.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <executorch/backends/webgpu/runtime/WebGPUGraph.h>
1212
#include <executorch/extension/module/module.h>
1313
#include <executorch/extension/tensor/tensor.h>
14+
#include <executorch/runtime/backend/backend_options_map.h>
15+
#include <executorch/runtime/backend/options.h>
1416

1517
#include <gtest/gtest.h>
1618

@@ -300,6 +302,14 @@ const Q4gswConfig kQ4gswConfigs[] = {
300302
// gs=8 (< BK=16) falls back to the per-nibble steel_half kernel.
301303
{"pwdq_gs64", 96, 2048, 256, 2.3e-4f, 1e-3f, true, false},
302304
{"pwdq_gs8", 96, 2048, 256, 2.3e-4f, 1e-3f, true, false},
305+
// f16-ACCUMULATE steel (pwdqf16acc): lossy, so a wider gate than the
306+
// f16-multiply steel_f16 (2.3e-4). f16 accumulation error grows with K, so
307+
// the deep-K down shape (K=8192) gets the loosest tol. Perplexity is the
308+
// primary quality gate (see the kernel diff); this catches gross bit/index
309+
// bugs. gs=32 (% BK == 0) selects pwdqf16acc; the sweep loads these rows
310+
// with the enable_f16_accumulate_gemm runtime spec set.
311+
{"pwdqf16acc", 96, 2048, 256, 2e-2f, 3e-2f, true, false},
312+
{"pwdqf16acc_down", 128, 8192, 2048, 5e-2f, 8e-2f, true, false},
303313
{"gate_proj_pf", 128, 2048, 8192, 1e-4f, 1e-3f, true, false}, // shmem via N
304314
{"down_proj_pf", 128, 8192, 2048, 1e-3f, 1e-2f, true, false}, // shmem via K
305315
{"shmem_edge", 130, 4096, 2056, 1e-4f, 1e-3f, true, false}, // partial tiles
@@ -563,7 +573,18 @@ void test_q4gsw_config(
563573
cfg.n);
564574

565575
Module module(pte);
566-
ASSERT_EQ(module.load_forward(), Error::Ok) << "could not load " << pte;
576+
// pwdqf16acc rows exercise the lossy f16-accumulate kernel, a runtime opt-in
577+
// (default off); enable it via the backend option keyed by the registered id.
578+
if (std::string(cfg.name).rfind("pwdqf16acc", 0) == 0) {
579+
BackendOptions<1> opts;
580+
opts.set_option("enable_f16_accumulate_gemm", true);
581+
LoadBackendOptionsMap map;
582+
ASSERT_EQ(map.set_options("VulkanBackend", opts.view()), Error::Ok);
583+
ASSERT_EQ(module.load_forward(nullptr, nullptr, &map), Error::Ok)
584+
<< "could not load " << pte;
585+
} else {
586+
ASSERT_EQ(module.load_forward(), Error::Ok) << "could not load " << pte;
587+
}
567588

568589
const int in_numel = cfg.m * cfg.k;
569590
const int out_numel = cfg.m * cfg.n;

0 commit comments

Comments
 (0)