← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
recurrent_qk_norm_kernels.c File Reference
#include "bf16_utils.h"
#include "ckernel_engine.h"
#include "ck_threadpool.h"
#include <limits.h>
#include <math.h>
#include <stdint.h>

Go to the source code of this file.

Functions

static int recurrent_ceil_log2 (int value)
 
static void recurrent_l2_norm_rows_backward_one (const float *d_out, const float *x, float *d_x, int rows, int dim, int head_dim, float eps)
 
static void recurrent_l2_norm_rows_forward_one (float *x, int rows, int dim, int head_dim, float eps)
 
static void recurrent_pytorch_bf16_l2_rows (float *x, int rows, int dim, int expanded_heads, int head_dim, float eps)
 
static float recurrent_pytorch_bf16_square_sum (const float *x, int dim)
 
static void recurrent_pytorch_fp32_l2_rows (float *x, int rows, int dim, int head_dim, float eps)
 
static float recurrent_pytorch_fp32_square_sum (const float *x, int dim)
 
void recurrent_qk_l2_norm_backward (const float *d_q_out, const float *d_k_out, const float *q, const float *k, float *d_q, float *d_k, int rows, int q_dim, int k_dim, int head_dim, float eps)
 
void recurrent_qk_l2_norm_forward (float *q, float *k, int rows, int q_dim, int k_dim, int head_dim, float eps)
 
static void recurrent_qk_l2_norm_head_range (int begin, int end, void *opaque)
 
void recurrent_qk_l2_norm_pytorch_bf16_storage (float *q, float *k, int rows, int q_dim, int k_dim, int expanded_heads, int head_dim, float eps)
 
void recurrent_qk_l2_norm_pytorch_fp32_output (float *q, float *k, int rows, int q_dim, int k_dim, int head_dim, float eps)
 

Function Documentation

◆ recurrent_ceil_log2()

static int recurrent_ceil_log2 ( int  value)
static

Definition at line 186 of file recurrent_qk_norm_kernels.c.

187{
188 int result = 0;
189 int remaining = value - 1;
190 while (remaining > 0) {
191 ++result;
192 remaining >>= 1;
193 }
194 return result;
195}

Referenced by recurrent_pytorch_fp32_square_sum().

◆ recurrent_l2_norm_rows_backward_one()

static void recurrent_l2_norm_rows_backward_one ( const float *  d_out,
const float *  x,
float *  d_x,
int  rows,
int  dim,
int  head_dim,
float  eps 
)
static

Definition at line 78 of file recurrent_qk_norm_kernels.c.

84 {
85 if (!d_out || !x || !d_x || rows <= 0 || dim <= 0 || head_dim <= 0) {
86 return;
87 }
88 const int num_heads = dim / head_dim;
89 if (num_heads <= 0 || num_heads * head_dim != dim) {
90 return;
91 }
92
93 for (int row = 0; row < rows; ++row) {
94 const float *d_row = d_out + (size_t) row * (size_t) dim;
95 const float *x_row = x + (size_t) row * (size_t) dim;
96 float *dx_row = d_x + (size_t) row * (size_t) dim;
97 for (int head = 0; head < num_heads; ++head) {
98 const float *d_head = d_row + (size_t) head * (size_t) head_dim;
99 const float *x_head = x_row + (size_t) head * (size_t) head_dim;
100 float *dx_head = dx_row + (size_t) head * (size_t) head_dim;
101
102 double sum_sq = 0.0;
103 double dot = 0.0;
104 for (int col = 0; col < head_dim; ++col) {
105 sum_sq += (double) (x_head[col] * x_head[col]);
106 dot += (double) (d_head[col] * x_head[col]);
107 }
108 const float norm = sqrtf((float) sum_sq);
109 const float inv_norm = 1.0f / fmaxf(norm, eps);
110 if (norm <= eps) {
111 for (int col = 0; col < head_dim; ++col) {
112 dx_head[col] = d_head[col] / eps;
113 }
114 continue;
115 }
116 const float proj_scale = inv_norm * inv_norm * inv_norm * (float) dot;
117 for (int col = 0; col < head_dim; ++col) {
118 dx_head[col] = inv_norm * d_head[col] - proj_scale * x_head[col];
119 }
120 }
121 }
122}

