Skip to content

Commit 91b2d97

Browse files
committed
Update
[ghstack-poisoned]
2 parents a9ff12e + 33fde93 commit 91b2d97

2 files changed

Lines changed: 50 additions & 19 deletions

File tree

backends/webgpu/test/native/test_dynamic_shape.cpp

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,35 +102,43 @@ void check_s(Module& m, const std::string& prefix, int s) {
102102
<< " golden.size=" << golden.size() << ")";
103103
}
104104

105-
// Dynamic quantized linear: input [M, kLinK] -> output [M, kLinN].
105+
// Dynamic quantized linear: input [M, kLinK] -> output [M, n]. kLinN is the
106+
// register-tiled/bicol config; kLinNShmem (N>=2048) routes to the shmem GEMM.
106107
constexpr int kLinK = 64;
107108
constexpr int kLinN = 128;
108-
// Run dyn_linear at [m_rows, kLinK] on an already-loaded module (so it can be
109+
constexpr int kLinNShmem = 2048;
110+
// Run <prefix> at [m_rows, kLinK] on an already-loaded module (so it can be
109111
// reused across M without a fresh load), and compare to the golden.
110-
void run_linear(Module& m, int m_rows) {
111-
const std::string base = g_dir + "/dyn_linear.S" + std::to_string(m_rows);
112+
void run_linear(Module& m, int m_rows, const char* prefix, int n) {
113+
const std::string base = g_dir + "/" + prefix + ".S" + std::to_string(m_rows);
112114
auto input = read_bin(base + ".input.bin");
113115
auto golden = read_bin(base + ".golden.bin");
114-
ASSERT_FALSE(input.empty()) << "missing dyn_linear.S" << m_rows;
116+
ASSERT_FALSE(input.empty()) << "missing " << prefix << ".S" << m_rows;
115117
auto t = make_tensor_ptr({m_rows, kLinK}, std::move(input));
116118
auto r = m.forward({EValue(t)});
117119
ASSERT_TRUE(r.ok() && !r.get().empty() && r.get()[0].isTensor())
118-
<< "dyn_linear M=" << m_rows << " forward failed";
120+
<< prefix << " M=" << m_rows << " forward failed";
119121
const auto& out = r.get()[0].toTensor();
120-
const size_t numel = static_cast<size_t>(m_rows) * kLinN;
122+
const size_t numel = static_cast<size_t>(m_rows) * n;
121123
ASSERT_EQ(static_cast<size_t>(out.numel()), numel)
122-
<< "dyn_linear M=" << m_rows << " output numel mismatch";
124+
<< prefix << " M=" << m_rows << " output numel mismatch";
123125
std::vector<float> got(
124126
out.const_data_ptr<float>(), out.const_data_ptr<float>() + numel);
125127
const float e = max_err(got, golden);
126128
// 4-bit quant: looser tol (the kernel mirrors the dequant-matmul reference).
127-
EXPECT_LT(e, 5e-3f) << "dyn_linear M=" << m_rows << " max_err=" << e;
129+
EXPECT_LT(e, 5e-3f) << prefix << " M=" << m_rows << " max_err=" << e;
128130
}
129131

130132
void check_linear(int m_rows) {
131133
Module m(g_dir + "/dyn_linear.pte");
132134
ASSERT_EQ(m.load_forward(), Error::Ok) << "load dyn_linear.pte";
133-
run_linear(m, m_rows);
135+
run_linear(m, m_rows, "dyn_linear", kLinN);
136+
}
137+
138+
void check_linear_shmem(int m_rows) {
139+
Module m(g_dir + "/dyn_linear_shmem.pte");
140+
ASSERT_EQ(m.load_forward(), Error::Ok) << "load dyn_linear_shmem.pte";
141+
run_linear(m, m_rows, "dyn_linear_shmem", kLinNShmem);
134142
}
135143

136144
// Dynamic SDPA (GQA prefill, input_pos=0): q[1,s,hq,d] k/v[1,s,hkv,d]
@@ -395,7 +403,25 @@ TEST(DynamicShape, QuantizedLinearReusedGraph) {
395403
Module m(g_dir + "/dyn_linear.pte");
396404
ASSERT_EQ(m.load_forward(), Error::Ok) << "load dyn_linear.pte";
397405
for (int m_rows : {128, 32, 1, 128}) {
398-
run_linear(m, m_rows);
406+
run_linear(m, m_rows, "dyn_linear", kLinN);
407+
}
408+
}
409+
410+
// I3: dynamic linear at N=2048 -> the shmem-GEMM route (K>=4096||N>=2048); the
411+
// resize hook recomputes the shmem tile count for the live M on the fixed shmem
412+
// pipeline (M=1 exercises a partial row-tile).
413+
TEST(DynamicShape, QuantizedLinearShmem) {
414+
for (int m_rows : {128, 32, 1}) {
415+
check_linear_shmem(m_rows);
416+
}
417+
}
418+
419+
// I4: shmem-routed linear reusing ONE loaded graph across M.
420+
TEST(DynamicShape, QuantizedLinearShmemReusedGraph) {
421+
Module m(g_dir + "/dyn_linear_shmem.pte");
422+
ASSERT_EQ(m.load_forward(), Error::Ok) << "load dyn_linear_shmem.pte";
423+
for (int m_rows : {128, 32, 1, 128}) {
424+
run_linear(m, m_rows, "dyn_linear_shmem", kLinNShmem);
399425
}
400426
}
401427

backends/webgpu/test/ops/dynamic_shape/test_dynamic_shape_export.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,10 @@ def export_dynamic_shape_cases(out_dir: str) -> None:
183183
)
184184
_write_goldens(rmsmul, "dyn_rmsmul", out_dir, [MAXS, 32, 1])
185185

