Skip to content

Commit 821b5a9

Browse files
pytorchbotJCNTH
andauthored
[ExecuTorch][WebGPU] Enable FlashDecoding by default for decode SDPA (runtime shape gate) (#20586)
This PR was created by the merge bot to help merge the original PR into the main branch. ghstack PR number: #20544 by @JulianCloudNTH ^ Please use this as the source of truth for the PR details, comments, and reviews ghstack PR base: https://github.com/pytorch/executorch/tree/gh/JulianCloudNTH/64/base ghstack PR head: https://github.com/pytorch/executorch/tree/gh/JulianCloudNTH/64/head Merge bot PR base: https://github.com/pytorch/executorch/tree/main Merge bot PR head: https://github.com/pytorch/executorch/tree/gh/JulianCloudNTH/64/orig @diff-train-skip-merge Co-authored-by: Julian Ng-Thow-Hing <juliannth@meta.com>
1 parent 55a71e6 commit 821b5a9

10 files changed

Lines changed: 757 additions & 24 deletions

File tree

backends/webgpu/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ set(WEBGPU_SRCS
5151
runtime/ops/permute/Permute.cpp
5252
runtime/ops/cat/Cat.cpp
5353
runtime/ops/index/Index.cpp
54+
runtime/ops/sdpa_fd_decode/SdpaFdDecode.cpp
5455
)
5556

5657
add_library(webgpu_backend ${WEBGPU_SRCS})

backends/webgpu/runtime/WebGPUGraph.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ WGPUBuffer WebGPUGraph::create_scratch_buffer(size_t nbytes) {
7777
return buffer;
7878
}
7979

80+
WGPUBuffer WebGPUGraph::make_uniform_buffer(const void* data, size_t size) {
81+
WGPUBufferDescriptor desc = {};
82+
desc.size = size;
83+
desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
84+
desc.mappedAtCreation = true;
85+
WGPUBuffer buffer = wgpuDeviceCreateBuffer(device_, &desc);
86+
void* mapped = wgpuBufferGetMappedRange(buffer, 0, size);
87+
std::memcpy(mapped, data, size);
88+
wgpuBufferUnmap(buffer);
89+
uniform_buffer_bytes_ += size;
90+
return buffer;
91+
}
92+
8093
void WebGPUGraph::update_symints_from_inputs(
8194
const std::vector<InputData>& inputs) {
8295
for (const auto& src : symint_sources_) {

backends/webgpu/runtime/WebGPUGraph.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ class WebGPUGraph {
228228
// Graph-owned scratch storage buffer for fused-op intermediates (e.g. SDPA).
229229
WGPUBuffer create_scratch_buffer(size_t nbytes);
230230

231+
// Create a mapped-at-creation uniform buffer from `size` bytes and track it
232+
// in the memory stats. Shared helper for ops needing a uniform Params buffer.
233+
WGPUBuffer make_uniform_buffer(const void* data, size_t size);
234+
231235
WGPUShaderModule get_or_create_shader(
232236
const std::string& key,
233237
const char* wgsl_source);

backends/webgpu/runtime/ops/sdpa/Sdpa.cpp

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
#include <executorch/backends/webgpu/runtime/ops/sdpa/sdpa_compute_attn_weights_wgsl.h>
1313
#include <executorch/backends/webgpu/runtime/ops/sdpa/sdpa_compute_out_wgsl.h>
1414
#include <executorch/backends/webgpu/runtime/ops/sdpa/sdpa_softmax_wgsl.h>
15+
#include <executorch/backends/webgpu/runtime/ops/sdpa_fd_decode/SdpaFdDecode.h>
1516
#include <executorch/backends/webgpu/runtime/ops/update_cache/update_cache_wgsl.h>
1617

1718
#include <webgpu/webgpu.h>
1819

1920
#include <cmath>
2021
#include <cstdint>
21-
#include <cstring>
2222
#include <stdexcept>
2323
#include <string>
2424

@@ -128,22 +128,6 @@ static ComputeOutParams make_compute_out_params(
128128
return p;
129129
}
130130

131-
// Create a uniform buffer initialized with the given bytes.
132-
WGPUBuffer
133-
make_uniform_buffer(WebGPUGraph& graph, const void* data, size_t size) {
134-
WGPUDevice device = graph.device();
135-
WGPUBufferDescriptor desc = {};
136-
desc.size = size;
137-
desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
138-
desc.mappedAtCreation = true;
139-
WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &desc);
140-
void* mapped = wgpuBufferGetMappedRange(buffer, 0, size);
141-
std::memcpy(mapped, data, size);
142-
wgpuBufferUnmap(buffer);
143-
graph.add_uniform_buffer_bytes(size);
144-
return buffer;
145-
}
146-
147131
// A buffer + its byte size, for binding.
148132
struct BufferBinding {
149133
WGPUBuffer buffer;
@@ -262,7 +246,7 @@ static WGPUBuffer record_update_cache_dispatch(
262246
device, static_cast<uint32_t>(kv_numel), uc_wg, label);
263247
UpdateCacheParams uc =
264248
make_update_cache_params(kv_numel, kv_dst_offset, cache_numel);
265-
WGPUBuffer ubuf = make_uniform_buffer(graph, &uc, sizeof(uc));
249+
WGPUBuffer ubuf = graph.make_uniform_buffer(&uc, sizeof(uc));
266250
BufferBinding bindings[2] = {
267251
{cache.buffer, cache.nbytes}, {src.buffer, src.nbytes}};
268252
build_dispatch(
@@ -429,9 +413,6 @@ void sdpa_with_kv_cache_impl(WebGPUGraph& graph, const std::vector<int>& args) {
429413
static_cast<uint64_t>(S) *
430414
static_cast<uint64_t>(dynamic_pos ? Cmax : context_len);
431415
const uint64_t aw_bytes = aw_cap_floats * sizeof(float);
432-
// Prefill scratch scales as Hq·S·Cmax; can be large for long-context prefill.
433-
WGPUBuffer attn_weights = graph.create_scratch_buffer(aw_bytes);
434-
WGPUBuffer attn_weights_softmax = graph.create_scratch_buffer(aw_bytes);
435416

436417
// Dynamic input_pos: the resize hook rewrites these per step.
437418
WGPUBuffer uc_k_buf = nullptr, uc_v_buf = nullptr, qk_buf = nullptr,
@@ -475,6 +456,18 @@ void sdpa_with_kv_cache_impl(WebGPUGraph& graph, const std::vector<int>& args) {
475456
dynamic_pos,
476457
"update_cache(V)");
477458

459+
// FlashDecoding decode (S==1, static pos). Shapes FD can't handle (head dim
460+
// > kSdpaFdMaxHeadDim) fall through to the materialized path below.
461+
if (S == 1 && !dynamic_pos && D <= kSdpaFdMaxHeadDim) {
462+
sdpa_fd_decode_dispatch(
463+
graph, q, k_cache, v_cache, out, Hq, Hkv, D, context_len, g, scale);
464+
return;
465+
}
466+
467+
// QK/softmax scratch — allocated only on the non-FD path (Hq*S*Cmax prefill).
468+
WGPUBuffer attn_weights = graph.create_scratch_buffer(aw_bytes);
469+
WGPUBuffer attn_weights_softmax = graph.create_scratch_buffer(aw_bytes);
470+
478471
// --- Dispatch 3: QK -> attn_weights. One thread per TM x TN tile.
479472
{
480473
if (aw_floats > UINT32_MAX) {
@@ -487,7 +480,7 @@ void sdpa_with_kv_cache_impl(WebGPUGraph& graph, const std::vector<int>& args) {
487480
device, static_cast<uint32_t>(qk_tiles), qk_wg, "QK");
488481
AttnWeightsParams p = make_attn_weights_params(
489482
S, Hq, Hkv, D, context_len, input_pos, g, scale);
490-
WGPUBuffer ubuf = make_uniform_buffer(graph, &p, sizeof(p));
483+
WGPUBuffer ubuf = graph.make_uniform_buffer(&p, sizeof(p));
491484
BufferBinding bindings[3] = {
492485
{attn_weights, aw_bytes},
493486
{q.buffer, q.nbytes},
@@ -513,7 +506,7 @@ void sdpa_with_kv_cache_impl(WebGPUGraph& graph, const std::vector<int>& args) {
513506
const uint32_t wgc = utils::compute_1d_workgroup_count(
514507
device, static_cast<uint32_t>(Hq * S), 1, "softmax");
515508
SoftmaxParams p = make_softmax_params(Hq, S, context_len);
516-
WGPUBuffer ubuf = make_uniform_buffer(graph, &p, sizeof(p));
509+
WGPUBuffer ubuf = graph.make_uniform_buffer(&p, sizeof(p));
517510
BufferBinding bindings[2] = {
518511
{attn_weights_softmax, aw_bytes}, {attn_weights, aw_bytes}};
519512
build_dispatch(
@@ -537,7 +530,7 @@ void sdpa_with_kv_cache_impl(WebGPUGraph& graph, const std::vector<int>& args) {
537530
const uint32_t wgc = utils::compute_1d_workgroup_count(
538531
device, static_cast<uint32_t>(av_tiles), av_wg, "AV");
539532
ComputeOutParams p = make_compute_out_params(S, Hq, Hkv, D, context_len, g);
540-
WGPUBuffer ubuf = make_uniform_buffer(graph, &p, sizeof(p));
533+
WGPUBuffer ubuf = graph.make_uniform_buffer(&p, sizeof(p));
541534
BufferBinding bindings[3] = {
542535
{out.buffer, out.nbytes},
543536
{attn_weights_softmax, aw_bytes},

0 commit comments

Comments
 (0)