Referenced by recurrent_qk_l2_norm_backward().

◆ recurrent_l2_norm_rows_forward_one()

static void recurrent_l2_norm_rows_forward_one ( float *  x,
int  rows,
int  dim,
int  head_dim,
float  eps 
)
static

Definition at line 13 of file recurrent_qk_norm_kernels.c.

17 {
18 const int num_heads = dim / head_dim;
19 for (int row = 0; row < rows; ++row) {
20 float *row_ptr = x + (size_t)row * (size_t)dim;
21 for (int head = 0; head < num_heads; ++head) {
22 float *head_ptr = row_ptr + (size_t)head * (size_t)head_dim;
23 double sum_sq = 0.0;
24 for (int col = 0; col < head_dim; ++col) {
25 sum_sq += (double)(head_ptr[col] * head_ptr[col]);
26 }
27 const float norm = sqrtf((float)sum_sq);
28 const float inv_norm = 1.0f / fmaxf(norm, eps);
29 for (int col = 0; col < head_dim; ++col) {
30 head_ptr[col] *= inv_norm;
31 }
32 }
33 }
34}

Referenced by recurrent_qk_l2_norm_forward().

◆ recurrent_pytorch_bf16_l2_rows()

static void recurrent_pytorch_bf16_l2_rows ( float *  x,
int  rows,
int  dim,
int  expanded_heads,
int  head_dim,
float  eps 
)
static

Definition at line 396 of file recurrent_qk_norm_kernels.c.

402{
403 if (!x || rows <= 0 || dim <= 0 || head_dim <= 0 ||
404 dim % head_dim != 0) {
405 return;
406 }
407 const int num_heads = dim / head_dim;
408 if (expanded_heads <= 0 || expanded_heads % num_heads != 0) {
409 return;
410 }
411 const int heads_per_group = expanded_heads / num_heads;
412 const int total_heads = rows * expanded_heads;
413 const int vector_limit = (total_heads / 32) * 32;
414 for (int row = 0; row < rows; ++row) {
415 float *row_ptr = x + (size_t)row * (size_t)dim;
416 for (int head = 0; head < num_heads; ++head) {
417 float *head_ptr =
418 row_ptr + (size_t)head * (size_t)head_dim;
420 head_ptr, head_dim);
421 sum = bf16_to_float(float_to_bf16(sum));
422 const float denominator =
423 bf16_to_float(float_to_bf16(sum + eps));
424 /*
425 * Qwen3Next repeat_interleave() expands compact Q/K groups to
426 * value heads before l2norm. TensorIterator's rsqrt tail is
427 * therefore determined by the expanded tensor, not this compact
428 * storage. Select the arithmetic used by the first repeated
429 * lane; all lanes are vector lanes for production prefill.
430 */
431 const int flat_head =
432 (row * expanded_heads) + head * heads_per_group;
433 float inverse;
434 if (flat_head < vector_limit) {
435 /* ATen Vec<BF16> evaluates 32 scalar outputs per vector
436 * iteration using FP32 rsqrt followed by BF16 storage. */
437 inverse = bf16_to_float(
438 float_to_bf16(1.0f / sqrtf(denominator)));
439 } else {
440 /* TensorIterator's BF16 scalar tail materializes sqrt to
441 * BF16 before applying the BF16 reciprocal. */
442 const float root =
443 bf16_to_float(float_to_bf16(sqrtf(denominator)));
444 inverse =
445 bf16_to_float(float_to_bf16(1.0f / root));
446 }
447 for (int col = 0; col < head_dim; ++col) {
448 const float value =
449 bf16_to_float(float_to_bf16(head_ptr[col]));
450 head_ptr[col] = bf16_to_float(
451 float_to_bf16(value * inverse));
452 }
453 }
454 }
455}
static uint16_t float_to_bf16(float f)
Definition bf16_utils.h:90
static float bf16_to_float(uint16_t v)
Definition bf16_utils.h:38
static float recurrent_pytorch_bf16_square_sum(const float *x, int dim)

