Skip to content

Commit 90a08cc

Browse files
committed
[ExecuTorch][Vulkan] Tests for et_vk.linear_dW
Pull Request resolved: #20940 Vulkan op-test golden for `et_vk.linear_q4gsw_dw` (ATen `d_out.reshape(-1,N).t() @ x.reshape(-1,K)` reference). 3 cases: tile-aligned, non-tile-multiple (partial-tile clamp), and >2D leading-dim flatten. Wires `linear_q4gsw_dw_test` in `targets.bzl` + `CMakeLists.txt`. ghstack-source-id: 405026090 @exported-using-ghexport Differential Revision: [D111761776](https://our.internmc.facebook.com/intern/diff/D111761776/)
1 parent b9ea96b commit 90a08cc

3 files changed

Lines changed: 119 additions & 0 deletions

File tree

backends/vulkan/test/op_tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ if(TARGET vulkan_backend AND LIB_TORCH)
119119
vulkan_op_test(
120120
adamw_step_test ${CMAKE_CURRENT_SOURCE_DIR}/adamw_step_test.cpp test_utils
121121
)
122+
vulkan_op_test(
123+
linear_dW_test ${CMAKE_CURRENT_SOURCE_DIR}/linear_dW_test.cpp test_utils
124+
)
122125

123126
# Only build generated op tests if a path to tags.yaml and
124127
# native_functions.yaml is provided. These files are required for codegen.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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 <gtest/gtest.h>
10+
11+
#include <ATen/ATen.h>
12+
13+
#include <executorch/backends/vulkan/runtime/api/api.h>
14+
#include <executorch/backends/vulkan/runtime/graph/ComputeGraph.h>
15+
#include <executorch/backends/vulkan/runtime/graph/ops/OperatorRegistry.h>
16+
17+
#include "test_utils.h"
18+
19+
//
20+
// Reference Implementation
21+
//
22+
23+
// Golden: dW[N, K] = d_out^T @ x, contracting over the flattened leading dims.
24+
// Mirrors the CPU-eager linear_dW_impl in custom_ops_lib.py.
25+
at::Tensor linear_dW_reference_impl(
26+
const at::Tensor& d_out,
27+
const at::Tensor& x) {
28+
const int64_t N = d_out.size(-1);
29+
const int64_t K = x.size(-1);
30+
return d_out.reshape({-1, N}).t().matmul(x.reshape({-1, K})).contiguous();
31+
}
32+
33+
//
34+
// Test function
35+
//
36+
37+
void test_vulkan_linear_dW_impl(
38+
const std::vector<int64_t>& d_out_sizes,
39+
const std::vector<int64_t>& x_sizes,
40+
const vkcompute::utils::StorageType storage = vkcompute::utils::kBuffer) {
41+
at::Tensor d_out =
42+
at::rand(d_out_sizes, at::device(at::kCPU).dtype(at::kFloat));
43+
at::Tensor x = at::rand(x_sizes, at::device(at::kCPU).dtype(at::kFloat));
44+
45+
at::Tensor dW_ref = linear_dW_reference_impl(d_out, x);
46+
47+
// Build Vulkan graph
48+
using namespace vkcompute;
49+
50+
GraphConfig config;
51+
ComputeGraph graph(config);
52+
53+
IOValueRef r_d_out = graph.add_input_tensor(
54+
d_out.sizes().vec(), from_at_scalartype(d_out.scalar_type()), storage);
55+
IOValueRef r_x = graph.add_input_tensor(
56+
x.sizes().vec(), from_at_scalartype(x.scalar_type()), storage);
57+
58+
const ValueRef r_dW = graph.add_tensor(
59+
dW_ref.sizes().vec(), from_at_scalartype(dW_ref.scalar_type()), storage);
60+
61+
VK_GET_OP_FN("et_vk.linear_dW.default")
62+
(graph, {r_d_out.value, r_x.value, r_dW});
63+
64+
ValueRef staging_out = graph.set_output_tensor(r_dW);
65+
66+
graph.prepare();
67+
graph.prepack();
68+
graph.propagate_resize();
69+
70+
graph.maybe_cast_and_copy_into_staging(
71+
r_d_out.staging,
72+
d_out.const_data_ptr(),
73+
d_out.numel(),
74+
from_at_scalartype(d_out.scalar_type()));
75+
graph.maybe_cast_and_copy_into_staging(
76+
r_x.staging,
77+
x.const_data_ptr(),
78+
x.numel(),
79+
from_at_scalartype(x.scalar_type()));
80+
81+
graph.execute();
82+
83+
at::Tensor vk_dW = at::empty_like(dW_ref);
84+
graph.maybe_cast_and_copy_from_staging(
85+
staging_out,
86+
vk_dW.mutable_data_ptr(),
87+
vk_dW.numel(),
88+
from_at_scalartype(vk_dW.scalar_type()));
89+
90+
ASSERT_TRUE(at::allclose(vk_dW, dW_ref, 1e-3, 1e-3));
91+
}
92+
93+
// Tile-aligned 2D shapes (M, N, K all multiples of 4).
94+
TEST(VulkanLinearDwTest, test_tile_aligned) {
95+
test_vulkan_linear_dW_impl(
96+
/*d_out_sizes=*/{8, 16}, /*x_sizes=*/{8, 32});
97+
}
98+
99+
// Non-tile-multiple shapes (M, N, K each not a multiple of 4) to exercise the
100+
// partial-tile min()-clamp paths in the shader.
101+
TEST(VulkanLinearDwTest, test_non_tile_multiple) {
102+
test_vulkan_linear_dW_impl(
103+
/*d_out_sizes=*/{5, 6}, /*x_sizes=*/{5, 10});
104+
}
105+
106+
// Leading dims > 2D: M is the flattened product of all leading dims.
107+
TEST(VulkanLinearDwTest, test_leading_dims_flatten) {
108+
test_vulkan_linear_dW_impl(
109+
/*d_out_sizes=*/{2, 3, 16}, /*x_sizes=*/{2, 3, 32});
110+
}

backends/vulkan/test/op_tests/targets.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ def define_common_targets(is_fbcode = False):
186186
":test_utils",
187187
]
188188
)
189+
define_test_targets(
190+
"linear_dW_test",
191+
extra_deps = [
192+
":test_utils",
193+
]
194+
)
189195
define_test_targets(
190196
"rms_norm_test",
191197
extra_deps = [

0 commit comments

Comments
 (0)