Skip to content

Commit 9b09f89

Browse files
committed
Update
[ghstack-poisoned]
2 parents 0b7f637 + 6e5feda commit 9b09f89

5 files changed

Lines changed: 40 additions & 19 deletions

File tree

backends/webgpu/runtime/ops/embedding_q4gsw/EmbeddingQ4gsw.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ void embedding_q4gsw_impl(WebGPUGraph& graph, const std::vector<int>& args) {
131131
"WebGPU embedding_q4gsw: dtype/byte-size mismatch "
132132
"(indices int32, weight uint8, scales/out fp32)");
133133
}
134+
if (total_blocks > UINT32_MAX) {
135+
throw std::runtime_error(
136+
"WebGPU embedding_q4gsw: total_blocks exceeds uint32 dispatch range");
137+
}
134138

135139
// 1D dispatch: one thread per 32-dim block; validate before any alloc.
136140
const uint32_t wg_size =
@@ -141,7 +145,7 @@ void embedding_q4gsw_impl(WebGPUGraph& graph, const std::vector<int>& args) {
141145
EmbeddingParams params = {};
142146
params.embed_dim = embed_dim;
143147
params.blocks_per_row = blocks_per_row;
144-
params.num_indices = num_indices;
148+
params.num_indices = num_indices; // std140 layout only; shader derives it
145149
params.group_size = static_cast<uint32_t>(group_size);
146150
params.groups_per_row = groups_per_row;
147151
params.bytes_per_row = bytes_per_row;

backends/webgpu/runtime/ops/rope/RotaryEmbedding.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ void apply_rotary_emb_impl(WebGPUGraph& graph, const std::vector<int>& args) {
179179
"freqs shape != [seq, head_dim/2]");
180180
}
181181

182+
if (xq_numel / 2u > UINT32_MAX || xk_numel / 2u > UINT32_MAX) {
183+
throw std::runtime_error(
184+
"WebGPU apply_rotary_emb: pair count exceeds uint32 dispatch range");
185+
}
186+
182187
const uint32_t wg_size =
183188
utils::clamp_workgroup_size(device, kRotaryEmbeddingWorkgroupSizeX);
184189
// Validate both dispatches before any GPU-object alloc (no leak on throw).

backends/webgpu/scripts/test_webgpu_native_ci.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ $PYTHON_EXECUTABLE -c "
6969
from executorch.backends.webgpu.test.ops.embedding_q4gsw.test_embedding_q4gsw import export_embedding_q4gsw_model
7070
export_embedding_q4gsw_model('${EMBEDDING_MODEL}', '${EMBEDDING_GOLDEN}', '${EMBEDDING_INDICES}')
7171
export_embedding_q4gsw_model('${EMBEDDING_LLAMA1B_MODEL}', '${EMBEDDING_LLAMA1B_GOLDEN}', '${EMBEDDING_LLAMA1B_INDICES}', 'llama1b')
72-
" || echo "WARN: embedding_q4gsw export failed; webgpu_native_test embedding cases self-skip"
72+
" || echo "WARN: embedding_q4gsw export failed; embedding configs will FAIL in webgpu_native_test"
7373

7474
$PYTHON_EXECUTABLE -c "
7575
from executorch.backends.webgpu.test.ops.rms_norm.test_rms_norm import export_rms_norm_cases

backends/webgpu/test/ops/embedding_q4gsw/test_embedding_q4gsw.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,15 @@ def test_golden_matches_eager(self) -> None:
122122
weight, scales, group_size = _quant_params(qm)
123123
vocab = weight.shape[0]
124124
embed = weight.shape[1] * 2
125-
# fp64 reference dequant so the oracle carries no fp32 rounding.
126-
deq = torch.empty((vocab, embed), dtype=torch.float64)
127-
for row in range(vocab):
128-
for d in range(embed):
129-
byte = int(weight[row, d // 2])
130-
nib = (byte >> 4) if (d % 2 == 0) else (byte & 0xF)
131-
scale = float(scales[row, d // group_size])
132-
deq[row, d] = (nib - 8) * scale
125+
# fp64 reference dequant, vectorized (no fp32 rounding in oracle).
126+
w = weight.to(torch.int64)
127+
nib = torch.empty((vocab, embed), dtype=torch.int64)
128+
nib[:, 0::2] = (w >> 4) & 0xF # even dim -> high nibble
129+
nib[:, 1::2] = w & 0xF # odd dim -> low nibble
130+
scale_exp = scales.to(torch.float64).repeat_interleave(
131+
group_size, dim=1
132+
)
133+
deq = (nib - 8).to(torch.float64) * scale_exp
133134
eager = torch.nn.functional.embedding(idx, deq)
134135
golden = _golden(qm, idx)
135136
torch.testing.assert_close(golden.double(), eager, atol=1e-5, rtol=1e-5)

backends/webgpu/test/test_webgpu_native.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,16 @@ static float q4gsw_ramp(int i) {
407407
return static_cast<float>((i % 17) - 8) / 16.0f;
408408
}
409409

410-
// Per-element dual tolerance (abs OR rel), parameterized like sdpa_within_tol.
410+
// Fwd decl of the per-element abs-OR-rel tolerance helper (defined below).
411+
static bool quant_within_tol(
412+
const float* out,
413+
const float* golden,
414+
int n,
415+
float atol,
416+
float rtol,
417+
float* ma,
418+
float* mr);
419+
411420
static std::vector<int32_t> load_indices(
412421
const std::string& path,
413422
size_t numel) {
@@ -483,19 +492,21 @@ static bool test_embedding_q4gsw(
483492
const float* out_data = out_tensor.const_data_ptr<float>();
484493

485494
float max_abs_err = 0.0f, max_rel_err = 0.0f;
486-
for (int i = 0; i < out_numel; i++) {
487-
const float ae = std::abs(out_data[i] - golden[i]);
488-
max_abs_err = std::max(max_abs_err, ae);
489-
max_rel_err =
490-
std::max(max_rel_err, ae / std::max(std::abs(golden[i]), 1e-6f));
491-
}
495+
const bool pass = quant_within_tol(
496+
out_data,
497+
golden.data(),
498+
out_numel,
499+
1e-3f,
500+
1e-3f,
501+
&max_abs_err,
502+
&max_rel_err);
492503
printf(
493504
"Max abs error: %e Max rel error: %e (checked %d elements)\n",
494505
max_abs_err,
495506
max_rel_err,
496507
out_numel);
497-
if (max_abs_err > 1e-3f || max_rel_err > 1e-3f) {
498-
printf("FAIL: embedding_q4gsw exceeds tolerance 1e-3\n");
508+
if (!pass) {
509+
printf("FAIL: embedding_q4gsw exceeds tolerance 1e-3 (abs AND rel)\n");
499510
return false;
500511
}
501512
printf("PASS: embedding_q4gsw test\n");

0 commit comments

Comments
 (0)