Skip to content

Commit 5b33966

Browse files
committed
[ExecuTorch][Vulkan] Add et_vk.linear_dW kernel (dense fp32 weight-grad GEMM)
Pull Request resolved: #20939 Vulkan GLSL kernel + handler for the training custom op `et_vk.linear_q4gsw_dw` — a dense fp32 GEMM `dW[N,K] = d_out^T @ x` (contracts over M; NOT quantized despite the name). Mirrors `impl/Matmul.cpp` `add_matmul_tiled_node` structure + `sizes_ubo` for dynamic shapes; the M-contraction tile math is transcribed from the golden-verified reference. AOT registration already exists under the shared Vulkan partitioner. ghstack-source-id: 405026089 @exported-using-ghexport Differential Revision: [D111761779](https://our.internmc.facebook.com/intern/diff/D111761779/)
1 parent 85ec860 commit 5b33966

3 files changed

Lines changed: 207 additions & 0 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#version 450 core
10+
11+
#define PRECISION ${PRECISION}
12+
13+
#define T ${texel_load_component_type(DTYPE, STORAGE)}
14+
15+
#define TILE_N 4
16+
#define TILE_K 4
17+
18+
${define_required_extensions(STORAGE, DTYPE)}
19+
20+
layout(std430) buffer;
21+
22+
${layout_declare_tensor(B, "w", "t_dw", DTYPE, STORAGE, is_scalar_array=True)}
23+
${layout_declare_tensor(B, "r", "t_dout", DTYPE, STORAGE, is_scalar_array=True)}
24+
${layout_declare_tensor(B, "r", "t_x", DTYPE, STORAGE, is_scalar_array=True)}
25+
26+
${layout_declare_ubo(B, "ivec4", "dout_sizes")}
27+
${layout_declare_ubo(B, "ivec4", "x_sizes")}
28+
29+
layout(local_size_x_id = 0, local_size_y_id = 1, local_size_z_id = 2) in;
30+
31+
void main() {
32+
// dW[N, K] = sum_m d_out[m, N] * x[m, K]; contraction over the flattened M.
33+
const int N = dout_sizes.x;
34+
const int M = dout_sizes.y * dout_sizes.z * dout_sizes.w;
35+
const int K = x_sizes.x;
36+
37+
const int nnt = (N + TILE_N - 1) / TILE_N;
38+
const int nkt = (K + TILE_K - 1) / TILE_K;
39+
const int tiles = nnt * nkt;
40+
41+
const int tile_idx = int(gl_GlobalInvocationID.x);
42+
if (tile_idx >= tiles) {
43+
return;
44+
}
45+
46+
const int n0 = (tile_idx / nkt) * TILE_N;
47+
const int k0 = (tile_idx % nkt) * TILE_K;
48+
49+
float acc[TILE_N * TILE_K];
50+
for (int i = 0; i < TILE_N * TILE_K; ++i) {
51+
acc[i] = 0.0;
52+
}
53+
54+
for (int m = 0; m < M; ++m) {
55+
float dout_reg[TILE_N];
56+
for (int nl = 0; nl < TILE_N; ++nl) {
57+
const int n_eff = min(n0 + nl, N - 1);
58+
dout_reg[nl] = float(t_dout[m * N + n_eff]);
59+
}
60+
for (int kl = 0; kl < TILE_K; ++kl) {
61+
const int k_eff = min(k0 + kl, K - 1);
62+
const float xv = float(t_x[m * K + k_eff]);
63+
for (int nl = 0; nl < TILE_N; ++nl) {
64+
acc[nl * TILE_K + kl] += dout_reg[nl] * xv;
65+
}
66+
}
67+
}
68+
69+
for (int nl = 0; nl < TILE_N; ++nl) {
70+
const int n = n0 + nl;
71+
for (int kl = 0; kl < TILE_K; ++kl) {
72+
const int k = k0 + kl;
73+
if (n < N && k < K) {
74+
t_dw[n * K + k] = T(acc[nl * TILE_K + kl]);
75+
}
76+
}
77+
}
78+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
linear_dW:
8+
parameter_names_with_default_values:
9+
DTYPE: float
10+
STORAGE: buffer
11+
generate_variant_forall:
12+
STORAGE:
13+
- VALUE: buffer
14+
DTYPE:
15+
- VALUE: float
16+
shader_variants:
17+
- NAME: linear_dW
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <executorch/backends/vulkan/runtime/graph/ops/OperatorRegistry.h>
10+
11+
#include <executorch/backends/vulkan/runtime/graph/ops/impl/Common.h>
12+
13+
#include <executorch/backends/vulkan/runtime/graph/ops/utils/ShaderNameUtils.h>
14+
15+
namespace vkcompute {
16+
17+
void resize_linear_dW_node(
18+
ComputeGraph* graph,
19+
const std::vector<ArgGroup>& args,
20+
const std::vector<ValueRef>& resize_args) {
21+
(void)resize_args;
22+
const ValueRef dW = args.at(0).refs.at(0);
23+
const ValueRef d_out = args.at(1).refs.at(0);
24+
const ValueRef x = args.at(1).refs.at(1);
25+
const int64_t N = graph->size_at<int64_t>(-1, d_out);
26+
const int64_t K = graph->size_at<int64_t>(-1, x);
27+
graph->virtual_resize(dW, {N, K});
28+
}
29+
30+
utils::uvec3 linear_dW_global_wg_size(
31+
ComputeGraph* graph,
32+
const vkapi::ShaderInfo& shader,
33+
const std::vector<ArgGroup>& args,
34+
const std::vector<ValueRef>& resize_args) {
35+
(void)shader;
36+
(void)resize_args;
37+
const ValueRef dW = args.at(0).refs.at(0);
38+
const uint32_t N = graph->size_at<uint32_t>(-2, dW);
39+
const uint32_t K = graph->size_at<uint32_t>(-1, dW);
40+
const uint32_t tiles = utils::div_up_4(N) * utils::div_up_4(K);
41+
return {tiles, 1u, 1u};
42+
}
43+
44+
utils::uvec3 linear_dW_local_wg_size(
45+
ComputeGraph* graph,
46+
const vkapi::ShaderInfo& shader,
47+
const utils::uvec3& global_workgroup_size,
48+
const std::vector<ArgGroup>& args,
49+
const std::vector<ValueRef>& resize_args) {
50+
(void)graph;
51+
(void)shader;
52+
(void)global_workgroup_size;
53+
(void)args;
54+
(void)resize_args;
55+
return {64u, 1u, 1u};
56+
}
57+
58+
void linear_dW(ComputeGraph& graph, const std::vector<ValueRef>& args) {
59+
int32_t i = 0;
60+
const ValueRef d_out = args.at(i++);
61+
const ValueRef x = args.at(i++);
62+
const ValueRef dW = args.at(i);
63+
64+
VK_CHECK_COND(graph.dtype_of(d_out) == vkapi::kFloat);
65+
VK_CHECK_COND(graph.dtype_of(x) == vkapi::kFloat);
66+
VK_CHECK_COND(graph.dtype_of(dW) == vkapi::kFloat);
67+
VK_CHECK_COND(graph.is_buffer_storage(d_out));
68+
VK_CHECK_COND(graph.is_buffer_storage(x));
69+
VK_CHECK_COND(graph.is_buffer_storage(dW));
70+
71+
const int64_t N = graph.size_at<int64_t>(-1, d_out);
72+
const int64_t K = graph.size_at<int64_t>(-1, x);
73+
VK_CHECK_COND(N > 0 && K > 0);
74+
VK_CHECK_COND(graph.numel_of(d_out) % N == 0);
75+
VK_CHECK_COND(graph.numel_of(x) % K == 0);
76+
VK_CHECK_COND(graph.numel_of(d_out) / N == graph.numel_of(x) / K);
77+
78+
const uint32_t tiles = utils::div_up_4(static_cast<uint32_t>(N)) *
79+
utils::div_up_4(static_cast<uint32_t>(K));
80+
VK_CHECK_COND(
81+
(tiles + 63u) / 64u <= 65535u,
82+
"linear_dW: tile count exceeds max workgroup count");
83+
84+
std::string kernel_name = "linear_dW";
85+
kernel_name.reserve(kShaderNameReserve);
86+
add_storage_type_suffix(kernel_name, graph.storage_type_of(dW));
87+
add_dtype_suffix(kernel_name, graph.dtype_of(dW));
88+
89+
graph.execute_nodes().emplace_back(new DynamicDispatchNode(
90+
graph,
91+
VK_KERNEL_FROM_STR(kernel_name),
92+
linear_dW_global_wg_size,
93+
linear_dW_local_wg_size,
94+
// Inputs and Outputs
95+
{{dW, vkapi::kWrite}, {{d_out, x}, vkapi::kRead}},
96+
// Shader params buffers
97+
{graph.sizes_ubo(d_out), graph.sizes_ubo(x)},
98+
// Push Constants
99+
{},
100+
// Specialization Constants
101+
{},
102+
// Resize Args
103+
{},
104+
// Resizing Logic
105+
resize_linear_dW_node));
106+
}
107+
108+
REGISTER_OPERATORS {
109+
VK_REGISTER_OP(et_vk.linear_dW.default, linear_dW);
110+
}
111+
112+
} // namespace vkcompute

0 commit comments

Comments
 (0)