186-
# 2d) 4-bit quantized linear with a DYNAMIC rows (M) dim — prefill GEMM.
186+
# 2d) 4-bit quantized linear with a DYNAMIC rows (M) dim — prefill GEMM
187+
# (register-tiled N=128) + a shmem-GEMM-routed variant (N=2048).
187188
_export_dynamic_linear(out_dir)
189+
_export_dynamic_linear(out_dir, n=LIN_SHMEM_N, prefix="dyn_linear_shmem")
188190

189191
# 2e) Fused SDPA with a DYNAMIC seq-len S (prefill, input_pos=0).
190192
_export_dynamic_sdpa(out_dir)
@@ -222,17 +224,20 @@ def export_dynamic_shape_cases(out_dir: str) -> None:
222224
# Quantized linear: K x N weight, dynamic rows M; input [M, K], output [M, N].
223225
LIN_K = 64
224226
LIN_N = 128
227+
LIN_SHMEM_N = 2048 # N>=2048 routes linear_q4gsw to the shmem-GEMM path
225228
LIN_GROUP = 32
226229
LIN_MAXM = 128
227230

228231

229-
def _export_dynamic_linear(out_dir: str) -> None:
232+
def _export_dynamic_linear(
233+
out_dir: str, n: int = LIN_N, prefix: str = "dyn_linear"
234+
) -> None:
230235
from executorch.backends.webgpu.test.ops.quantized_linear.test_quantized_linear import (
231236
_fp64_golden,
232237
_make_quantized_model,
233238
)
234239

235-
model = _make_quantized_model(LIN_K, LIN_N, LIN_GROUP)
240+
model = _make_quantized_model(LIN_K, n, LIN_GROUP)
236241
x = _ramp((LIN_MAXM, LIN_K))
237242
m_dim = torch.export.Dim("m", min=1, max=LIN_MAXM)
238243
ep = torch.export.export(model, (x,), dynamic_shapes=({0: m_dim},))
@@ -244,17 +249,17 @@ def _export_dynamic_linear(out_dir: str) -> None:
244249
for plan in et.executorch_program.execution_plan
245250
for d in plan.delegates
246251
), "linear_q4gsw not delegated"
247-
with open(os.path.join(out_dir, "dyn_linear.pte"), "wb") as f:
252+
with open(os.path.join(out_dir, f"{prefix}.pte"), "wb") as f:
248253
f.write(et.buffer)
249-
print("Exported dyn_linear.pte")
254+
print(f"Exported {prefix}.pte")
250255
for m in [LIN_MAXM, 32, 1]:
251256
xm = _ramp((m, LIN_K))
252257
g = _fp64_golden(model, xm).astype("<f4") # [m, N]
253258
xm.detach().numpy().astype("<f4").tofile(
254-
os.path.join(out_dir, f"dyn_linear.S{m}.input.bin")
259+
os.path.join(out_dir, f"{prefix}.S{m}.input.bin")
255260
)
256-
g.tofile(os.path.join(out_dir, f"dyn_linear.S{m}.golden.bin"))
257-
print(f" golden dyn_linear M={m}")
261+
g.tofile(os.path.join(out_dir, f"{prefix}.S{m}.golden.bin"))
262+
print(f" golden {prefix} M={m}")
258263

259264

260265
# Dynamic SDPA: GQA prefill (input_pos=0), q/k/v seq-len dynamic.

0 commit comments

Comments
 (0)