main
h 299 lines 8.34 KB
Raw
1 // Adapted from
2 // https://github.com/sgl-project/sglang/tree/main/sgl-kernel/csrc/cpu
3
4 // clang-format off
5
6 // To use the transpose functions
7 #include <ATen/native/cpu/utils.h>
8
9 #include "vec.h"
10
11 namespace {
12
13 using namespace at::vec;
14
15 template <typename index_t>
16 inline index_t get_index(index_t* ind, int i) {
17 return (ind == nullptr) ? (index_t)i : ind[i];
18 }
19
20 #if defined(CPU_CAPABILITY_AVX512)
21 // key: from [N, 32] to [32/2, N, 2]
22 template <typename scalar_t, typename index_t>
23 inline void pack_vnni_Nx32(
24 scalar_t* __restrict__ dst,
25 const scalar_t* __restrict__ src,
26 const index_t* __restrict__ ind,
27 int N,
28 int ld_src,
29 int ld_dst) {
30 __m512i vinputs[16];
31
32 int n = 0;
33 for (; n < N; ++n) {
34 index_t index = get_index(ind, n);
35 vinputs[n] = _mm512_loadu_si512(src + index * ld_src);
36 }
37 // padding with zero to avoid uninitialized vectors
38 for (; n < 16; ++n) {
39 vinputs[n] = _mm512_set1_epi32(0);
40 }
41
42 // pack key
43 transpose_16x16_32bit(vinputs);
44
45 const __mmask16 vmask = (1 << N) - 1;
46 for (int k = 0; k < 16; ++k) {
47 _mm512_mask_storeu_epi32(dst + k * ld_dst * 2, vmask, vinputs[k]);
48 }
49 }
50
51 template <typename scalar_t, typename index_t>
52 inline void pack_vnni_N_remainder(
53 scalar_t* __restrict__ dst,
54 const scalar_t* __restrict__ src,
55 const index_t* __restrict__ ind,
56 int N,
57 int K,
58 int ld_src,
59 int ld_dst) {
60 __m512i vinputs[16];
61
62 int K2 = K >> 1;
63 const __mmask16 vmask = (1 << K2) - 1;
64
65 int n = 0;
66 for (; n < N; ++n) {
67 index_t index = get_index(ind, n);
68 vinputs[n] = _mm512_maskz_loadu_epi32(vmask, src + index * ld_src);
69 }
70 // padding with zero to avoid uninitialized vectors
71 for (; n < 16; ++n) {
72 vinputs[n] = _mm512_set1_epi32(0);
73 }
74
75 // pack key
76 transpose_16x16_32bit(vinputs);
77
78 const __mmask16 vmask2 = (1 << N) - 1;
79 for (int k = 0; k < K2; ++k) {
80 _mm512_mask_storeu_epi32(dst + k * ld_dst * 2, vmask2, vinputs[k]);
81 }
82 }
83
84 // value: from [K, 32] to [K/2, 32, 2]
85 template <typename scalar_t, typename index_t>
86 inline void pack_vnni_Kx32(
87 scalar_t* __restrict__ dst,
88 const scalar_t* __restrict__ src,
89 const index_t* __restrict__ ind,
90 int K,
91 int ld_src,
92 int ld_dst) {
93 __m512i vinputs[2];
94
95 int k = 0;
96 for (; k < K; ++k) {
97 index_t index = get_index(ind, k);
98 vinputs[k] = _mm512_loadu_si512(src + index * ld_src);
99 }
100 // padding with zero to avoid uninitialized vectors
101 for (; k < 2; ++k) {
102 vinputs[k] = _mm512_set1_epi32(0);
103 }
104
105 // pack value
106 __m512i d0, d1;
107 std::tie(d0, d1) = transpose_2x32_16bit(vinputs[0], vinputs[1]);
108 _mm512_storeu_si512(dst + 0 * ld_dst * 2, d0);
109 _mm512_storeu_si512(dst + 0 * ld_dst * 2 + 32, d1);
110 }
111
112 template <typename scalar_t, typename index_t>
113 inline void pack_vnni_K_remainder(
114 scalar_t* __restrict__ dst,
115 const scalar_t* __restrict__ src,
116 const index_t* __restrict__ ind,
117 int K,
118 int N,
119 int ld_src,
120 int ld_dst) {
121 __m512i vinputs[2];
122
123 const __mmask32 vmask = (1 << N) - 1;
124
125 int k = 0;
126 for (; k < K; ++k) {
127 index_t index = get_index(ind, k);
128 vinputs[k] = _mm512_maskz_loadu_epi16(vmask, src + index * ld_src);
129 }
130 // padding with zero to avoid uninitialized vectors
131 for (; k < 2; ++k) {
132 vinputs[k] = _mm512_set1_epi32(0);
133 }
134
135 // pack value
136 __m512i d0, d1;
137 std::tie(d0, d1) = transpose_2x32_16bit(vinputs[0], vinputs[1]);
138
139 if (N <= 16) {
140 // 2N * 16bits: N * 32bits
141 const __mmask16 vmask2 = (1 << N) - 1;
142 _mm512_mask_storeu_epi32(dst + 0 * ld_dst * 2, vmask2, d0);
143 } else {
144 // 2(N-16) * 16bits: (N-16) * 32bits
145 const __mmask16 vmask2 = (1 << (N - 16)) - 1;
146 _mm512_storeu_epi32(dst + 0 * ld_dst * 2, d0);
147 _mm512_mask_storeu_epi32(dst + 0 * ld_dst * 2 + 32, vmask2, d1);
148 }
149 }
150 #endif
151
152 // convert to vnni format
153 // from [N, K/2, 2] to [K/2, N, 2] for bfloat16 and float16
154 template <typename scalar_t, typename index_t, bool is_indexed>
155 void pack_vnni(
156 scalar_t* __restrict__ dst,
157 const scalar_t* __restrict__ src,
158 const index_t* __restrict__ ind,
159 int N,
160 int K,
161 int ld_src,
162 int ld_dst) {
163 #if defined(CPU_CAPABILITY_AVX512)
164 const int NB = div_up(N, 16);
165 const int KB = K / 32;
166 const int K_remainder = K - KB * 32;
167
168 for (int nb = 0; nb < NB; ++nb) {
169 int nb_size = std::min(N - nb * 16, 16);
170 for (int kb = 0; kb < KB; ++kb) {
171 // handle 16x512bits each block
172 pack_vnni_Nx32<scalar_t, index_t>(
173 /* dst */ dst + ((kb * 32) >> 1) * ld_dst * 2 + nb * 16 * 2,
174 /* src */ src + kb * 32 + (is_indexed ? 0 : nb * 16 * ld_src),
175 /* ind */ is_indexed ? ind + nb * 16 : nullptr,
176 /* N */ nb_size,
177 /* ld_src */ ld_src,
178 /* ld_dst */ ld_dst);
179 }
180 if (K_remainder > 0) {
181 pack_vnni_N_remainder<scalar_t, index_t>(
182 /* dst */ dst + ((KB * 32) >> 1) * ld_dst * 2 + nb * 16 * 2,
183 /* src */ src + KB * 32 + (is_indexed ? 0 : nb * 16 * ld_src),
184 /* ind */ is_indexed ? ind + nb * 16 : nullptr,
185 /* N */ nb_size,
186 /* K */ K_remainder,
187 /* ld_src */ ld_src,
188 /* ld_dst */ ld_dst);
189 }
190 }
191 #else
192 for (int n = 0; n < N; ++n) {
193 index_t index = get_index(ind, n);
194 for (int k = 0; k < K / 2; ++k) {
195 for (int d = 0; d < 2; ++d) {
196 dst[k * ld_dst * 2 + n * 2 + d] = src[index * ld_src + k * 2 + d];
197 }
198 }
199 }
200 #endif
201 }
202
203 template <typename scalar_t>
204 void pack_vnni(scalar_t* __restrict__ dst, const scalar_t* __restrict__ src, int N, int K, int ld_src, int ld_dst) {
205 pack_vnni<scalar_t, int32_t, false>(dst, src, nullptr, N, K, ld_src, ld_dst);
206 }
207
208 template <typename scalar_t, typename index_t>
209 void pack_vnni(
210 scalar_t* __restrict__ dst,
211 const scalar_t* __restrict__ src,
212 const index_t* __restrict__ ind,
213 int N,
214 int K,
215 int ld_src,
216 int ld_dst) {
217 assert(ind != nullptr);
218 pack_vnni<scalar_t, index_t, true>(dst, src, ind, N, K, ld_src, ld_dst);
219 }
220
221 // convert to vnni format
222 // from [K/2, 2, N] to [K/2, N, 2] for bfloat16 and float16
223 template <typename scalar_t, typename index_t, bool is_indexed>
224 void pack_vnni2(
225 scalar_t* __restrict__ dst,
226 const scalar_t* __restrict__ src,
227 const index_t* __restrict__ ind,
228 int K,
229 int N,
230 int ld_src,
231 int ld_dst) {
232 #if defined(CPU_CAPABILITY_AVX512)
233 const int KB = div_up(K, 2);
234 const int NB = N / 32;
235 const int N_remainder = N - NB * 32;
236
237 for (int kb = 0; kb < KB; ++kb) {
238 int kb_size = std::min(K - kb * 2, 2);
239 for (int nb = 0; nb < NB; ++nb) {
240 // handle 2x512bits each block
241 pack_vnni_Kx32<scalar_t, index_t>(
242 /* dst */ dst + ((kb * 2) >> 1) * ld_dst * 2 + nb * 32 * 2,
243 /* src */ src + (is_indexed ? 0 : kb * 2 * ld_src) + nb * 32,
244 /* ind */ is_indexed ? ind + kb * 2 : nullptr,
245 /* K */ kb_size,
246 /* ld_src */ ld_src,
247 /* ld_dst */ ld_dst);
248 }
249 if (N_remainder > 0) {
250 pack_vnni_K_remainder(
251 /* dst */ dst + ((kb * 2) >> 1) * ld_dst * 2 + NB * 32 * 2,
252 /* src */ src + (is_indexed ? 0 : kb * 2 * ld_src) + NB * 32,
253 /* ind */ is_indexed ? ind + kb * 2 : nullptr,
254 /* K */ kb_size,
255 /* N */ N_remainder,
256 /* ld_src */ ld_src,
257 /* ld_dst */ ld_dst);
258 }
259 }
260 #else
261 int k = 0;
262 for (; k < (K >> 1) * 2; k += 2) {
263 index_t index0 = get_index(ind, k + 0);
264 index_t index1 = get_index(ind, k + 1);
265 for (int n = 0; n < N; ++n) {
266 dst[(k >> 1) * ld_dst * 2 + n * 2 + 0] = src[index0 * ld_src + n];
267 dst[(k >> 1) * ld_dst * 2 + n * 2 + 1] = src[index1 * ld_src + n];
268 }
269 }
270 if (K % 2 != 0) {
271 index_t index = get_index(ind, K - 1);
272 for (int n = 0; n < N; ++n) {
273 dst[(K >> 1) * ld_dst * 2 + n * 2 + 0] = src[index * ld_src + n];
274 dst[(K >> 1) * ld_dst * 2 + n * 2 + 1] = 0;
275 }
276 k += 2;
277 }
278 #endif
279 }
280
281 template <typename scalar_t>
282 void pack_vnni2(scalar_t* __restrict__ dst, const scalar_t* __restrict__ src, int K, int N, int ld_src, int ld_dst) {
283 pack_vnni2<scalar_t, int32_t, false>(dst, src, nullptr, K, N, ld_src, ld_dst);
284 }
285
286 template <typename scalar_t, typename index_t>
287 void pack_vnni2(
288 scalar_t* __restrict__ dst,
289 const scalar_t* __restrict__ src,
290 const index_t* __restrict__ ind,
291 int K,
292 int N,
293 int ld_src,
294 int ld_dst) {
295 assert(ind != nullptr);
296 pack_vnni2<scalar_t, index_t, true>(dst, src, ind, K, N, ld_src, ld_dst);
297 }
298
299 } // anonymous namespace