References bf16_to_float(), float_to_bf16(), and recurrent_pytorch_bf16_square_sum().

Referenced by recurrent_qk_l2_norm_pytorch_bf16_storage().

◆ recurrent_pytorch_bf16_square_sum()

static float recurrent_pytorch_bf16_square_sum ( const float *  x,
int  dim 
)
static

Definition at line 347 of file recurrent_qk_norm_kernels.c.

348{
349#if defined(__AVX2__)
350 if (dim >= 32) {
351 __m256 streams[4] = {
352 _mm256_setzero_ps(), _mm256_setzero_ps(),
353 _mm256_setzero_ps(), _mm256_setzero_ps()
354 };
355 int d = 0;
356 for (; d + 32 <= dim; d += 32) {
357 for (int stream = 0; stream < 4; ++stream) {
358 _Alignas(32) float lanes[8];
359 const __m256 values = _mm256_loadu_ps(x + d + stream * 8);
360 _mm256_store_ps(lanes, _mm256_mul_ps(values, values));
361 for (int lane = 0; lane < 8; ++lane) {
362 lanes[lane] =
363 bf16_to_float(float_to_bf16(lanes[lane]));
364 }
365 streams[stream] =
366 _mm256_add_ps(streams[stream], _mm256_load_ps(lanes));
367 }
368 }
369 __m256 reduced = _mm256_add_ps(streams[0], streams[1]);
370 reduced = _mm256_add_ps(reduced, streams[2]);
371 reduced = _mm256_add_ps(reduced, streams[3]);
372 _Alignas(32) float lanes[8];
373 _mm256_store_ps(lanes, reduced);
374 volatile float sum = 0.0f;
375 for (int lane = 0; lane < 8; ++lane) {
376 sum = sum + lanes[lane];
377 }
378 for (; d < dim; ++d) {
379 const float value = bf16_to_float(float_to_bf16(x[d]));
380 const float square =
381 bf16_to_float(float_to_bf16(value * value));
382 sum = sum + square;
383 }
384 return sum;
385 }
386#endif
387 volatile float sum = 0.0f;
388 for (int d = 0; d < dim; ++d) {
389 const float value = bf16_to_float(float_to_bf16(x[d]));
390 const float square = bf16_to_float(float_to_bf16(value * value));
391 sum = sum + square;
392 }
393 return sum;
394}

References bf16_to_float(), and float_to_bf16().

Referenced by recurrent_pytorch_bf16_l2_rows().

◆ recurrent_pytorch_fp32_l2_rows()

static void recurrent_pytorch_fp32_l2_rows ( float *  x,
int  rows,
int  dim,
int  head_dim,
float  eps 
)
static

Definition at line 286 of file recurrent_qk_norm_kernels.c.

291{
292 if (!x || rows <= 0 || dim <= 0 || head_dim <= 0 ||
293 dim % head_dim != 0) {
294 return;
295 }
296 const int num_heads = dim / head_dim;
297 for (int row = 0; row < rows; ++row) {
298 float *row_ptr = x + (size_t)row * (size_t)dim;
299 for (int head = 0; head < num_heads; ++head) {
300 float *head_ptr = row_ptr + (size_t)head * (size_t)head_dim;
301 const float sum = recurrent_pytorch_fp32_square_sum(
302 head_ptr, head_dim);
303 const float inverse = 1.0f / sqrtf(sum + eps);
304#if defined(__AVX512F__)
305 const __m512 inv = _mm512_set1_ps(inverse);
306 int d = 0;
307 for (; d + 16 <= head_dim; d += 16) {
308 _mm512_storeu_ps(
309 head_ptr + d,
310 _mm512_mul_ps(_mm512_loadu_ps(head_ptr + d), inv));
311 }
312 for (; d < head_dim; ++d) {
313 head_ptr[d] = head_ptr[d] * inverse;
314 }
315#elif defined(__AVX2__)
316 const __m256 inv = _mm256_set1_ps(inverse);
317 int d = 0;
318 for (; d + 8 <= head_dim; d += 8) {
319 _mm256_storeu_ps(
320 head_ptr + d,
321 _mm256_mul_ps(_mm256_loadu_ps(head_ptr + d), inv));
322 }
323 for (; d < head_dim; ++d) {
324 head_ptr[d] = head_ptr[d] * inverse;
325 }
326#else
327 for (int d = 0; d < head_dim; ++d) {
328 head_ptr[d] = head_ptr[d] * inverse;
329 }
330#endif
331 }
332 }
333}
static float recurrent_pytorch_fp32_square_sum(const float *x, int dim)

