|
9 | 9 | #include <executorch/backends/webgpu/runtime/WebGPUGraph.h> |
10 | 10 | #include <executorch/backends/webgpu/runtime/WebGPUUtils.h> |
11 | 11 | #include <executorch/backends/webgpu/runtime/ops/OperatorRegistry.h> |
| 12 | +#include <executorch/backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_coop4_wgsl.h> |
12 | 13 | #include <executorch/backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_wgsl.h> |
13 | 14 |
|
14 | 15 | #include <webgpu/webgpu.h> |
@@ -93,18 +94,6 @@ void q4gsw_linear_impl(WebGPUGraph& graph, const std::vector<int>& args) { |
93 | 94 | "WebGPU linear_q4gsw: N*K_packed must be a multiple of 4 (u32-packed)"); |
94 | 95 | } |
95 | 96 |
|
96 | | - // Register-tiled GEMM: one thread per TM x TN tile; validate before alloc. |
97 | | - const uint32_t wg_size = |
98 | | - utils::clamp_workgroup_size(device, kQ4gswLinearWorkgroupSizeX); |
99 | | - const int64_t total_tiles = |
100 | | - q4gsw_ceil_div(M, kQ4gswTileM) * q4gsw_ceil_div(N, kQ4gswTileN); |
101 | | - if (total_tiles > static_cast<int64_t>(UINT32_MAX)) { |
102 | | - throw std::runtime_error( |
103 | | - "WebGPU linear_q4gsw: tile count exceeds the 1D dispatch limit"); |
104 | | - } |
105 | | - const uint32_t workgroup_count = utils::compute_1d_workgroup_count( |
106 | | - device, static_cast<uint32_t>(total_tiles), wg_size, "linear_q4gsw"); |
107 | | - |
108 | 97 | // fp32-only byte-size guards (no runtime dtype); fp16 scales -> bail. |
109 | 98 | const uint64_t scales_numel = |
110 | 99 | static_cast<uint64_t>(num_groups) * static_cast<uint64_t>(padded_N); |
@@ -132,6 +121,35 @@ void q4gsw_linear_impl(WebGPUGraph& graph, const std::vector<int>& args) { |
132 | 121 | "WebGPU linear_q4gsw: scales dims too small for K/N"); |
133 | 122 | } |
134 | 123 |
|
| 124 | + // M==1 decode -> coop4 GEMV (needs K%8==0 && gs%8==0); else tiled GEMM. |
| 125 | + const uint32_t wg_size = |
| 126 | + utils::clamp_workgroup_size(device, kQ4gswLinearWorkgroupSizeX); |
| 127 | + const bool use_gemv = (M == 1u && K % 8u == 0u && gs % 8u == 0u); |
| 128 | + const char* shader_src = use_gemv ? kQ4gswLinearCoop4WGSL : kQ4gswLinearWGSL; |
| 129 | + uint32_t workgroup_count; |
| 130 | + if (use_gemv) { |
| 131 | + // coop4: fixed 64 lanes, 1 workgroup per output, grid-strided over M*N. |
| 132 | + const uint64_t outputs = |
| 133 | + static_cast<uint64_t>(M) * static_cast<uint64_t>(N); |
| 134 | + if (outputs == 0u || outputs > UINT32_MAX) { |
| 135 | + throw std::runtime_error("WebGPU linear_q4gsw: M*N out of range"); |
| 136 | + } |
| 137 | + workgroup_count = |
| 138 | + utils::clamp_workgroup_count(device, static_cast<uint32_t>(outputs)); |
| 139 | + if (workgroup_count == 0u) { |
| 140 | + throw std::runtime_error("WebGPU linear_q4gsw: zero GEMV dispatch"); |
| 141 | + } |
| 142 | + } else { |
| 143 | + const int64_t total_tiles = |
| 144 | + q4gsw_ceil_div(M, kQ4gswTileM) * q4gsw_ceil_div(N, kQ4gswTileN); |
| 145 | + if (total_tiles > static_cast<int64_t>(UINT32_MAX)) { |
| 146 | + throw std::runtime_error( |
| 147 | + "WebGPU linear_q4gsw: tile count exceeds the 1D dispatch limit"); |
| 148 | + } |
| 149 | + workgroup_count = utils::compute_1d_workgroup_count( |
| 150 | + device, static_cast<uint32_t>(total_tiles), wg_size, "linear_q4gsw"); |
| 151 | + } |
| 152 | + |
135 | 153 | // Optional bias: real buffer if present, else a dummy for the fixed layout. |
136 | 154 | uint32_t has_bias = 0; |
137 | 155 | WGPUBuffer bias_buffer = nullptr; |
@@ -172,7 +190,7 @@ void q4gsw_linear_impl(WebGPUGraph& graph, const std::vector<int>& args) { |
172 | 190 |
|
173 | 191 | WGPUShaderSourceWGSL wgsl_desc = {}; |
174 | 192 | wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL; |
175 | | - wgsl_desc.code = {kQ4gswLinearWGSL, WGPU_STRLEN}; |
| 193 | + wgsl_desc.code = {shader_src, WGPU_STRLEN}; |
176 | 194 | WGPUShaderModuleDescriptor shader_desc = {}; |
177 | 195 | shader_desc.nextInChain = &wgsl_desc.chain; |
178 | 196 | WGPUShaderModule shader = wgpuDeviceCreateShaderModule(device, &shader_desc); |
@@ -210,8 +228,9 @@ void q4gsw_linear_impl(WebGPUGraph& graph, const std::vector<int>& args) { |
210 | 228 | pipeline_desc.layout = pipeline_layout; |
211 | 229 | pipeline_desc.compute.module = shader; |
212 | 230 | pipeline_desc.compute.entryPoint = {"main", WGPU_STRLEN}; |
213 | | - pipeline_desc.compute.constantCount = 1; |
214 | | - pipeline_desc.compute.constants = &wg_size_constant; |
| 231 | + // coop4 GEMV uses fixed @workgroup_size(64); only the GEMM has an override. |
| 232 | + pipeline_desc.compute.constantCount = use_gemv ? 0u : 1u; |
| 233 | + pipeline_desc.compute.constants = use_gemv ? nullptr : &wg_size_constant; |
215 | 234 | WGPUComputePipeline pipeline = |
216 | 235 | wgpuDeviceCreateComputePipeline(device, &pipeline_desc); |
217 | 236 |
|
|
0 commit comments