1+ // SPDX-License-Identifier: MIT
2+ // Copyright (C) 2024-2025, Advanced Micro Devices, Inc. All rights reserved.
3+
4+ #include " deepgemm_common.cuh"
5+ #include " deepgemm_lookup.h"
6+ #include " deepgemm_manifest.h"
7+ #include < cmath>
8+ #include " py_itfs_common.h"
9+
10+ using RowwiseKernel = std::function<
11+ torch::Tensor (torch::Tensor &, torch::Tensor &,
12+ torch::Tensor &, torch::Tensor &,
13+ std::optional<torch::Tensor>, std::optional<torch::Tensor>)>;
14+
15+ // Define a custom hash function for std::tuple<int, int, int>
16+ struct IntTupleHash
17+ {
18+ size_t operator ()(const std::tuple<int , int , int > &t) const
19+ {
20+ auto hash1 = std::hash<int >{}(std::get<0 >(t));
21+ auto hash2 = std::hash<int >{}(std::get<1 >(t));
22+ auto hash3 = std::hash<int >{}(std::get<2 >(t));
23+ return hash1 ^ hash2 ^ hash3;
24+ }
25+ };
26+
27+ // For certain high priority shapes, we directly use the best kernel rather
28+ // than use heuristics.
29+ using RowwiseKernelMap = std::unordered_map<
30+ std::tuple<int , int , int >,
31+ RowwiseKernel,
32+ IntTupleHash>;
33+
34+ template <typename ABDataType, typename AccDataType, typename CDataType>
35+ RowwiseKernel rowwise_heuristic_dispatch (int M, int N, int K)
36+ {
37+ // Apply shape heuristics to find a suitable kernel implementation.
38+ if (M < 128 )
39+ {
40+ return deepgemm_256x32x64x256_16x16x64_1x4<ABDataType, AccDataType, CDataType>;
41+ }
42+ else
43+ {
44+ return deepgemm_256x128x128x128_16x16x64_1x4<ABDataType, AccDataType, CDataType>;
45+ }
46+ }
47+
48+ // Helper function to return the next largest power of 2
49+ static constexpr int nextPow2 (unsigned int num)
50+ {
51+ if (num <= 1 )
52+ return 1 ;
53+ return 1 << (CHAR_BIT * sizeof (num) - __builtin_clz (num - 1 ));
54+ }
55+
56+ template <typename ABDataType, typename AccDataType, typename CDataType>
57+ RowwiseKernel rowwise_dispatch (int M, int N, int K)
58+ {
59+ // TODO: add tuner @lalala-sh
60+ // For a given shape, either find the best kernel via lookup or heuristic.
61+ // For many small M shapes, we bucket them to the next largest kernel.
62+ // This is fine since kernels are padded anyway.
63+
64+ // static const auto lookup = [&]
65+ // {
66+ // return RowwiseKernelMap{GENERATE_LOOKUP_TABLE(ABDataType, AccDataType, CDataType)};
67+ // }();
68+
69+ // // First check if this shape(M,N,K) is available in the direct lookup.
70+ // auto it = lookup.find({M, N, K});
71+ // // If we found an optimal kernel, use it.
72+ // if (it != lookup.end())
73+ // {
74+ // return it->second;
75+ // }
76+
77+ // int padded_m = M;
78+ // if (M > 1 && M <= 16)
79+ // {
80+ // padded_m = 16;
81+ // }
82+ // else if (M <= 16384)
83+ // {
84+ // padded_m = nextPow2(M);
85+ // }
86+ // else if (M <= 20480)
87+ // {
88+ // padded_m = 20480;
89+ // }
90+ // // Second check if this shape(padded_m,N,K) is available in the direct lookup.
91+ // it = lookup.find({padded_m, N, K});
92+ // // If we found an optimal kernel, use it.
93+ // if (it != lookup.end())
94+ // {
95+ // return it->second;
96+ // }
97+ // Otherwise, use heuristics.
98+ return rowwise_heuristic_dispatch<ABDataType, AccDataType, CDataType>(M, N, K);
99+ }
100+
101+ torch::Tensor deepgemm (
102+ torch::Tensor &XQ ,
103+ torch::Tensor &WQ ,
104+ torch::Tensor &Y,
105+ torch::Tensor &grouped_layout,
106+ std::optional<torch::Tensor> x_scale,
107+ std::optional<torch::Tensor> w_scale)
108+ {
109+ TORCH_CHECK (XQ .dtype () == WQ .dtype (),
110+ " Weights and activations should both be int8/fp8!" );
111+ if (x_scale != std::nullopt && w_scale != std::nullopt )
112+ TORCH_CHECK (x_scale.value ().dtype () == w_scale.value ().dtype (),
113+ " Scales should have the same dtype!" );
114+
115+ int M = XQ .size (0 );
116+ int N = WQ .size (0 );
117+ int K = XQ .size (1 );
118+ int KBatch = 1 ;
119+
120+
121+
122+ if (XQ .dtype () == at::ScalarType::BFloat16 || XQ .dtype () == at::ScalarType::Half)
123+ {
124+ if (XQ .dtype () == at::ScalarType::Half)
125+ {
126+ rowwise_dispatch<fp16, float , fp16>(M, N, K)(XQ , WQ , Y, grouped_layout, x_scale, w_scale);
127+ }
128+ else
129+ {
130+ rowwise_dispatch<bf16 , float , bf16 >(M, N, K)(XQ , WQ , Y, grouped_layout, x_scale, w_scale);
131+ }
132+ }
133+ else if (XQ .dtype () == torch_fp8)
134+ {
135+ if (Y.dtype () == at::ScalarType::Half)
136+ {
137+ rowwise_dispatch<fp8, float , fp16>(M, N, K)(XQ , WQ , Y, grouped_layout, x_scale, w_scale);
138+ }
139+ else if (Y.dtype () == at::ScalarType::BFloat16)
140+ {
141+ rowwise_dispatch<fp8, float , bf16 >(M, N, K)(XQ , WQ , Y, grouped_layout, x_scale, w_scale);
142+ }
143+ }
144+ else
145+ {
146+ TORCH_CHECK (false , " Unsupported scales/output dtype!" );
147+ }
148+ return Y;
149+ }
0 commit comments