References recurrent_pytorch_fp32_square_sum().

Referenced by recurrent_qk_l2_norm_pytorch_fp32_output().

◆ recurrent_pytorch_fp32_square_sum()

static float recurrent_pytorch_fp32_square_sum ( const float *  x,
int  dim 
)
static

Definition at line 197 of file recurrent_qk_norm_kernels.c.

198{
199#if defined(__AVX2__)
200 if (dim >= 8) {
201 enum { ilp_factor = 4, num_levels = 4, vector_width = 8 };
202 const int vector_count = dim / vector_width;
203 const int cascade_count = vector_count / ilp_factor;
204 const int level_power =
205 recurrent_ceil_log2(cascade_count) / num_levels > 4
206 ? recurrent_ceil_log2(cascade_count) / num_levels
207 : 4;
208 const int level_step = 1 << level_power;
209 const int level_mask = level_step - 1;
210 __m256 acc[num_levels][ilp_factor];
211 for (int level = 0; level < num_levels; ++level) {
212 for (int lane = 0; lane < ilp_factor; ++lane) {
213 acc[level][lane] = _mm256_setzero_ps();
214 }
215 }
216
217 int group = 0;
218 while (group + level_step <= cascade_count) {
219 for (int offset = 0; offset < level_step; ++offset, ++group) {
220 for (int lane = 0; lane < ilp_factor; ++lane) {
221 const int vector_index = group * ilp_factor + lane;
222 const __m256 value = _mm256_loadu_ps(
223 x + vector_index * vector_width);
224 const __m256 square = _mm256_mul_ps(value, value);
225 acc[0][lane] = _mm256_add_ps(acc[0][lane], square);
226 }
227 }
228 for (int level = 1; level < num_levels; ++level) {
229 for (int lane = 0; lane < ilp_factor; ++lane) {
230 acc[level][lane] =
231 _mm256_add_ps(acc[level][lane], acc[level - 1][lane]);
232 acc[level - 1][lane] = _mm256_setzero_ps();
233 }
234 const int mask = level_mask << (level * level_power);
235 if ((group & mask) != 0) {
236 break;
237 }
238 }
239 }
240 for (; group < cascade_count; ++group) {
241 for (int lane = 0; lane < ilp_factor; ++lane) {
242 const int vector_index = group * ilp_factor + lane;
243 const __m256 value = _mm256_loadu_ps(
244 x + vector_index * vector_width);
245 const __m256 square = _mm256_mul_ps(value, value);
246 acc[0][lane] = _mm256_add_ps(acc[0][lane], square);
247 }
248 }
249 for (int level = 1; level < num_levels; ++level) {
250 for (int lane = 0; lane < ilp_factor; ++lane) {
251 acc[0][lane] =
252 _mm256_add_ps(acc[0][lane], acc[level][lane]);
253 }
254 }
255
256 int vector_index = cascade_count * ilp_factor;
257 for (; vector_index < vector_count; ++vector_index) {
258 const __m256 value = _mm256_loadu_ps(
259 x + vector_index * vector_width);
260 const __m256 square = _mm256_mul_ps(value, value);
261 acc[0][0] = _mm256_add_ps(acc[0][0], square);
262 }
263 for (int lane = 1; lane < ilp_factor; ++lane) {
264 acc[0][0] = _mm256_add_ps(acc[0][0], acc[0][lane]);
265 }
266
267 _Alignas(32) float lanes[8];
268 _mm256_store_ps(lanes, acc[0][0]);
269 volatile float sum = 0.0f;
270 for (int lane = 0; lane < 8; ++lane) {
271 sum = sum + lanes[lane];
272 }
273 for (int d = vector_count * vector_width; d < dim; ++d) {
274 sum = sum + x[d] * x[d];
275 }
276 return sum;
277 }
278#endif
279 volatile float sum = 0.0f;
280 for (int d = 0; d < dim; ++d) {
281 sum = sum + x[d] * x[d];
282 }
283 return sum;
284}
static int recurrent_ceil_log2(int value)
int32_t int32_t int32_t int32_t int32_t mask
Definition tokenizer.h:234

References mask, and recurrent_ceil_log2().

Referenced by recurrent_pytorch_fp32_l2_rows().

◆ recurrent_qk_l2_norm_backward()

void recurrent_qk_l2_norm_backward ( const float *  d_q_out,
const float *  d_k_out,
const float *  q,
const float *  k,
float *  d_q,
float *  d_k,
int  rows,
int  q_dim,
int  k_dim,
int  head_dim,
float  eps 
)

Definition at line 472 of file recurrent_qk_norm_kernels.c.

482 {
483 recurrent_l2_norm_rows_backward_one(d_q_out, q, d_q, rows, q_dim, head_dim, eps);
484 recurrent_l2_norm_rows_backward_one(d_k_out, k, d_k, rows, k_dim, head_dim, eps);
485}
static void recurrent_l2_norm_rows_backward_one(const float *d_out, const float *x, float *d_x, int rows, int dim, int head_dim, float eps)

References recurrent_l2_norm_rows_backward_one().

◆ recurrent_qk_l2_norm_forward()

void recurrent_qk_l2_norm_forward ( float *  q,
float *  k,
int  rows,
int  q_dim,
int  k_dim,
int  head_dim,
float  eps 
)

Definition at line 124 of file recurrent_qk_norm_kernels.c.

130 {
131 if (!q || !k || rows <= 0 || q_dim <= 0 || k_dim <= 0 ||
132 head_dim <= 0 || q_dim % head_dim != 0 || k_dim % head_dim != 0) {
133 return;
134 }
135 const int q_heads = q_dim / head_dim;
136 const int k_heads = k_dim / head_dim;
137 if (q_heads > INT_MAX - k_heads) {
138 return;
139 }
140 const int heads_per_row = q_heads + k_heads;
141 if (rows > INT_MAX / heads_per_row) {
142 return;
143 }
144
145 ck_recurrent_qk_l2_args_t args = {
146 q, k, q_heads, k_heads, head_dim, eps,
147 };
148 const int jobs = rows * heads_per_row;
149 const size_t elements = (size_t)jobs * (size_t)head_dim;
150 const size_t minimum_elements_per_thread = 32768u;
151 const uintptr_t q_begin = (uintptr_t)q;
152 const uintptr_t q_end = q_begin +
153 (size_t)rows * (size_t)q_dim * sizeof(float);
154 const uintptr_t k_begin = (uintptr_t)k;
155 const uintptr_t k_end = k_begin +
156 (size_t)rows * (size_t)k_dim * sizeof(float);
157 const int ranges_overlap = q_begin < k_end && k_begin < q_end;
158 if (ranges_overlap || elements < 2u * minimum_elements_per_thread) {
159 recurrent_l2_norm_rows_forward_one(q, rows, q_dim, head_dim, eps);
160 recurrent_l2_norm_rows_forward_one(k, rows, k_dim, head_dim, eps);
161 return;
162 }
163
164 ck_threadpool_t *pool = ck_threadpool_global();
165 int active = pool ? ck_threadpool_n_threads(pool) : 1;
166 size_t useful_threads =
167 (elements + minimum_elements_per_thread - 1u) /
168 minimum_elements_per_thread;
169 if ((size_t)active > useful_threads) {
170 active = (int)useful_threads;
171 }
172 if (active > jobs) {
173 active = jobs;
174 }
175
176 if (active > 1 && ck_threadpool_thread_id(pool) <= 0) {
178 pool, active, 0, jobs, 8,
180 } else {
181 recurrent_l2_norm_rows_forward_one(q, rows, q_dim, head_dim, eps);
182 recurrent_l2_norm_rows_forward_one(k, rows, k_dim, head_dim, eps);
183 }
184}
void ck_threadpool_parallel_for_n(ck_threadpool_t *pool, int active_threads, int begin, int end, int grain_size, ck_range_fn_t fn, void *args)
ck_threadpool_t * ck_threadpool_global(void)
int ck_threadpool_thread_id(const ck_threadpool_t *pool)
int ck_threadpool_n_threads(const ck_threadpool_t *pool)
static void recurrent_qk_l2_norm_head_range(int begin, int end, void *opaque)
static void recurrent_l2_norm_rows_forward_one(float *x, int rows, int dim, int head_dim, float eps)

References ck_threadpool_global(), ck_threadpool_n_threads(), ck_threadpool_parallel_for_n(), ck_threadpool_thread_id(), recurrent_l2_norm_rows_forward_one(), and recurrent_qk_l2_norm_head_range().

Referenced by ck_test_recurrent_qk_l2_norm().

◆ recurrent_qk_l2_norm_head_range()

static void recurrent_qk_l2_norm_head_range ( int  begin,
int  end,
void *  opaque 
)
static

Definition at line 45 of file recurrent_qk_norm_kernels.c.

47 {
48 const ck_recurrent_qk_l2_args_t *args =
49 (const ck_recurrent_qk_l2_args_t *)opaque;
50 const int heads_per_row = args->q_heads + args->k_heads;
51 for (int job = begin; job < end; ++job) {
52 const int row = job / heads_per_row;
53 const int local_head = job - row * heads_per_row;
54 float *head_ptr;
55 if (local_head < args->q_heads) {
56 head_ptr = args->q +
57 ((size_t)row * (size_t)args->q_heads +
58 (size_t)local_head) * (size_t)args->head_dim;
59 } else {
60 head_ptr = args->k +
61 ((size_t)row * (size_t)args->k_heads +
62 (size_t)(local_head - args->q_heads)) *
63 (size_t)args->head_dim;
64 }
65
66 double sum_sq = 0.0;
67 for (int col = 0; col < args->head_dim; ++col) {
68 sum_sq += (double)(head_ptr[col] * head_ptr[col]);
69 }
70 const float norm = sqrtf((float)sum_sq);
71 const float inv_norm = 1.0f / fmaxf(norm, args->eps);
72 for (int col = 0; col < args->head_dim; ++col) {
73 head_ptr[col] *= inv_norm;
74 }
75 }
76}
uint32_t end
Definition utf8.c:215

References end.

Referenced by recurrent_qk_l2_norm_forward().

◆ recurrent_qk_l2_norm_pytorch_bf16_storage()

void recurrent_qk_l2_norm_pytorch_bf16_storage ( float *  q,
float *  k,
int  rows,
int  q_dim,
int  k_dim,
int  expanded_heads,
int  head_dim,
float  eps 
)

Definition at line 457 of file recurrent_qk_norm_kernels.c.

465{
467 q, rows, q_dim, expanded_heads, head_dim, eps);
469 k, rows, k_dim, expanded_heads, head_dim, eps);
470}
static void recurrent_pytorch_bf16_l2_rows(float *x, int rows, int dim, int expanded_heads, int head_dim, float eps)

References recurrent_pytorch_bf16_l2_rows().

◆ recurrent_qk_l2_norm_pytorch_fp32_output()

void recurrent_qk_l2_norm_pytorch_fp32_output ( float *  q,
float *  k,
int  rows,
int  q_dim,
int  k_dim,
int  head_dim,
float  eps 
)

Definition at line 335 of file recurrent_qk_norm_kernels.c.

342{
343 recurrent_pytorch_fp32_l2_rows(q, rows, q_dim, head_dim, eps);
344 recurrent_pytorch_fp32_l2_rows(k, rows, k_dim, head_dim, eps);
345}
static void recurrent_pytorch_fp32_l2_rows(float *x, int rows, int dim, int head_dim, float eps)

References recurrent_pytorch_fp32_l2_rows().