← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
gemm_kernels_bf16.c
Go to the documentation of this file.
1/**
2 * @file gemm_kernels_bf16.c
3 * @brief Optimized BF16 GEMM Kernels for AVX-512
4 *
5 * CK-ENGINE KERNEL RULES:
6 * =======================
7 * 1. NO malloc/free - memory via bump allocator, pointers passed in
8 * 2. NO OpenMP - parallelization at orchestrator/codegen layer
9 * 3. API must define: inputs, outputs, workspace, and memory layouts
10 * 4. Pure computation - deterministic, no side effects
11 *
12 * After changes: make test && make llamacpp-parity-full
13 *
14 * Layout:
15 * A: [M x K] row-major (BF16)
16 * B: [N x K] row-major, stored as [out x in] (BF16)
17 * C: [M x N] row-major (BF16 or FP32)
18 *
19 * Key optimizations:
20 * 1. AVX-512 BF16 instructions (VDPBF16PS) when available
21 * 2. Cache blocking for L1/L2 efficiency
22 * 3. Vectorized BF16<->FP32 conversion
23 * 4. CKE thread-pool row parallelization
24 */
25
26#include <stdint.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31static void ck_pytorch_brgemm_fault(const char *message, int M, int N, int K)
32{
33 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: PyTorch oneDNN BF16 BRGEMM %s "
34 "(M=%d N=%d K=%d)\n", message, M, N, K);
35 abort();
36}
37
38#ifdef USE_ONEDNN
39#include <dnnl.h>
40#include <pthread.h>
41#endif
42
43#if defined(__AVX512F__)
44#include <immintrin.h>
45#endif
46
47#if defined(__linux__) && defined(__AMX_TILE__)
48#include <sys/syscall.h>
49#include <unistd.h>
50#endif
51
52#ifdef _OPENMP
53#include <omp.h>
54#endif
55
56#include "bf16_utils.h"
57#include "ckernel_engine.h"
58#include "ck_threadpool.h"
59
60/* Block sizes tuned for typical L1/L2 cache */
61#define BLK_M 64
62#define BLK_N 64
63#define BLK_K 256
64
65static inline int ck_min_i(int a, int b) { return a < b ? a : b; }
66
67/* ==========================================================================
68 * Reference Implementation (scalar, for correctness testing)
69 * Kept for debugging/validation but not called in normal operation.
70 * ========================================================================== */
72static void gemm_bf16_scalar(const uint16_t *A,
73 const uint16_t *B,
74 const uint16_t *bias,
75 uint16_t *C,
76 int M, int N, int K)
77{
78 for (int i = 0; i < M; ++i) {
79 for (int j = 0; j < N; ++j) {
80 float sum = bias ? bf16_to_float(bias[j]) : 0.0f;
81 const size_t a_row = (size_t)i * (size_t)K;
82 const size_t b_row = (size_t)j * (size_t)K;
83 for (int k = 0; k < K; ++k) {
84 sum += bf16_to_float(A[a_row + k]) * bf16_to_float(B[b_row + k]);
85 }
86 C[(size_t)i * (size_t)N + j] = float_to_bf16(sum);
87 }
88 }
89}
90#if defined(__AVX512F__)
91
92/* ==========================================================================
93 * AVX-512F: Vectorized BF16 conversion + FMA
94 * Works on all AVX-512 CPUs (no BF16 instruction required)
95 *
96 * BF16 conversion functions (bf16x16_to_fp32, fp32x16_to_bf16) are now
97 * provided by bf16_utils.h for consistency across all kernels.
98 * ========================================================================== */
99
100/* BF16 dot product: 16 pairs, accumulate to FP32 */
101static inline __m512 bf16_dot16(__m256i a_bf16, __m256i b_bf16, __m512 acc)
102{
103 __m512 a_fp32 = bf16x16_to_fp32(a_bf16);
104 __m512 b_fp32 = bf16x16_to_fp32(b_bf16);
105 return _mm512_fmadd_ps(a_fp32, b_fp32, acc);
106}
107
108/* ==========================================================================
109 * AVX-512 Vectorized GEMM (using AVX-512F, works everywhere)
110 * C[M,N] = A[M,K] @ B[N,K].T
111 * ========================================================================== */
112static void gemm_bf16_avx512(const uint16_t *A,
113 const uint16_t *B,
114 const uint16_t *bias,
115 uint16_t *C,
116 int M, int N, int K)
117{
118 #pragma omp parallel for schedule(dynamic)
119 for (int i = 0; i < M; ++i) {
120 const uint16_t *a_row = A + (size_t)i * K;
121
122 for (int j = 0; j < N; ++j) {
123 const uint16_t *b_row = B + (size_t)j * K;
124
125 /* Initialize accumulator */
126 __m512 sum_vec = _mm512_setzero_ps();
127
128 /* Vectorized inner loop: process 16 elements at a time */
129 int k = 0;
130 for (; k <= K - 16; k += 16) {
131 __m256i a_bf16 = _mm256_loadu_si256((const __m256i *)(a_row + k));
132 __m256i b_bf16 = _mm256_loadu_si256((const __m256i *)(b_row + k));
133 sum_vec = bf16_dot16(a_bf16, b_bf16, sum_vec);
134 }
135
136 /* Horizontal sum */
137 float sum = _mm512_reduce_add_ps(sum_vec);
138
139 /* Scalar tail */
140 for (; k < K; ++k) {
141 sum += bf16_to_float(a_row[k]) * bf16_to_float(b_row[k]);
142 }
143
144 /* Add bias */
145 if (bias) {
146 sum += bf16_to_float(bias[j]);
147 }
148
149 C[(size_t)i * N + j] = float_to_bf16(sum);
150 }
151 }
152}
153
154/* ==========================================================================
155 * Cache-Blocked AVX-512 GEMM
156 * Better memory access pattern for large matrices
157 * ========================================================================== */
158static void gemm_bf16_blocked_avx512(const uint16_t *A,
159 const uint16_t *B,
160 const uint16_t *bias,
161 uint16_t *C,
162 int M, int N, int K)
163{
164 /* Initialize C with bias */
165 #pragma omp parallel for
166 for (int i = 0; i < M; ++i) {
167 for (int j = 0; j < N; ++j) {
168 float b = bias ? bf16_to_float(bias[j]) : 0.0f;
169 C[(size_t)i * N + j] = float_to_bf16(b);
170 }
171 }
172
173 /* Blocked GEMM */
174 #pragma omp parallel for collapse(2) schedule(dynamic)
175 for (int ii = 0; ii < M; ii += BLK_M) {
176 for (int jj = 0; jj < N; jj += BLK_N) {
177 int i_end = ck_min_i(ii + BLK_M, M);
178 int j_end = ck_min_i(jj + BLK_N, N);
179
180 /* Local FP32 accumulator for this block */
181 float acc[BLK_M][BLK_N];
182 for (int i = 0; i < BLK_M; ++i) {
183 for (int j = 0; j < BLK_N; ++j) {
184 acc[i][j] = 0.0f;
185 }
186 }
187
188 /* K-dimension blocking */
189 for (int kk = 0; kk < K; kk += BLK_K) {
190 int k_end = ck_min_i(kk + BLK_K, K);
191
192 for (int i = ii; i < i_end; ++i) {
193 const uint16_t *a_row = A + (size_t)i * K;
194 int local_i = i - ii;
195
196 for (int j = jj; j < j_end; ++j) {
197 const uint16_t *b_row = B + (size_t)j * K;
198 int local_j = j - jj;
199
200 __m512 sum_vec = _mm512_setzero_ps();
201
202 int k = kk;
203 for (; k <= k_end - 16; k += 16) {
204 __m256i a_bf16 = _mm256_loadu_si256((const __m256i *)(a_row + k));
205 __m256i b_bf16 = _mm256_loadu_si256((const __m256i *)(b_row + k));
206 sum_vec = bf16_dot16(a_bf16, b_bf16, sum_vec);
207 }
208
209 float partial = _mm512_reduce_add_ps(sum_vec);
210 for (; k < k_end; ++k) {
211 partial += bf16_to_float(a_row[k]) * bf16_to_float(b_row[k]);
212 }
213
214 acc[local_i][local_j] += partial;
215 }
216 }
217 }
218
219 /* Write accumulated results back */
220 for (int i = ii; i < i_end; ++i) {
221 for (int j = jj; j < j_end; ++j) {
222 float old_val = bf16_to_float(C[(size_t)i * N + j]);
223 float new_val = old_val + acc[i - ii][j - jj];
224 C[(size_t)i * N + j] = float_to_bf16(new_val);
225 }
226 }
227 }
228 }
229}
230
231/*
232 * Native AVX-512 BF16 support (VDPBF16PS instruction)
233 * Only compiles on Ice Lake / Sapphire Rapids or newer
234 * Compile with: -mavx512bf16 (gcc/clang) or /arch:AVX512 (MSVC with recent SDK)
235 */
236#if defined(__AVX512BF16__) && defined(__AVX512VL__)
237
238/* Load 32 BF16 values into __m512bh */
239static inline __m512bh load_bf16x32(const uint16_t *ptr)
240{
241 return (__m512bh)_mm512_loadu_si512((const __m512i *)ptr);
242}
243
244#if defined(__AMX_TILE__) && defined(__AMX_BF16__)
245
246#ifndef ARCH_REQ_XCOMP_PERM
247#define ARCH_REQ_XCOMP_PERM 0x1023
248#endif
249#ifndef XFEATURE_XTILE_DATA
250#define XFEATURE_XTILE_DATA 18
251#endif
252
253typedef struct ck_amx_tile_config {
254 uint8_t palette_id;
255 uint8_t start_row;
256 uint8_t reserved_0[14];
257 uint16_t colsb[16];
258 uint8_t rows[16];
259} ck_amx_tile_config;
260
261_Static_assert(sizeof(ck_amx_tile_config) == 64,
262 "AMX tile configuration must occupy exactly 64 bytes");
263
264static int ck_amx_request_xtile_data(void)
265{
266#if defined(__linux__)
267 static int state = 0;
268 if (state == 1) {
269 return 1;
270 }
271 if (state == -1) {
272 return 0;
273 }
274 long rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILE_DATA);
275 state = (rc == 0) ? 1 : -1;
276 return state == 1;
277#else
278 return 0;
279#endif
280}
281
282static void ck_amx_config_bf16_16x16x32(void)
283{
284 ck_amx_tile_config cfg;
285 memset(&cfg, 0, sizeof(cfg));
286 cfg.palette_id = 1;
287
288 cfg.rows[0] = 16; /* A: 16 rows x 32 BF16 */
289 cfg.colsb[0] = 64;
290 cfg.rows[1] = 16; /* B: 16 K-pair rows x 16 BF16-pair columns */
291 cfg.colsb[1] = 64;
292 cfg.rows[2] = 16; /* C: 16 rows x 16 FP32 */
293 cfg.colsb[2] = 64;
294 for (int tile = 3; tile <= 5; ++tile) {
295 cfg.rows[tile] = 16;
296 cfg.colsb[tile] = 64;
297 }
298
299 _tile_loadconfig(&cfg);
300}
301
302static void ck_amx_config_bf16_16x16_kblock(int k_block)
303{
304 ck_amx_tile_config cfg;
305 memset(&cfg, 0, sizeof(cfg));
306 cfg.palette_id = 1;
307 cfg.rows[0] = 16;
308 cfg.colsb[0] = (uint16_t)(k_block * (int)sizeof(uint16_t));
309 cfg.rows[1] = (uint8_t)(k_block / 2);
310 cfg.colsb[1] = 64;
311 cfg.rows[2] = 16;
312 cfg.colsb[2] = 64;
313 /* Keep the palette structurally identical to the proven 16x16x32
314 * configuration. */
315 for (int tile = 3; tile <= 5; ++tile) {
316 cfg.rows[tile] = 16;
317 cfg.colsb[tile] = 64;
318 }
319 /* GCC 11 models ldtilecfg as reading less than its architectural 64-byte
320 * operand and can otherwise delete dynamic row/column descriptor stores. */
321 __asm__ volatile("" : : "m"(cfg) : "memory");
322 _tile_loadconfig(&cfg);
323}
324
325static void ck_pack_bf16_ktile_pairs_16x16(uint16_t *dst,
326 const uint16_t *B,
327 int K,
328 int j,
329 int k)
330{
331 for (int kp = 0; kp < 16; ++kp) {
332 const int k0 = k + kp * 2;
333 for (int nn = 0; nn < 16; ++nn) {
334 dst[(size_t)kp * 32u + (size_t)nn * 2u + 0u] =
335 B[(size_t)(j + nn) * (size_t)K + (size_t)k0];
336 dst[(size_t)kp * 32u + (size_t)nn * 2u + 1u] =
337 B[(size_t)(j + nn) * (size_t)K + (size_t)(k0 + 1)];
338 }
339 }
340}
341
342static void gemm_bf16_fp32out_amx(const uint16_t *A,
343 const uint16_t *B,
344 const float *bias,
345 float *C,
346 int M, int N, int K)
347{
348 ck_amx_config_bf16_16x16x32();
349
350 uint16_t b_tile[16 * 32];
351
352 for (int i = 0; i < M; i += 16) {
353 for (int j = 0; j < N; j += 16) {
354 _tile_zero(2);
355
356 for (int k = 0; k < K; k += 32) {
357 ck_pack_bf16_ktile_pairs_16x16(b_tile, B, K, j, k);
358 _tile_loadd(0, A + (size_t)i * (size_t)K + (size_t)k, K * (int)sizeof(uint16_t));
359 _tile_loadd(1, b_tile, 32 * (int)sizeof(uint16_t));
360 _tile_dpbf16ps(2, 0, 1);
361 }
362
363 _tile_stored(2, C + (size_t)i * (size_t)N + (size_t)j, N * (int)sizeof(float));
364
365 if (bias) {
366 for (int ii = 0; ii < 16; ++ii) {
367 float *c_row = C + (size_t)(i + ii) * (size_t)N + (size_t)j;
368 for (int jj = 0; jj < 16; ++jj) {
369 c_row[jj] += bias[j + jj];
370 }
371 }
372 }
373 }
374 }
375
376 _tile_release();
377}
378
379#define HAVE_AMX_BF16 1
380#else
381#define HAVE_AMX_BF16 0
382#endif /* __AMX_TILE__ && __AMX_BF16__ */
383
384static void gemm_bf16_native(const uint16_t *A,
385 const uint16_t *B,
386 const uint16_t *bias,
387 uint16_t *C,
388 int M, int N, int K)
389{
390 #pragma omp parallel for schedule(dynamic)
391 for (int i = 0; i < M; ++i) {
392 for (int j = 0; j < N; ++j) {
393 /* Initialize accumulator */
394 __m512 sum_vec = _mm512_setzero_ps();
395
396 /* Native BF16 dot product: 32 pairs per instruction! */
397 int k = 0;
398 for (; k <= K - 32; k += 32) {
399 __m512bh a_vec = load_bf16x32(A + (size_t)i * K + k);
400 __m512bh b_vec = load_bf16x32(B + (size_t)j * K + k);
401 sum_vec = _mm512_dpbf16_ps(sum_vec, a_vec, b_vec);
402 }
403
404 float sum = _mm512_reduce_add_ps(sum_vec);
405
406 /* Scalar tail */
407 for (; k < K; ++k) {
408 sum += bf16_to_float(A[(size_t)i * K + k]) *
409 bf16_to_float(B[(size_t)j * K + k]);
410 }
411
412 if (bias) {
413 sum += bf16_to_float(bias[j]);
414 }
415
416 C[(size_t)i * N + j] = float_to_bf16(sum);
417 }
418 }
419}
420
421#define HAVE_NATIVE_BF16 1
422#else
423#define HAVE_NATIVE_BF16 0
424#endif /* __AVX512BF16__ && __AVX512VL__ */
425
426#endif /* __AVX512F__ */
427
428/* ==========================================================================
429 * Public API: Auto-dispatch to best available implementation
430 * ========================================================================== */
431void gemm_blocked_serial_bf16(const uint16_t *A,
432 const uint16_t *B,
433 const uint16_t *bias,
434 uint16_t *C,
435 int M, int N, int K)
436{
437 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0) {
438 return;
439 }
440
441#if HAVE_NATIVE_BF16
442 /* Native BF16 instructions available (Ice Lake / Sapphire Rapids+) */
443 gemm_bf16_native(A, B, bias, C, M, N, K);
444#elif defined(__AVX512F__)
445 /* Use AVX-512F with software BF16 conversion */
446 if (M * N > 4096) {
447 gemm_bf16_blocked_avx512(A, B, bias, C, M, N, K);
448 } else {
449 gemm_bf16_avx512(A, B, bias, C, M, N, K);
450 }
451#else
452 /* Scalar fallback */
453 gemm_bf16_scalar(A, B, bias, C, M, N, K);
454#endif
455}
456
457/* ==========================================================================
458 * GEMM with FP32 output (useful for intermediate computations)
459 * ========================================================================== */
460void gemm_bf16_fp32out(const uint16_t *A,
461 const uint16_t *B,
462 const float *bias,
463 float *C,
464 int M, int N, int K)
465{
466 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0) {
467 return;
468 }
469
470#if HAVE_NATIVE_BF16
471#if HAVE_AMX_BF16
472 const char *amx_env = getenv("CK_BF16_AMX");
473 if (amx_env && amx_env[0] == '1' &&
474 (M % 16) == 0 && (N % 16) == 0 && (K % 32) == 0 &&
475 M >= 16 && N >= 16 && K >= 32 && ck_amx_request_xtile_data()) {
476 gemm_bf16_fp32out_amx(A, B, bias, C, M, N, K);
477 return;
478 }
479#endif
480
481 #pragma omp parallel for schedule(dynamic)
482 for (int i = 0; i < M; ++i) {
483 const uint16_t *a_row = A + (size_t)i * K;
484 int j = 0;
485
486 for (; j + 4 <= N; j += 4) {
487 const uint16_t *b0 = B + (size_t)(j + 0) * K;
488 const uint16_t *b1 = B + (size_t)(j + 1) * K;
489 const uint16_t *b2 = B + (size_t)(j + 2) * K;
490 const uint16_t *b3 = B + (size_t)(j + 3) * K;
491 __m512 acc0 = _mm512_setzero_ps();
492 __m512 acc1 = _mm512_setzero_ps();
493 __m512 acc2 = _mm512_setzero_ps();
494 __m512 acc3 = _mm512_setzero_ps();
495
496 int k = 0;
497 for (; k <= K - 32; k += 32) {
498 const __m512bh a_vec = load_bf16x32(a_row + k);
499 acc0 = _mm512_dpbf16_ps(acc0, a_vec, load_bf16x32(b0 + k));
500 acc1 = _mm512_dpbf16_ps(acc1, a_vec, load_bf16x32(b1 + k));
501 acc2 = _mm512_dpbf16_ps(acc2, a_vec, load_bf16x32(b2 + k));
502 acc3 = _mm512_dpbf16_ps(acc3, a_vec, load_bf16x32(b3 + k));
503 }
504
505 float s0 = _mm512_reduce_add_ps(acc0);
506 float s1 = _mm512_reduce_add_ps(acc1);
507 float s2 = _mm512_reduce_add_ps(acc2);
508 float s3 = _mm512_reduce_add_ps(acc3);
509 for (; k < K; ++k) {
510 const float a = bf16_to_float(a_row[k]);
511 s0 += a * bf16_to_float(b0[k]);
512 s1 += a * bf16_to_float(b1[k]);
513 s2 += a * bf16_to_float(b2[k]);
514 s3 += a * bf16_to_float(b3[k]);
515 }
516 if (bias) {
517 s0 += bias[j + 0];
518 s1 += bias[j + 1];
519 s2 += bias[j + 2];
520 s3 += bias[j + 3];
521 }
522 C[(size_t)i * N + (j + 0)] = s0;
523 C[(size_t)i * N + (j + 1)] = s1;
524 C[(size_t)i * N + (j + 2)] = s2;
525 C[(size_t)i * N + (j + 3)] = s3;
526 }
527
528 for (; j < N; ++j) {
529 const uint16_t *b_row = B + (size_t)j * K;
530 __m512 sum_vec = _mm512_setzero_ps();
531
532 int k = 0;
533 for (; k <= K - 32; k += 32) {
534 const __m512bh a_vec = load_bf16x32(a_row + k);
535 const __m512bh b_vec = load_bf16x32(b_row + k);
536 sum_vec = _mm512_dpbf16_ps(sum_vec, a_vec, b_vec);
537 }
538
539 float sum = _mm512_reduce_add_ps(sum_vec);
540 for (; k < K; ++k) {
541 sum += bf16_to_float(a_row[k]) * bf16_to_float(b_row[k]);
542 }
543 if (bias) {
544 sum += bias[j];
545 }
546 C[(size_t)i * N + j] = sum;
547 }
548 }
549#elif defined(__AVX512F__)
550 #pragma omp parallel for schedule(dynamic)
551 for (int i = 0; i < M; ++i) {
552 const uint16_t *a_row = A + (size_t)i * K;
553
554 for (int j = 0; j < N; ++j) {
555 const uint16_t *b_row = B + (size_t)j * K;
556
557 __m512 sum_vec = _mm512_setzero_ps();
558
559 int k = 0;
560 for (; k <= K - 16; k += 16) {
561 __m256i a_bf16 = _mm256_loadu_si256((const __m256i *)(a_row + k));
562 __m256i b_bf16 = _mm256_loadu_si256((const __m256i *)(b_row + k));
563 sum_vec = bf16_dot16(a_bf16, b_bf16, sum_vec);
564 }
565
566 float sum = _mm512_reduce_add_ps(sum_vec);
567
568 for (; k < K; ++k) {
569 sum += bf16_to_float(a_row[k]) * bf16_to_float(b_row[k]);
570 }
571
572 if (bias) {
573 sum += bias[j];
574 }
575
576 C[(size_t)i * N + j] = sum;
577 }
578 }
579#else
580 for (int i = 0; i < M; ++i) {
581 for (int j = 0; j < N; ++j) {
582 float sum = bias ? bias[j] : 0.0f;
583 for (int k = 0; k < K; ++k) {
584 sum += bf16_to_float(A[(size_t)i * K + k]) *
585 bf16_to_float(B[(size_t)j * K + k]);
586 }
587 C[(size_t)i * N + j] = sum;
588 }
589 }
590#endif
591}
592
593
594/* ============================================================================
595 * Inference kernels for exact safetensors/BUMP BF16 weights.
596 *
597 * The v8 inference graph currently keeps activation streams as FP32. These
598 * wrappers preserve the established quantized/FP16 inference ABI while consuming
599 * BF16 row-major weights from safetensors BUMP artifacts:
600 * GEMV: y[M] = W[M,K] @ bf16_round(x[K])
601 * GEMM: C[M,N] = bf16_round(A[M,K]) @ W[N,K].T + bias[N]
602 *
603 * Rounding the FP32 activation to BF16 before multiply gives a closer contract
604 * to a BF16 PyTorch model than multiplying full FP32 activations by BF16
605 * weights, while still avoiding a separate activation-conversion buffer.
606 * ========================================================================== */
607static void gemv_bf16_row_range(float *y,
608 const uint16_t *w,
609 const float *x,
610 int M, int K,
611 int row_begin, int row_end)
612{
613 if (!y || !w || !x || M <= 0 || K <= 0 ||
614 row_begin < 0 || row_begin >= row_end || row_end > M) {
615 return;
616 }
617
618 for (int i = row_begin; i < row_end; ++i) {
619 const uint16_t *w_row = w + (size_t)i * (size_t)K;
620 float sum = 0.0f;
621 for (int k = 0; k < K; ++k) {
622 const float xb = bf16_to_float(float_to_bf16(x[k]));
623 sum += xb * bf16_to_float(w_row[k]);
624 }
625 y[i] = sum;
626 }
627}
628
629void gemv_bf16(float *y,
630 const void *W,
631 const float *x,
632 int M, int K)
633{
635 y, (const uint16_t *)W, x, M, K, 0, M);
636}
637
638typedef struct {
639 float *y;
640 const uint16_t *w;
641 const float *x;
642 int M;
643 int K;
644} ck_gemv_bf16_args_t;
645
646static void ck_gemv_bf16_rows(int begin, int end, void *opaque)
647{
648 const ck_gemv_bf16_args_t *args = (const ck_gemv_bf16_args_t *)opaque;
650 args->y, args->w, args->x, args->M, args->K, begin, end);
651}
652
654 const void *W,
655 const float *x,
656 int M, int K)
657{
658 ck_threadpool_t *pool = ck_threadpool_global();
659 if (!pool || ck_threadpool_n_threads(pool) <= 1 || M < 2 ||
660 (size_t)M * (size_t)K <= 65536) {
661 gemv_bf16(y, W, x, M, K);
662 return;
663 }
664
665 ck_gemv_bf16_args_t args = {
666 .y = y, .w = (const uint16_t *)W, .x = x, .M = M, .K = K,
667 };
668 int active = ck_threadpool_n_threads(pool);
669 if (active > M) active = M;
670 int grain = M / (active * 4);
671 if (grain < 1) grain = 1;
673 pool, active, 0, M, grain, ck_gemv_bf16_rows, &args);
674}
675
677 const uint16_t *w,
678 const float *x,
679 int M, int K,
680 int row_begin, int row_end)
681{
682 gemv_bf16_row_range(y, w, x, M, K, row_begin, row_end);
683 for (int row = row_begin; row < row_end; ++row) {
684 y[row] = bf16_to_float(float_to_bf16(y[row]));
685 }
686}
687
688static void ck_gemv_bf16_storage_rows(int begin, int end, void *opaque)
689{
690 const ck_gemv_bf16_args_t *args = (const ck_gemv_bf16_args_t *)opaque;
692 args->y, args->w, args->x, args->M, args->K, begin, end);
693}
694
696 const void *W,
697 const float *x,
698 int M, int K)
699{
700 ck_threadpool_t *pool = ck_threadpool_global();
701 if (!pool || ck_threadpool_n_threads(pool) <= 1 || M < 2 ||
702 (size_t)M * (size_t)K <= 65536) {
704 y, (const uint16_t *)W, x, M, K, 0, M);
705 return;
706 }
707
708 ck_gemv_bf16_args_t args = {
709 .y = y, .w = (const uint16_t *)W, .x = x, .M = M, .K = K,
710 };
711 int active = ck_threadpool_n_threads(pool);
712 if (active > M) active = M;
713 int grain = M / (active * 4);
714 if (grain < 1) grain = 1;
716 pool, active, 0, M, grain, ck_gemv_bf16_storage_rows, &args);
717}
718
720 const void *W,
721 const float *x,
722 int M, int K)
723{
725}
726
727void gemm_nt_bf16_row_range(const float *A,
728 const void *B,
729 const float *bias,
730 float *C,
731 int M, int N, int K,
732 int row_begin, int row_end)
733{
734 const uint16_t *w = (const uint16_t *)B;
735 if (!A || !w || !C || M <= 0 || N <= 0 || K <= 0 ||
736 row_begin < 0 || row_begin >= row_end || row_end > M) {
737 return;
738 }
739
740 for (int i = row_begin; i < row_end; ++i) {
741 const float *a_row = A + (size_t)i * (size_t)K;
742 float *c_row = C + (size_t)i * (size_t)N;
743 for (int j = 0; j < N; ++j) {
744 const uint16_t *w_row = w + (size_t)j * (size_t)K;
745 float sum = bias ? bias[j] : 0.0f;
746 for (int k = 0; k < K; ++k) {
747 const float ab = bf16_to_float(float_to_bf16(a_row[k]));
748 sum += ab * bf16_to_float(w_row[k]);
749 }
750 c_row[j] = sum;
751 }
752 }
753}
754
755void gemm_nt_bf16(const float *A,
756 const void *B,
757 const float *bias,
758 float *C,
759 int M, int N, int K)
760{
761 if (M <= 0) return;
762 gemm_nt_bf16_row_range(A, B, bias, C, M, N, K, 0, M);
763}
764
765typedef struct {
766 const float *A;
767 const void *B;
768 const float *bias;
769 float *C;
770 int M;
771 int N;
772 int K;
773} ck_gemm_nt_bf16_exact_args_t;
774
775static void ck_gemm_nt_bf16_exact_rows(int begin, int end, void *opaque)
776{
777 const ck_gemm_nt_bf16_exact_args_t *args =
778 (const ck_gemm_nt_bf16_exact_args_t *)opaque;
780 args->A, args->B, args->bias, args->C,
781 args->M, args->N, args->K, begin, end);
782}
783
785 const void *B,
786 const float *bias,
787 float *C,
788 int M, int N, int K)
789{
790 ck_threadpool_t *pool = ck_threadpool_global();
791 const char *disabled = getenv("CK_DISABLE_BF16_GEMM_PARALLEL_PREFILL");
792 if ((disabled && disabled[0] && strcmp(disabled, "0") != 0) ||
793 !pool || ck_threadpool_n_threads(pool) <= 1 || M < 2 ||
794 (size_t)M * (size_t)N <= 4096) {
795 gemm_nt_bf16(A, B, bias, C, M, N, K);
796 return;
797 }
798
799 ck_gemm_nt_bf16_exact_args_t args = {
800 .A = A, .B = B, .bias = bias, .C = C, .M = M, .N = N, .K = K,
801 };
802 int active = ck_threadpool_n_threads(pool);
803 if (active > M) active = M;
804 int grain = M / (active * 4);
805 if (grain < 1) grain = 1;
807 pool, active, 0, M, grain, ck_gemm_nt_bf16_exact_rows, &args);
808}
809
810/* ==========================================================================
811 * Backward kernels for training
812 * ========================================================================== */
813
814/* gemm_nn_bf16: C = A @ B (no transpose), for dL/dX computation */
815void gemm_nn_bf16(const uint16_t *A,
816 const uint16_t *B,
817 const uint16_t *bias,
818 uint16_t *C,
819 int M, int N, int K)
820{
821 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0) {
822 return;
823 }
824
825#if defined(__AVX512F__)
826 #pragma omp parallel for
827 for (int i = 0; i < M; ++i) {
828 /* Initialize row with bias */
829 int j = 0;
830 for (; j <= N - 16; j += 16) {
831 __m512 b_vec = bias ? bf16x16_to_fp32(_mm256_loadu_si256((const __m256i *)(bias + j)))
832 : _mm512_setzero_ps();
833 __m256i out = fp32x16_to_bf16(b_vec);
834 _mm256_storeu_si256((__m256i *)(C + (size_t)i * N + j), out);
835 }
836 for (; j < N; ++j) {
837 float b = bias ? bf16_to_float(bias[j]) : 0.0f;
838 C[(size_t)i * N + j] = float_to_bf16(b);
839 }
840
841 /* Accumulate: C[i,:] += A[i,k] * B[k,:] */
842 for (int k = 0; k < K; ++k) {
843 float a_val = bf16_to_float(A[(size_t)i * K + k]);
844 __m512 a_broadcast = _mm512_set1_ps(a_val);
845
846 j = 0;
847 for (; j <= N - 16; j += 16) {
848 __m256i b_bf16 = _mm256_loadu_si256((const __m256i *)(B + (size_t)k * N + j));
849 __m512 b_fp32 = bf16x16_to_fp32(b_bf16);
850
851 __m256i c_bf16 = _mm256_loadu_si256((const __m256i *)(C + (size_t)i * N + j));
852 __m512 c_fp32 = bf16x16_to_fp32(c_bf16);
853
854 c_fp32 = _mm512_fmadd_ps(a_broadcast, b_fp32, c_fp32);
855
856 __m256i c_out = fp32x16_to_bf16(c_fp32);
857 _mm256_storeu_si256((__m256i *)(C + (size_t)i * N + j), c_out);
858 }
859 for (; j < N; ++j) {
860 float c_val = bf16_to_float(C[(size_t)i * N + j]);
861 c_val += a_val * bf16_to_float(B[(size_t)k * N + j]);
862 C[(size_t)i * N + j] = float_to_bf16(c_val);
863 }
864 }
865 }
866#else
867 /* Scalar fallback */
868 for (int i = 0; i < M; ++i) {
869 for (int j = 0; j < N; ++j) {
870 float sum = bias ? bf16_to_float(bias[j]) : 0.0f;
871 for (int k = 0; k < K; ++k) {
872 sum += bf16_to_float(A[(size_t)i * K + k]) *
873 bf16_to_float(B[(size_t)k * N + j]);
874 }
875 C[(size_t)i * N + j] = float_to_bf16(sum);
876 }
877 }
878#endif
879}
880
881/* gemm_tn_bf16: C = A.T @ B, for dL/dW computation */
882void gemm_tn_bf16(const uint16_t *A,
883 const uint16_t *B,
884 const uint16_t *bias,
885 uint16_t *C,
886 int M, int N, int K)
887{
888 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0) {
889 return;
890 }
891
892 /* A is [K x M], we want A.T which is [M x K] */
893 /* B is [K x N] */
894 /* C is [M x N] */
895
896#if defined(__AVX512F__)
897 /* Initialize C with bias */
898 #pragma omp parallel for
899 for (int i = 0; i < M; ++i) {
900 for (int j = 0; j < N; ++j) {
901 float b = bias ? bf16_to_float(bias[j]) : 0.0f;
902 C[(size_t)i * N + j] = float_to_bf16(b);
903 }
904 }
905
906 /* Accumulate: C[i,j] += sum_k A[k,i] * B[k,j] */
907 #pragma omp parallel for
908 for (int i = 0; i < M; ++i) {
909 for (int j = 0; j < N; ++j) {
910 __m512 sum_vec = _mm512_setzero_ps();
911
912 int k = 0;
913 for (; k <= K - 16; k += 16) {
914 /* Gather A[k:k+16, i] - strided access */
915 __m512 a_fp32 = _mm512_setzero_ps();
916 for (int kk = 0; kk < 16; ++kk) {
917 float val = bf16_to_float(A[(size_t)(k + kk) * M + i]);
918 a_fp32 = _mm512_mask_mov_ps(a_fp32, 1 << kk, _mm512_set1_ps(val));
919 }
920
921 /* Note: B has stride N, so we need to gather element by element */
922 __m512 b_fp32 = _mm512_setzero_ps();
923 for (int kk = 0; kk < 16; ++kk) {
924 float val = bf16_to_float(B[(size_t)(k + kk) * N + j]);
925 b_fp32 = _mm512_mask_mov_ps(b_fp32, 1 << kk, _mm512_set1_ps(val));
926 }
927
928 sum_vec = _mm512_fmadd_ps(a_fp32, b_fp32, sum_vec);
929 }
930
931 float sum = _mm512_reduce_add_ps(sum_vec);
932
933 for (; k < K; ++k) {
934 sum += bf16_to_float(A[(size_t)k * M + i]) *
935 bf16_to_float(B[(size_t)k * N + j]);
936 }
937
938 float old_val = bf16_to_float(C[(size_t)i * N + j]);
939 C[(size_t)i * N + j] = float_to_bf16(old_val + sum);
940 }
941 }
942#else
943 for (int i = 0; i < M; ++i) {
944 for (int j = 0; j < N; ++j) {
945 float sum = bias ? bf16_to_float(bias[j]) : 0.0f;
946 for (int k = 0; k < K; ++k) {
947 sum += bf16_to_float(A[(size_t)k * M + i]) *
948 bf16_to_float(B[(size_t)k * N + j]);
949 }
950 C[(size_t)i * N + j] = float_to_bf16(sum);
951 }
952 }
953#endif
954}
955
956/*
957 * Mixed-precision BF16 linear backward for training.
958 *
959 * Forward contract:
960 * Y[t, o] = dot(input[t, :], weight[o, :]) + bias[o]
961 *
962 * Inputs are BF16 storage, math and gradients are FP32. This mirrors the
963 * standard mixed-precision training contract where activations/weights may be
964 * BF16 but gradient accumulation remains FP32.
965 */
966typedef struct {
967 const float *A;
968 const uint16_t *B;
969 const float *bias;
970 float *C;
971 int M;
972 int N;
973 int K;
974} ck_gemm_bf16_native_args_t;
975
976static void ck_gemm_bf16_native_work(int ith, int nth, void *opaque)
977{
978 ck_gemm_bf16_native_args_t *args = (ck_gemm_bf16_native_args_t *)opaque;
979 const int N = args->N;
980 const int K = args->K;
981 enum { ROW_TILE = 4 };
982 uint16_t *a_bf16 = (uint16_t *)alloca(
983 (size_t)ROW_TILE * (size_t)K * sizeof(uint16_t));
984
985 for (int row0 = ith * ROW_TILE; row0 < args->M; row0 += nth * ROW_TILE) {
986 const int rows = args->M - row0 < ROW_TILE ? args->M - row0 : ROW_TILE;
987 for (int r = 0; r < rows; ++r) {
988 const float *src = args->A + (size_t)(row0 + r) * (size_t)K;
989 uint16_t *ar = a_bf16 + (size_t)r * (size_t)K;
990 for (int k = 0; k < K; ++k) ar[k] = float_to_bf16(src[k]);
991 }
992
993#if HAVE_NATIVE_BF16
994 int j = 0;
995 for (; j + 4 <= N; j += 4) {
996 const uint16_t *b0 = args->B + (size_t)(j + 0) * K;
997 const uint16_t *b1 = args->B + (size_t)(j + 1) * K;
998 const uint16_t *b2 = args->B + (size_t)(j + 2) * K;
999 const uint16_t *b3 = args->B + (size_t)(j + 3) * K;
1000 __m512 acc[ROW_TILE][4];
1001 for (int r = 0; r < rows; ++r) {
1002 for (int lane = 0; lane < 4; ++lane) acc[r][lane] = _mm512_setzero_ps();
1003 }
1004 int k = 0;
1005 for (; k <= K - 32; k += 32) {
1006 const __m512bh bv[4] = {
1007 load_bf16x32(b0 + k), load_bf16x32(b1 + k),
1008 load_bf16x32(b2 + k), load_bf16x32(b3 + k)
1009 };
1010 for (int r = 0; r < rows; ++r) {
1011 const __m512bh av =
1012 load_bf16x32(a_bf16 + (size_t)r * (size_t)K + k);
1013 for (int lane = 0; lane < 4; ++lane) {
1014 acc[r][lane] = _mm512_dpbf16_ps(acc[r][lane], av, bv[lane]);
1015 }
1016 }
1017 }
1018 for (int r = 0; r < rows; ++r) {
1019 float sums[4];
1020 for (int lane = 0; lane < 4; ++lane) {
1021 sums[lane] = _mm512_reduce_add_ps(acc[r][lane]);
1022 }
1023 const uint16_t *ar = a_bf16 + (size_t)r * (size_t)K;
1024 for (int tail = k; tail < K; ++tail) {
1025 const float av = bf16_to_float(ar[tail]);
1026 sums[0] += av * bf16_to_float(b0[tail]);
1027 sums[1] += av * bf16_to_float(b1[tail]);
1028 sums[2] += av * bf16_to_float(b2[tail]);
1029 sums[3] += av * bf16_to_float(b3[tail]);
1030 }
1031 float *dst = args->C + (size_t)(row0 + r) * (size_t)N;
1032 for (int lane = 0; lane < 4; ++lane) {
1033 if (args->bias) sums[lane] += args->bias[j + lane];
1034 dst[j + lane] = bf16_to_float(float_to_bf16(sums[lane]));
1035 }
1036 }
1037 }
1038 for (; j < N; ++j) {
1039 const uint16_t *b = args->B + (size_t)j * K;
1040 __m512 acc[ROW_TILE];
1041 for (int r = 0; r < rows; ++r) acc[r] = _mm512_setzero_ps();
1042 int k = 0;
1043 for (; k <= K - 32; k += 32) {
1044 const __m512bh bv = load_bf16x32(b + k);
1045 for (int r = 0; r < rows; ++r) {
1046 acc[r] = _mm512_dpbf16_ps(
1047 acc[r], load_bf16x32(a_bf16 + (size_t)r * (size_t)K + k), bv);
1048 }
1049 }
1050 for (int r = 0; r < rows; ++r) {
1051 const uint16_t *ar = a_bf16 + (size_t)r * (size_t)K;
1052 float sum = _mm512_reduce_add_ps(acc[r]);
1053 for (int tail = k; tail < K; ++tail) {
1054 sum += bf16_to_float(ar[tail]) * bf16_to_float(b[tail]);
1055 }
1056 if (args->bias) sum += args->bias[j];
1057 args->C[(size_t)(row0 + r) * (size_t)N + j] =
1059 }
1060 }
1061#else
1062 for (int r = 0; r < rows; ++r) {
1063 const uint16_t *ar = a_bf16 + (size_t)r * (size_t)K;
1064 float *dst = args->C + (size_t)(row0 + r) * (size_t)N;
1065 for (int j = 0; j < N; ++j) {
1066 const uint16_t *b = args->B + (size_t)j * K;
1067 float sum = args->bias ? args->bias[j] : 0.0f;
1068 for (int k = 0; k < K; ++k) {
1069 sum += bf16_to_float(ar[k]) * bf16_to_float(b[k]);
1070 }
1071 dst[j] = bf16_to_float(float_to_bf16(sum));
1072 }
1073 }
1074#endif
1075 }
1076}
1077
1079 const void *B,
1080 const float *bias,
1081 float *C,
1082 int M, int N, int K)
1083{
1084 const uint16_t *weights = (const uint16_t *)B;
1085 if (!A || !weights || !C || M <= 0 || N <= 0 || K <= 0) return;
1086
1087 ck_gemm_bf16_native_args_t args = {
1088 .A = A, .B = weights, .bias = bias, .C = C, .M = M, .N = N, .K = K
1089 };
1090 ck_threadpool_t *pool = ck_threadpool_global();
1091 int active = pool ? ck_threadpool_n_threads(pool) : 1;
1092 if (active > M) active = M;
1093 if (active > 24) active = 24;
1094 if (!pool || active <= 1 || (size_t)M * (size_t)N <= 4096) {
1095 ck_gemm_bf16_native_work(0, 1, &args);
1096 return;
1097 }
1099}
1100
1101typedef struct {
1102 const float *src;
1103 uint16_t *dst;
1104 size_t count;
1105} ck_bf16_convert_args_t;
1106
1107static void ck_bf16_convert_work(int ith, int nth, void *opaque)
1108{
1109 ck_bf16_convert_args_t *args = (ck_bf16_convert_args_t *)opaque;
1110 const size_t begin = args->count * (size_t)ith / (size_t)nth;
1111 const size_t end = args->count * (size_t)(ith + 1) / (size_t)nth;
1112 for (size_t i = begin; i < end; ++i) args->dst[i] = float_to_bf16(args->src[i]);
1113}
1114
1115typedef struct {
1116 const uint16_t *A;
1117 const uint16_t *B;
1118 const float *bias;
1119 float *C;
1120 int M;
1121 int N;
1122 int K;
1123 int failed;
1124} ck_gemm_bf16_amx_args_t;
1125
1126static void ck_gemm_bf16_amx_work(int ith, int nth, void *opaque)
1127{
1128#if HAVE_AMX_BF16
1129 ck_gemm_bf16_amx_args_t *args = (ck_gemm_bf16_amx_args_t *)opaque;
1130 if (!ck_amx_request_xtile_data()) {
1131 __atomic_store_n(&args->failed, 1, __ATOMIC_RELAXED);
1132 return;
1133 }
1134 ck_amx_config_bf16_16x16x32();
1135 uint16_t b_tile[16 * 32];
1136 const int mb = args->M / 16;
1137 const int nb = args->N / 16;
1138 const int m_groups = (mb + 3) / 4;
1139 const int jobs = m_groups * nb;
1140
1141 for (int job = ith; job < jobs; job += nth) {
1142 const int m_group = job / nb;
1143 const int j = (job % nb) * 16;
1144 const int group_blocks = (mb - m_group * 4 < 4) ? mb - m_group * 4 : 4;
1145 _tile_zero(2);
1146 if (group_blocks > 1) _tile_zero(3);
1147 if (group_blocks > 2) _tile_zero(4);
1148 if (group_blocks > 3) _tile_zero(5);
1149 for (int k = 0; k < args->K; k += 32) {
1150 ck_pack_bf16_ktile_pairs_16x16(b_tile, args->B, args->K, j, k);
1151 _tile_loadd(1, b_tile, 32 * (int)sizeof(uint16_t));
1152 for (int g = 0; g < group_blocks; ++g) {
1153 const int i = (m_group * 4 + g) * 16;
1154 _tile_loadd(0, args->A + (size_t)i * args->K + k,
1155 args->K * (int)sizeof(uint16_t));
1156 switch (g) {
1157 case 0: _tile_dpbf16ps(2, 0, 1); break;
1158 case 1: _tile_dpbf16ps(3, 0, 1); break;
1159 case 2: _tile_dpbf16ps(4, 0, 1); break;
1160 default: _tile_dpbf16ps(5, 0, 1); break;
1161 }
1162 }
1163 }
1164 for (int g = 0; g < group_blocks; ++g) {
1165 const int i = (m_group * 4 + g) * 16;
1166 float *tile_dst = args->C + (size_t)i * args->N + j;
1167 const int tile_stride = args->N * (int)sizeof(float);
1168 switch (g) {
1169 case 0: _tile_stored(2, tile_dst, tile_stride); break;
1170 case 1: _tile_stored(3, tile_dst, tile_stride); break;
1171 case 2: _tile_stored(4, tile_dst, tile_stride); break;
1172 default: _tile_stored(5, tile_dst, tile_stride); break;
1173 }
1174 if (args->bias) {
1175 for (int ii = 0; ii < 16; ++ii) {
1176 float *row = args->C + (size_t)(i + ii) * args->N + j;
1177 for (int jj = 0; jj < 16; ++jj) row[jj] += args->bias[j + jj];
1178 }
1179 }
1180 }
1181 }
1182 _tile_release();
1183#else
1184 (void)ith; (void)nth; (void)opaque;
1185#endif
1186}
1187
1188typedef struct {
1189 float *values;
1190 size_t count;
1191} ck_bf16_round_args_t;
1192
1193static void ck_bf16_round_work(int ith, int nth, void *opaque)
1194{
1195 ck_bf16_round_args_t *args = (ck_bf16_round_args_t *)opaque;
1196 const size_t begin = args->count * (size_t)ith / (size_t)nth;
1197 const size_t end = args->count * (size_t)(ith + 1) / (size_t)nth;
1198 for (size_t i = begin; i < end; ++i) {
1199 args->values[i] = bf16_to_float(float_to_bf16(args->values[i]));
1200 }
1201}
1202
1204{
1205#if HAVE_AMX_BF16
1206 return ck_amx_request_xtile_data();
1207#else
1208 return 0;
1209#endif
1210}
1211
1212int ck_gemm_bf16_fp32out_amx_raw(const uint16_t *A,
1213 const uint16_t *B,
1214 float *C,
1215 int M, int N, int K,
1216 int accumulate)
1217{
1218#if HAVE_AMX_BF16
1219 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0 ||
1220 (M % 16) != 0 || (N % 16) != 0 || (K % 2) != 0 ||
1221 !ck_amx_request_xtile_data()) {
1222 return 0;
1223 }
1224 /* Match oneDNN BRGEMM: largest even divisor of K no greater than 32. */
1225 int k_block = K < 32 ? K : 32;
1226 while (k_block > 2 && K % k_block != 0) k_block -= 2;
1227 ck_amx_config_bf16_16x16_kblock(k_block);
1228 uint16_t b_tile[16 * 32];
1229 for (int i = 0; i < M; i += 16) {
1230 for (int j = 0; j < N; j += 16) {
1231 if (accumulate) {
1232 _tile_loadd(2, C + (size_t)i * (size_t)N + (size_t)j,
1233 N * (int)sizeof(float));
1234 } else {
1235 _tile_zero(2);
1236 }
1237 for (int k = 0; k < K; k += k_block) {
1238 memset(b_tile, 0, sizeof(b_tile));
1239 for (int kp = 0; kp < k_block / 2; ++kp) {
1240 const int k0 = k + kp * 2;
1241 for (int nn = 0; nn < 16; ++nn) {
1242 b_tile[(size_t)kp * 32u + (size_t)nn * 2u] =
1243 B[(size_t)(j + nn) * (size_t)K + (size_t)k0];
1244 b_tile[(size_t)kp * 32u + (size_t)nn * 2u + 1u] =
1245 B[(size_t)(j + nn) * (size_t)K + (size_t)k0 + 1u];
1246 }
1247 }
1248 _tile_loadd(0, A + (size_t)i * (size_t)K + (size_t)k,
1249 K * (int)sizeof(uint16_t));
1250 _tile_loadd(1, b_tile, 32 * (int)sizeof(uint16_t));
1251 _tile_dpbf16ps(2, 0, 1);
1252 }
1253 _tile_stored(2, C + (size_t)i * (size_t)N + (size_t)j,
1254 N * (int)sizeof(float));
1255 }
1256 }
1257 _tile_release();
1258 return 1;
1259#else
1260 (void)A; (void)B; (void)C; (void)M; (void)N; (void)K; (void)accumulate;
1261 return 0;
1262#endif
1263}
1264
1266 const void *B,
1267 const float *bias,
1268 float *C,
1269 int M, int N, int K,
1270 uint16_t *a_bf16,
1271 size_t a_bf16_bytes)
1272{
1273#if HAVE_AMX_BF16
1274 if (!A || !B || !C || M < 16 || N < 16 || K < 32 ||
1275 (M % 16) != 0 || (N % 16) != 0 || (K % 32) != 0) {
1276 fprintf(stderr,
1277 "HARD KERNEL CONTRACT FAULT: AMX BF16 GEMM requires non-null buffers "
1278 "and M%%16=N%%16=K%%32=0 (M=%d N=%d K=%d)\n",
1279 M, N, K);
1280 abort();
1281 }
1282 const size_t input_count = (size_t)M * K;
1283 if (!a_bf16 || input_count > SIZE_MAX / sizeof(uint16_t) ||
1284 a_bf16_bytes < input_count * sizeof(uint16_t)) {
1285 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: AMX BF16 activation workspace is too small\n");
1286 abort();
1287 }
1288 ck_threadpool_t *pool = ck_threadpool_global();
1289 int active = pool ? ck_threadpool_n_threads(pool) : 1;
1290 if (active > 24) active = 24;
1291 ck_bf16_convert_args_t convert = {.src=A, .dst=a_bf16, .count=input_count};
1292 if (pool && active > 1) ck_threadpool_dispatch_n(pool, active, ck_bf16_convert_work, &convert);
1293 else ck_bf16_convert_work(0, 1, &convert);
1294 ck_gemm_bf16_amx_args_t gemm = {
1295 .A=a_bf16, .B=(const uint16_t *)B, .bias=bias, .C=C,
1296 .M=M, .N=N, .K=K, .failed=0
1297 };
1298 if (pool && active > 1) ck_threadpool_dispatch_n(pool, active, ck_gemm_bf16_amx_work, &gemm);
1299 else ck_gemm_bf16_amx_work(0, 1, &gemm);
1300 if (__atomic_load_n(&gemm.failed, __ATOMIC_RELAXED)) {
1301 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: AMX tile permission request failed\n");
1302 abort();
1303 }
1304 ck_bf16_round_args_t round = {.values=C, .count=(size_t)M * N};
1305 if (pool && active > 1) ck_threadpool_dispatch_n(pool, active, ck_bf16_round_work, &round);
1306 else ck_bf16_round_work(0, 1, &round);
1307 return;
1308#else
1309 (void)A; (void)B; (void)bias; (void)C; (void)M; (void)N; (void)K;
1310 (void)a_bf16; (void)a_bf16_bytes;
1311 fprintf(stderr,
1312 "HARD KERNEL CONTRACT FAULT: gemm_nt_bf16_amx_bf16_storage was selected "
1313 "without AMX BF16 support\n");
1314 abort();
1315#endif
1316}
1317
1319 const void *B,
1320 const float *bias,
1321 float *C,
1322 int M, int N, int K)
1323{
1324 size_t input_count = 0;
1325 if (M > 0 && K > 0 && (size_t)M <= SIZE_MAX / (size_t)K) {
1326 input_count = (size_t)M * (size_t)K;
1327 }
1328 uint16_t *workspace = input_count > 0 && input_count <= SIZE_MAX / sizeof(uint16_t)
1329 ? (uint16_t *)malloc(input_count * sizeof(uint16_t))
1330 : NULL;
1331 if (!workspace) {
1332 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: AMX BF16 compatibility workspace allocation failed\n");
1333 abort();
1334 }
1336 A, B, bias, C, M, N, K, workspace, input_count * sizeof(uint16_t));
1337 free(workspace);
1338}
1339
1341 const float *A, const void *B, const float *bias, float *C,
1342 int M, int N, int K, uint16_t *a_bf16, size_t a_bf16_bytes)
1343{
1344 const int amx_shape = M >= 16 && N >= 16 && K >= 32 &&
1345 (M % 16) == 0 && (N % 16) == 0 && (K % 32) == 0;
1346 if (amx_shape && ck_gemm_bf16_amx_available()) {
1348 A, B, bias, C, M, N, K, a_bf16, a_bf16_bytes);
1349 return;
1350 }
1351 gemm_nt_bf16_native_bf16_storage(A, B, bias, C, M, N, K);
1352}
1353
1355 const void *B,
1356 const float *bias,
1357 float *C,
1358 int M, int N, int K)
1359{
1360 const int amx_shape = M >= 16 && N >= 16 && K >= 32 &&
1361 (M % 16) == 0 && (N % 16) == 0 && (K % 32) == 0;
1362 if (amx_shape && ck_gemm_bf16_amx_available()) {
1363 gemm_nt_bf16_amx_bf16_storage(A, B, bias, C, M, N, K);
1364 return;
1365 }
1366 gemm_nt_bf16_native_bf16_storage(A, B, bias, C, M, N, K);
1367}
1368
1369#ifdef USE_ONEDNN
1370static dnnl_engine_t ck_pytorch_brgemm_engine;
1371static dnnl_stream_t ck_pytorch_brgemm_stream;
1372static int ck_pytorch_brgemm_init_status = -1;
1373static pthread_once_t ck_pytorch_brgemm_once = PTHREAD_ONCE_INIT;
1374static pthread_mutex_t ck_pytorch_brgemm_lock = PTHREAD_MUTEX_INITIALIZER;
1375static const dnnl_version_t *ck_pytorch_brgemm_version;
1376
1377static void ck_pytorch_brgemm_init(void)
1378{
1379 const dnnl_version_t *version = dnnl_version();
1380 if (!version || version->cpu_runtime != DNNL_RUNTIME_OMP || !version->hash) {
1381 fprintf(stderr,
1382 "HARD KERNEL CONTRACT FAULT: PyTorch oneDNN BF16 BRGEMM requires "
1383 "an identity-bearing OpenMP oneDNN runtime "
1384 "(found %d.%d.%d runtime=%u hash=%s)\n",
1385 version ? version->major : -1,
1386 version ? version->minor : -1,
1387 version ? version->patch : -1,
1388 version ? version->cpu_runtime : 0,
1389 version && version->hash ? version->hash : "<missing>");
1390 return;
1391 }
1392 ck_pytorch_brgemm_version = version;
1393 if (dnnl_engine_create(&ck_pytorch_brgemm_engine, dnnl_cpu, 0) != dnnl_success) return;
1394 if (dnnl_stream_create(&ck_pytorch_brgemm_stream, ck_pytorch_brgemm_engine,
1395 dnnl_stream_default_flags) != dnnl_success) {
1396 dnnl_engine_destroy(ck_pytorch_brgemm_engine);
1397 ck_pytorch_brgemm_engine = NULL;
1398 return;
1399 }
1400 ck_pytorch_brgemm_init_status = 0;
1401}
1402
1403static void ck_pytorch_brgemm_require_version(int major, int minor, int patch,
1404 const char *source_hash,
1405 const char *provider,
1406 int M, int N, int K)
1407{
1408 pthread_once(&ck_pytorch_brgemm_once, ck_pytorch_brgemm_init);
1409 if (ck_pytorch_brgemm_init_status != 0 || !ck_pytorch_brgemm_version) {
1410 ck_pytorch_brgemm_fault("could not initialize oneDNN", M, N, K);
1411 }
1412 if (ck_pytorch_brgemm_version->major != major ||
1413 ck_pytorch_brgemm_version->minor != minor ||
1414 ck_pytorch_brgemm_version->patch != patch ||
1415 strcmp(ck_pytorch_brgemm_version->hash, source_hash) != 0) {
1416 fprintf(stderr,
1417 "HARD KERNEL CONTRACT FAULT: %s requires oneDNN %d.%d.%d "
1418 "OpenMP at %s (found %d.%d.%d runtime=%u hash=%s)\n",
1419 provider, major, minor, patch, source_hash,
1420 ck_pytorch_brgemm_version->major,
1421 ck_pytorch_brgemm_version->minor,
1422 ck_pytorch_brgemm_version->patch,
1423 ck_pytorch_brgemm_version->cpu_runtime,
1424 ck_pytorch_brgemm_version->hash);
1425 abort();
1426 }
1427}
1428#endif
1429
1431 int M, int N, int K,
1432 size_t *input_bytes, size_t *output_bytes, size_t *bias_bytes)
1433{
1434 if (M <= 0 || N <= 0 || K <= 0 ||
1435 (size_t)M > SIZE_MAX / (size_t)K ||
1436 (size_t)M > SIZE_MAX / (size_t)N) {
1437 ck_pytorch_brgemm_fault("workspace size overflow", M, N, K);
1438 }
1439 const size_t input_count = (size_t)M * (size_t)K;
1440 const size_t output_count = (size_t)M * (size_t)N;
1441 if (input_count > SIZE_MAX / sizeof(uint16_t) ||
1442 output_count > SIZE_MAX / sizeof(uint16_t) ||
1443 (size_t)N > SIZE_MAX / sizeof(uint16_t)) {
1444 ck_pytorch_brgemm_fault("workspace byte size overflow", M, N, K);
1445 }
1446 *input_bytes = input_count * sizeof(uint16_t);
1447 *output_bytes = output_count * sizeof(uint16_t);
1448 *bias_bytes = (size_t)N * sizeof(uint16_t);
1449}
1450
1452 const float *A, const void *B, const float *bias, float *C,
1453 int M, int N, int K,
1454 uint16_t *input_bf16, size_t input_bf16_bytes,
1455 uint16_t *output_bf16, size_t output_bf16_bytes,
1456 uint16_t *bias_bf16, size_t bias_bf16_bytes)
1457{
1458#ifdef USE_ONEDNN
1459 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0) {
1460 ck_pytorch_brgemm_fault("received an invalid tensor contract", M, N, K);
1461 }
1462 if (ck_pytorch_brgemm_init_status != 0) {
1463 ck_pytorch_brgemm_fault("could not initialize oneDNN", M, N, K);
1464 }
1465
1466 size_t required_input_bytes = 0;
1467 size_t required_output_bytes = 0;
1468 size_t required_bias_bytes = 0;
1470 M, N, K, &required_input_bytes, &required_output_bytes,
1471 &required_bias_bytes);
1472 const size_t input_count = required_input_bytes / sizeof(*input_bf16);
1473 const size_t output_count = required_output_bytes / sizeof(*output_bf16);
1474 if (!input_bf16 || input_bf16_bytes < required_input_bytes ||
1475 !output_bf16 || output_bf16_bytes < required_output_bytes ||
1476 (bias && (!bias_bf16 || bias_bf16_bytes < required_bias_bytes))) {
1477 ck_pytorch_brgemm_fault("received an undersized workspace", M, N, K);
1478 }
1479 for (size_t i = 0; i < input_count; ++i) input_bf16[i] = float_to_bf16(A[i]);
1480 if (bias) {
1481 for (int j = 0; j < N; ++j) bias_bf16[j] = float_to_bf16(bias[j]);
1482 for (int i = 0; i < M; ++i) {
1483 memcpy(output_bf16 + (size_t)i * (size_t)N,
1484 bias_bf16, (size_t)N * sizeof(*bias_bf16));
1485 }
1486 }
1487
1488 dnnl_memory_desc_t src_md = NULL, weights_md = NULL, dst_md = NULL;
1489 dnnl_primitive_attr_t attr = NULL;
1490 dnnl_post_ops_t post_ops = NULL;
1491 dnnl_primitive_desc_t primitive_desc = NULL;
1492 dnnl_primitive_t primitive = NULL;
1493 dnnl_memory_t src_mem = NULL, weights_mem = NULL, dst_mem = NULL;
1494 dnnl_dims_t src_dims = {M, K};
1495 dnnl_dims_t weights_dims = {K, N};
1496 dnnl_dims_t dst_dims = {M, N};
1497 dnnl_dims_t src_strides = {K, 1};
1498 dnnl_dims_t weights_strides = {1, K};
1499 dnnl_dims_t dst_strides = {N, 1};
1500 dnnl_status_t status = dnnl_success;
1501
1502#define CK_DNNL(call) do { status = (call); if (status != dnnl_success) goto cleanup; } while (0)
1503 pthread_mutex_lock(&ck_pytorch_brgemm_lock);
1504 CK_DNNL(dnnl_memory_desc_create_with_strides(&src_md, 2, src_dims, dnnl_bf16, src_strides));
1505 CK_DNNL(dnnl_memory_desc_create_with_strides(
1506 &weights_md, 2, weights_dims, dnnl_bf16, weights_strides));
1507 CK_DNNL(dnnl_memory_desc_create_with_strides(&dst_md, 2, dst_dims, dnnl_bf16, dst_strides));
1508 if (bias) {
1509 CK_DNNL(dnnl_primitive_attr_create(&attr));
1510 CK_DNNL(dnnl_post_ops_create(&post_ops));
1511 CK_DNNL(dnnl_post_ops_append_sum(post_ops, 1.0f, 0, dnnl_bf16));
1512 CK_DNNL(dnnl_primitive_attr_set_post_ops(attr, post_ops));
1513 }
1514 CK_DNNL(dnnl_matmul_primitive_desc_create(
1515 &primitive_desc, ck_pytorch_brgemm_engine, src_md, weights_md, NULL, dst_md, attr));
1516 CK_DNNL(dnnl_primitive_create(&primitive, primitive_desc));
1517 CK_DNNL(dnnl_memory_create(&src_mem, src_md, ck_pytorch_brgemm_engine, input_bf16));
1518 CK_DNNL(dnnl_memory_create(
1519 &weights_mem, weights_md, ck_pytorch_brgemm_engine, (void *)B));
1520 CK_DNNL(dnnl_memory_create(&dst_mem, dst_md, ck_pytorch_brgemm_engine, output_bf16));
1521 dnnl_exec_arg_t args[] = {
1522 {DNNL_ARG_SRC, src_mem},
1523 {DNNL_ARG_WEIGHTS, weights_mem},
1524 {DNNL_ARG_DST, dst_mem},
1525 };
1526 CK_DNNL(dnnl_primitive_execute(
1527 primitive, ck_pytorch_brgemm_stream, (int)(sizeof(args) / sizeof(args[0])), args));
1528 CK_DNNL(dnnl_stream_wait(ck_pytorch_brgemm_stream));
1529
1530cleanup:
1531 if (dst_mem) dnnl_memory_destroy(dst_mem);
1532 if (weights_mem) dnnl_memory_destroy(weights_mem);
1533 if (src_mem) dnnl_memory_destroy(src_mem);
1534 if (primitive) dnnl_primitive_destroy(primitive);
1535 if (primitive_desc) dnnl_primitive_desc_destroy(primitive_desc);
1536 if (dst_md) dnnl_memory_desc_destroy(dst_md);
1537 if (weights_md) dnnl_memory_desc_destroy(weights_md);
1538 if (src_md) dnnl_memory_desc_destroy(src_md);
1539 if (post_ops) dnnl_post_ops_destroy(post_ops);
1540 if (attr) dnnl_primitive_attr_destroy(attr);
1541 pthread_mutex_unlock(&ck_pytorch_brgemm_lock);
1542#undef CK_DNNL
1543
1544 if (status != dnnl_success) {
1545 ck_pytorch_brgemm_fault("execution failed", M, N, K);
1546 }
1547 for (size_t i = 0; i < output_count; ++i) C[i] = bf16_to_float(output_bf16[i]);
1548#else
1549 (void)A; (void)B; (void)bias; (void)C; (void)M; (void)N; (void)K;
1550 (void)input_bf16; (void)input_bf16_bytes;
1551 (void)output_bf16; (void)output_bf16_bytes;
1552 (void)bias_bf16; (void)bias_bf16_bytes;
1553 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: PyTorch oneDNN BF16 BRGEMM was "
1554 "selected without USE_ONEDNN=1\n");
1555 abort();
1556#endif
1557}
1558
1560 const float *A, const void *B, const float *bias, float *C,
1561 int M, int N, int K)
1562{
1563 size_t input_bytes = 0;
1564 size_t output_bytes = 0;
1565 size_t bias_bytes = 0;
1567 M, N, K, &input_bytes, &output_bytes, &bias_bytes);
1568 uint16_t *input_bf16 = (uint16_t *)malloc(input_bytes);
1569 uint16_t *output_bf16 = (uint16_t *)malloc(output_bytes);
1570 uint16_t *bias_bf16 = bias ? (uint16_t *)malloc(bias_bytes) : NULL;
1571 if (!input_bf16 || !output_bf16 || (bias && !bias_bf16)) {
1572 free(bias_bf16);
1573 free(output_bf16);
1574 free(input_bf16);
1575 ck_pytorch_brgemm_fault("workspace allocation failed", M, N, K);
1576 }
1578 A, B, bias, C, M, N, K,
1579 input_bf16, input_bytes, output_bf16, output_bytes,
1580 bias_bf16, bias_bytes);
1581 free(bias_bf16);
1582 free(output_bf16);
1583 free(input_bf16);
1584}
1585
1587 const void *B,
1588 const float *bias,
1589 float *C,
1590 int M, int N, int K)
1591{
1592#ifdef USE_ONEDNN
1593 ck_pytorch_brgemm_require_version(
1594 3, 7, 1, "8d263e693366ef8db40acc569cc7d8edf644556d",
1595 "gemm_nt_bf16_pytorch_onednn_brgemm_bf16_storage", M, N, K);
1596#endif
1598 A, B, bias, C, M, N, K);
1599}
1600
1602 const float *A, const void *B, const float *bias, float *C,
1603 int M, int N, int K)
1604{
1605#ifdef USE_ONEDNN
1606 ck_pytorch_brgemm_require_version(
1607 3, 12, 0, "80afa71049cd69a3df32adcccb623b12cd7baa22",
1608 "gemm_nt_bf16_pytorch_onednn_3_12_brgemm_bf16_storage", M, N, K);
1609#endif
1611 A, B, bias, C, M, N, K);
1612}
1613
1615 const float *A, const void *B, const float *bias, float *C,
1616 int M, int N, int K,
1617 uint16_t *input_bf16, size_t input_bf16_bytes,
1618 uint16_t *output_bf16, size_t output_bf16_bytes,
1619 uint16_t *bias_bf16, size_t bias_bf16_bytes)
1620{
1621#ifdef USE_ONEDNN
1622 ck_pytorch_brgemm_require_version(
1623 3, 12, 0, "80afa71049cd69a3df32adcccb623b12cd7baa22",
1624 "gemm_nt_bf16_pytorch_onednn_3_12_brgemm_bf16_storage_workspace",
1625 M, N, K);
1626#endif
1628 A, B, bias, C, M, N, K,
1629 input_bf16, input_bf16_bytes, output_bf16, output_bf16_bytes,
1630 bias_bf16, bias_bf16_bytes);
1631}
1632
1634 const float *input, const void *weights, const float *bias, float *output,
1635 int batch, int out_channels, int in_channels, int temporal,
1636 int patch_h, int patch_w)
1637{
1638#ifdef USE_ONEDNN
1639 if (!input || !weights || !bias || !output || batch <= 0 ||
1640 out_channels <= 0 || in_channels <= 0 || temporal <= 0 ||
1641 patch_h <= 0 || patch_w <= 0) {
1642 ck_pytorch_brgemm_fault("invalid Conv3D patch contract", batch,
1643 out_channels, in_channels * temporal * patch_h * patch_w);
1644 }
1645 pthread_once(&ck_pytorch_brgemm_once, ck_pytorch_brgemm_init);
1646 if (ck_pytorch_brgemm_init_status != 0) {
1647 ck_pytorch_brgemm_fault("could not initialize oneDNN Conv3D", batch,
1648 out_channels, in_channels * temporal * patch_h * patch_w);
1649 }
1650 ck_pytorch_brgemm_require_version(
1651 3, 7, 1, "8d263e693366ef8db40acc569cc7d8edf644556d",
1652 "patch_projection_bf16_pytorch_onednn_conv3d_storage",
1653 batch, out_channels, in_channels * temporal * patch_h * patch_w);
1654
1655 const size_t input_count = (size_t)batch * (size_t)in_channels *
1656 (size_t)temporal * (size_t)patch_h * (size_t)patch_w;
1657 const size_t output_count = (size_t)batch * (size_t)out_channels;
1658 uint16_t *input_bf16 = (uint16_t *)malloc(input_count * sizeof(*input_bf16));
1659 uint16_t *bias_bf16 = (uint16_t *)malloc((size_t)out_channels * sizeof(*bias_bf16));
1660 uint16_t *output_bf16 = (uint16_t *)malloc(output_count * sizeof(*output_bf16));
1661 if (!input_bf16 || !bias_bf16 || !output_bf16) {
1662 free(output_bf16);
1663 free(bias_bf16);
1664 free(input_bf16);
1665 ck_pytorch_brgemm_fault("Conv3D workspace allocation failed", batch,
1666 out_channels, in_channels * temporal * patch_h * patch_w);
1667 }
1668 for (size_t i = 0; i < input_count; ++i) input_bf16[i] = float_to_bf16(input[i]);
1669 for (int i = 0; i < out_channels; ++i) bias_bf16[i] = float_to_bf16(bias[i]);
1670
1671 dnnl_dims_t src_dims = {batch, in_channels, temporal, patch_h, patch_w};
1672 dnnl_dims_t weight_dims = {
1673 out_channels, in_channels, temporal, patch_h, patch_w};
1674 dnnl_dims_t bias_dims = {out_channels};
1675 dnnl_dims_t dst_dims = {batch, out_channels, 1, 1, 1};
1676 dnnl_dims_t strides = {temporal, patch_h, patch_w};
1677 dnnl_dims_t dilates = {0, 0, 0};
1678 dnnl_dims_t padding = {0, 0, 0};
1679
1680 dnnl_memory_desc_t user_src_md = NULL, user_weight_md = NULL;
1681 dnnl_memory_desc_t bias_md = NULL, user_dst_md = NULL;
1682 dnnl_memory_desc_t any_src_md = NULL, any_weight_md = NULL, any_dst_md = NULL;
1683 dnnl_primitive_desc_t conv_pd = NULL;
1684 dnnl_primitive_t conv = NULL;
1685 dnnl_primitive_desc_t reorder_pd = NULL;
1686 dnnl_primitive_t reorder = NULL;
1687 dnnl_exec_arg_t reorder_args[2];
1688 dnnl_memory_t user_src = NULL, user_weight = NULL, bias_mem = NULL, user_dst = NULL;
1689 dnnl_memory_t conv_src = NULL, conv_weight = NULL, conv_dst = NULL;
1690 dnnl_status_t status = dnnl_success;
1691
1692#define CK_DNNL_CONV(call) do { status = (call); if (status != dnnl_success) goto cleanup_conv; } while (0)
1693 pthread_mutex_lock(&ck_pytorch_brgemm_lock);
1694 CK_DNNL_CONV(dnnl_memory_desc_create_with_tag(
1695 &user_src_md, 5, src_dims, dnnl_bf16, dnnl_ncdhw));
1696 CK_DNNL_CONV(dnnl_memory_desc_create_with_tag(
1697 &user_weight_md, 5, weight_dims, dnnl_bf16, dnnl_oidhw));
1698 CK_DNNL_CONV(dnnl_memory_desc_create_with_tag(
1699 &bias_md, 1, bias_dims, dnnl_bf16, dnnl_x));
1700 CK_DNNL_CONV(dnnl_memory_desc_create_with_tag(
1701 &user_dst_md, 5, dst_dims, dnnl_bf16, dnnl_ncdhw));
1702 CK_DNNL_CONV(dnnl_memory_desc_create_with_tag(
1703 &any_src_md, 5, src_dims, dnnl_bf16, dnnl_format_tag_any));
1704 CK_DNNL_CONV(dnnl_memory_desc_create_with_tag(
1705 &any_weight_md, 5, weight_dims, dnnl_bf16, dnnl_format_tag_any));
1706 CK_DNNL_CONV(dnnl_memory_desc_create_with_tag(
1707 &any_dst_md, 5, dst_dims, dnnl_bf16, dnnl_format_tag_any));
1708 CK_DNNL_CONV(dnnl_convolution_forward_primitive_desc_create(
1709 &conv_pd, ck_pytorch_brgemm_engine, dnnl_forward_training,
1710 dnnl_convolution_direct, any_src_md, any_weight_md, bias_md, any_dst_md,
1711 strides, dilates, padding, padding, NULL));
1712
1713 const_dnnl_memory_desc_t conv_src_md =
1714 dnnl_primitive_desc_query_md(conv_pd, dnnl_query_src_md, 0);
1715 const_dnnl_memory_desc_t conv_weight_md =
1716 dnnl_primitive_desc_query_md(conv_pd, dnnl_query_weights_md, 0);
1717 const_dnnl_memory_desc_t conv_dst_md =
1718 dnnl_primitive_desc_query_md(conv_pd, dnnl_query_dst_md, 0);
1719 CK_DNNL_CONV(dnnl_memory_create(
1720 &user_src, user_src_md, ck_pytorch_brgemm_engine, input_bf16));
1721 CK_DNNL_CONV(dnnl_memory_create(
1722 &user_weight, user_weight_md, ck_pytorch_brgemm_engine, (void *)weights));
1723 CK_DNNL_CONV(dnnl_memory_create(
1724 &bias_mem, bias_md, ck_pytorch_brgemm_engine, bias_bf16));
1725 CK_DNNL_CONV(dnnl_memory_create(
1726 &user_dst, user_dst_md, ck_pytorch_brgemm_engine, output_bf16));
1727 CK_DNNL_CONV(dnnl_memory_create(
1728 &conv_src, conv_src_md, ck_pytorch_brgemm_engine, DNNL_MEMORY_ALLOCATE));
1729 CK_DNNL_CONV(dnnl_memory_create(
1730 &conv_weight, conv_weight_md, ck_pytorch_brgemm_engine, DNNL_MEMORY_ALLOCATE));
1731 CK_DNNL_CONV(dnnl_memory_create(
1732 &conv_dst, conv_dst_md, ck_pytorch_brgemm_engine, DNNL_MEMORY_ALLOCATE));
1733
1734 CK_DNNL_CONV(dnnl_reorder_primitive_desc_create(
1735 &reorder_pd, user_src_md, ck_pytorch_brgemm_engine,
1736 conv_src_md, ck_pytorch_brgemm_engine, NULL));
1737 CK_DNNL_CONV(dnnl_primitive_create(&reorder, reorder_pd));
1738 reorder_args[0] = (dnnl_exec_arg_t){DNNL_ARG_FROM, user_src};
1739 reorder_args[1] = (dnnl_exec_arg_t){DNNL_ARG_TO, conv_src};
1740 CK_DNNL_CONV(dnnl_primitive_execute(
1741 reorder, ck_pytorch_brgemm_stream, 2, reorder_args));
1742 dnnl_primitive_destroy(reorder); reorder = NULL;
1743 dnnl_primitive_desc_destroy(reorder_pd); reorder_pd = NULL;
1744
1745 CK_DNNL_CONV(dnnl_reorder_primitive_desc_create(
1746 &reorder_pd, user_weight_md, ck_pytorch_brgemm_engine,
1747 conv_weight_md, ck_pytorch_brgemm_engine, NULL));
1748 CK_DNNL_CONV(dnnl_primitive_create(&reorder, reorder_pd));
1749 reorder_args[0] = (dnnl_exec_arg_t){DNNL_ARG_FROM, user_weight};
1750 reorder_args[1] = (dnnl_exec_arg_t){DNNL_ARG_TO, conv_weight};
1751 CK_DNNL_CONV(dnnl_primitive_execute(
1752 reorder, ck_pytorch_brgemm_stream, 2, reorder_args));
1753 dnnl_primitive_destroy(reorder); reorder = NULL;
1754 dnnl_primitive_desc_destroy(reorder_pd); reorder_pd = NULL;
1755
1756 CK_DNNL_CONV(dnnl_primitive_create(&conv, conv_pd));
1757 dnnl_exec_arg_t conv_args[] = {
1758 {DNNL_ARG_SRC, conv_src},
1759 {DNNL_ARG_WEIGHTS, conv_weight},
1760 {DNNL_ARG_BIAS, bias_mem},
1761 {DNNL_ARG_DST, conv_dst},
1762 };
1763 CK_DNNL_CONV(dnnl_primitive_execute(
1764 conv, ck_pytorch_brgemm_stream,
1765 (int)(sizeof(conv_args) / sizeof(conv_args[0])), conv_args));
1766
1767 CK_DNNL_CONV(dnnl_reorder_primitive_desc_create(
1768 &reorder_pd, conv_dst_md, ck_pytorch_brgemm_engine,
1769 user_dst_md, ck_pytorch_brgemm_engine, NULL));
1770 CK_DNNL_CONV(dnnl_primitive_create(&reorder, reorder_pd));
1771 reorder_args[0] = (dnnl_exec_arg_t){DNNL_ARG_FROM, conv_dst};
1772 reorder_args[1] = (dnnl_exec_arg_t){DNNL_ARG_TO, user_dst};
1773 CK_DNNL_CONV(dnnl_primitive_execute(
1774 reorder, ck_pytorch_brgemm_stream, 2, reorder_args));
1775 CK_DNNL_CONV(dnnl_stream_wait(ck_pytorch_brgemm_stream));
1776
1777cleanup_conv:
1778 if (reorder) dnnl_primitive_destroy(reorder);
1779 if (reorder_pd) dnnl_primitive_desc_destroy(reorder_pd);
1780 if (conv) dnnl_primitive_destroy(conv);
1781 if (conv_dst) dnnl_memory_destroy(conv_dst);
1782 if (conv_weight) dnnl_memory_destroy(conv_weight);
1783 if (conv_src) dnnl_memory_destroy(conv_src);
1784 if (user_dst) dnnl_memory_destroy(user_dst);
1785 if (bias_mem) dnnl_memory_destroy(bias_mem);
1786 if (user_weight) dnnl_memory_destroy(user_weight);
1787 if (user_src) dnnl_memory_destroy(user_src);
1788 if (conv_pd) dnnl_primitive_desc_destroy(conv_pd);
1789 if (any_dst_md) dnnl_memory_desc_destroy(any_dst_md);
1790 if (any_weight_md) dnnl_memory_desc_destroy(any_weight_md);
1791 if (any_src_md) dnnl_memory_desc_destroy(any_src_md);
1792 if (user_dst_md) dnnl_memory_desc_destroy(user_dst_md);
1793 if (bias_md) dnnl_memory_desc_destroy(bias_md);
1794 if (user_weight_md) dnnl_memory_desc_destroy(user_weight_md);
1795 if (user_src_md) dnnl_memory_desc_destroy(user_src_md);
1796 pthread_mutex_unlock(&ck_pytorch_brgemm_lock);
1797#undef CK_DNNL_CONV
1798
1799 if (status != dnnl_success) {
1800 free(output_bf16);
1801 free(bias_bf16);
1802 free(input_bf16);
1803 ck_pytorch_brgemm_fault("oneDNN Conv3D execution failed", batch,
1804 out_channels, in_channels * temporal * patch_h * patch_w);
1805 }
1806 for (size_t i = 0; i < output_count; ++i) output[i] = bf16_to_float(output_bf16[i]);
1807 free(output_bf16);
1808 free(bias_bf16);
1809 free(input_bf16);
1810#else
1811 (void)input; (void)weights; (void)bias; (void)output; (void)batch;
1812 (void)out_channels; (void)in_channels; (void)temporal; (void)patch_h; (void)patch_w;
1813 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: PyTorch oneDNN BF16 Conv3D "
1814 "was selected without USE_ONEDNN=1\n");
1815 abort();
1816#endif
1817}
1818
1820 const float *image, const void *weights_t0, const void *weights_t1,
1821 const float *bias, float *output, int channels, int image_h, int image_w,
1822 int patch_size, int out_channels, int merge_size)
1823{
1824#ifdef USE_ONEDNN
1825 if (!image || !weights_t0 || !weights_t1 || !bias || !output ||
1826 channels <= 0 || image_h <= 0 || image_w <= 0 || patch_size <= 0 ||
1827 out_channels <= 0 || merge_size <= 0 || image_h % patch_size != 0 ||
1828 image_w % patch_size != 0) {
1829 ck_pytorch_brgemm_fault("invalid image patch projection contract",
1830 image_h, image_w, patch_size);
1831 }
1832 const int grid_h = image_h / patch_size;
1833 const int grid_w = image_w / patch_size;
1834 if (grid_h % merge_size != 0 || grid_w % merge_size != 0) {
1835 ck_pytorch_brgemm_fault("patch grid is not merge-tile aligned",
1836 grid_h, grid_w, merge_size);
1837 }
1838 const int batch = grid_h * grid_w;
1839 const int temporal = 2;
1840 const int half_k = channels * patch_size * patch_size;
1841 const int full_k = temporal * half_k;
1842 float *patches = (float *)malloc((size_t)batch * (size_t)full_k * sizeof(*patches));
1843 uint16_t *weights = (uint16_t *)malloc(
1844 (size_t)out_channels * (size_t)full_k * sizeof(*weights));
1845 if (!patches || !weights) {
1846 free(weights);
1847 free(patches);
1848 ck_pytorch_brgemm_fault("image patch projection workspace allocation failed",
1849 batch, out_channels, full_k);
1850 }
1851
1852 for (int tok = 0; tok < batch; ++tok) {
1853 const int tiles_per_row = grid_w / merge_size;
1854 const int tile_area = merge_size * merge_size;
1855 const int tile = tok / tile_area;
1856 const int within = tok % tile_area;
1857 const int patch_y = (tile / tiles_per_row) * merge_size + within / merge_size;
1858 const int patch_x = (tile % tiles_per_row) * merge_size + within % merge_size;
1859 float *dst = patches + (size_t)tok * (size_t)full_k;
1860 for (int c = 0; c < channels; ++c) {
1861 for (int t = 0; t < temporal; ++t) {
1862 for (int py = 0; py < patch_size; ++py) {
1863 const float *src = image +
1864 ((size_t)c * (size_t)image_h +
1865 (size_t)(patch_y * patch_size + py)) * (size_t)image_w +
1866 (size_t)(patch_x * patch_size);
1867 memcpy(dst, src, (size_t)patch_size * sizeof(*dst));
1868 dst += patch_size;
1869 }
1870 }
1871 }
1872 }
1873
1874 const uint16_t *w0 = (const uint16_t *)weights_t0;
1875 const uint16_t *w1 = (const uint16_t *)weights_t1;
1876 for (int n = 0; n < out_channels; ++n) {
1877 uint16_t *dst = weights + (size_t)n * (size_t)full_k;
1878 for (int c = 0; c < channels; ++c) {
1879 const size_t channel_offset =
1880 (size_t)n * (size_t)half_k +
1881 (size_t)c * (size_t)patch_size * (size_t)patch_size;
1882 const size_t plane_bytes =
1883 (size_t)patch_size * (size_t)patch_size * sizeof(*dst);
1884 memcpy(dst, w0 + channel_offset, plane_bytes);
1885 dst += patch_size * patch_size;
1886 memcpy(dst, w1 + channel_offset, plane_bytes);
1887 dst += patch_size * patch_size;
1888 }
1889 }
1890
1892 patches, weights, bias, output, batch, out_channels, channels,
1893 temporal, patch_size, patch_size);
1894 free(weights);
1895 free(patches);
1896#else
1897 (void)image; (void)weights_t0; (void)weights_t1; (void)bias; (void)output;
1898 (void)channels; (void)image_h; (void)image_w; (void)patch_size;
1899 (void)out_channels; (void)merge_size;
1900 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: PyTorch oneDNN BF16 image "
1901 "patch projection was selected without USE_ONEDNN=1\n");
1902 abort();
1903#endif
1904}
1905
1906typedef struct {
1907 const float *image;
1908 const uint16_t *weights_t0;
1909 const uint16_t *weights_t1;
1910 const float *bias;
1911 float *output;
1912 int channels;
1913 int image_h;
1914 int image_w;
1915 int patch_size;
1916 int out_channels;
1917 int merge_size;
1918 int grid_w;
1919 int batch;
1920} ck_patch_projection_bf16_native_args_t;
1921
1923 int ith, int nth, void *opaque)
1924{
1925 ck_patch_projection_bf16_native_args_t *args =
1926 (ck_patch_projection_bf16_native_args_t *)opaque;
1927 const int begin = args->batch * ith / nth;
1928 const int end = args->batch * (ith + 1) / nth;
1929 const int patch_area = args->patch_size * args->patch_size;
1930 const int half_k = args->channels * patch_area;
1931 const int tiles_per_row = args->grid_w / args->merge_size;
1932 const int tile_area = args->merge_size * args->merge_size;
1933
1934 for (int tok = begin; tok < end; ++tok) {
1935 const int tile = tok / tile_area;
1936 const int within = tok % tile_area;
1937 const int patch_y =
1938 (tile / tiles_per_row) * args->merge_size + within / args->merge_size;
1939 const int patch_x =
1940 (tile % tiles_per_row) * args->merge_size + within % args->merge_size;
1941 for (int n = 0; n < args->out_channels; ++n) {
1942 float sum = args->bias
1943 ? bf16_to_float(float_to_bf16(args->bias[n]))
1944 : 0.0f;
1945#if defined(__AVX512BF16__) && defined(__AVX512VL__)
1946 if (args->patch_size == 16) {
1947 __m256 acc = _mm256_setzero_ps();
1948 for (int c = 0; c < args->channels; ++c) {
1949 for (int t = 0; t < 2; ++t) {
1950 const uint16_t *weights = t == 0
1951 ? args->weights_t0 : args->weights_t1;
1952 const uint16_t *weight_plane = weights +
1953 (size_t)n * (size_t)half_k +
1954 (size_t)c * (size_t)patch_area;
1955 for (int py = 0; py < 16; ++py) {
1956 const float *src = args->image +
1957 ((size_t)c * (size_t)args->image_h +
1958 (size_t)(patch_y * 16 + py)) *
1959 (size_t)args->image_w +
1960 (size_t)(patch_x * 16);
1961 const __m256bh image_bf16 = _mm256_cvtne2ps_pbh(
1962 _mm256_loadu_ps(src + 8), _mm256_loadu_ps(src));
1963 const __m256bh weight_bf16 = (__m256bh)_mm256_loadu_si256(
1964 (const __m256i *)(weight_plane + (size_t)py * 16u));
1965 acc = _mm256_dpbf16_ps(acc, image_bf16, weight_bf16);
1966 }
1967 }
1968 }
1969 float lanes[8];
1970 _mm256_storeu_ps(lanes, acc);
1971 const float sum01 = lanes[0] + lanes[1];
1972 const float sum23 = lanes[2] + lanes[3];
1973 const float sum45 = lanes[4] + lanes[5];
1974 const float sum67 = lanes[6] + lanes[7];
1975 sum += (sum01 + sum23) + (sum45 + sum67);
1976 } else
1977#endif
1978 {
1979 for (int c = 0; c < args->channels; ++c) {
1980 for (int t = 0; t < 2; ++t) {
1981 const uint16_t *weights = t == 0
1982 ? args->weights_t0 : args->weights_t1;
1983 const uint16_t *weight_plane = weights +
1984 (size_t)n * (size_t)half_k +
1985 (size_t)c * (size_t)patch_area;
1986 for (int py = 0; py < args->patch_size; ++py) {
1987 const float *src = args->image +
1988 ((size_t)c * (size_t)args->image_h +
1989 (size_t)(patch_y * args->patch_size + py)) *
1990 (size_t)args->image_w +
1991 (size_t)(patch_x * args->patch_size);
1992 for (int px = 0; px < args->patch_size; ++px) {
1993 const float value = bf16_to_float(float_to_bf16(src[px]));
1994 const float weight = bf16_to_float(
1995 weight_plane[(size_t)py *
1996 (size_t)args->patch_size + (size_t)px]);
1997 sum += value * weight;
1998 }
1999 }
2000 }
2001 }
2002 }
2003 args->output[(size_t)tok * (size_t)args->out_channels + (size_t)n] =
2005 }
2006 }
2007}
2008
2010 const float *image, const void *weights_t0, const void *weights_t1,
2011 const float *bias, float *output, int channels, int image_h, int image_w,
2012 int patch_size, int out_channels, int merge_size)
2013{
2014 if (!image || !weights_t0 || !weights_t1 || !output || channels <= 0 ||
2015 image_h <= 0 || image_w <= 0 || patch_size <= 0 || out_channels <= 0 ||
2016 merge_size <= 0 || image_h % patch_size != 0 ||
2017 image_w % patch_size != 0) {
2018 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: invalid native BF16 image patch projection\n");
2019 abort();
2020 }
2021 const int grid_h = image_h / patch_size;
2022 const int grid_w = image_w / patch_size;
2023 if (grid_h % merge_size != 0 || grid_w % merge_size != 0) {
2024 fprintf(stderr, "HARD KERNEL CONTRACT FAULT: native BF16 patch grid is not merge aligned\n");
2025 abort();
2026 }
2027 ck_patch_projection_bf16_native_args_t args = {
2028 .image = image,
2029 .weights_t0 = (const uint16_t *)weights_t0,
2030 .weights_t1 = (const uint16_t *)weights_t1,
2031 .bias = bias,
2032 .output = output,
2033 .channels = channels,
2034 .image_h = image_h,
2035 .image_w = image_w,
2036 .patch_size = patch_size,
2037 .out_channels = out_channels,
2038 .merge_size = merge_size,
2039 .grid_w = grid_w,
2040 .batch = grid_h * grid_w,
2041 };
2042 ck_threadpool_t *pool = ck_threadpool_global();
2043 int active = pool ? ck_threadpool_n_threads(pool) : 1;
2044 if (active > args.batch) active = args.batch;
2045 if (active > 24) active = 24;
2046 if (pool && active > 1) {
2048 pool, active, ck_patch_projection_bf16_native_work, &args);
2049 } else {
2051 }
2052}
2053
2055 const void *B,
2056 const float *bias,
2057 float *C,
2058 int M, int N, int K,
2059 int row_begin, int row_end)
2060{
2061 gemm_nt_bf16_row_range(A, B, bias, C, M, N, K, row_begin, row_end);
2062 for (int row = row_begin; row < row_end; ++row) {
2063 float *dst = C + (size_t)row * (size_t)N;
2064 for (int col = 0; col < N; ++col) {
2065 dst[col] = bf16_to_float(float_to_bf16(dst[col]));
2066 }
2067 }
2068}
2069
2071 const void *B,
2072 const float *bias,
2073 float *C,
2074 int M, int N, int K,
2075 int row_begin, int row_end)
2076{
2077 const uint16_t *w = (const uint16_t *)B;
2078 if (!A || !w || !C || M <= 0 || N <= 0 || K <= 0 ||
2079 row_begin < 0 || row_begin >= row_end || row_end > M) {
2080 return;
2081 }
2082 for (int row = row_begin; row < row_end; ++row) {
2083 const float *a_row = A + (size_t)row * (size_t)K;
2084 float *dst = C + (size_t)row * (size_t)N;
2085 for (int col = 0; col < N; ++col) {
2086 const uint16_t *w_row = w + (size_t)col * (size_t)K;
2087 /* BF16 products are exact in double. A single FP32 accumulator can
2088 * round a long dot across a BF16 midpoint before output storage;
2089 * that changes downstream routing. Keep the accumulation accurate
2090 * until the declared FP32 bias/output conversion boundary. */
2091 double even = 0.0;
2092 double odd = 0.0;
2093 int k = 0;
2094 for (; k + 1 < K; k += 2) {
2095 even += (double)bf16_to_float(float_to_bf16(a_row[k])) *
2096 (double)bf16_to_float(w_row[k]);
2097 odd += (double)bf16_to_float(float_to_bf16(a_row[k + 1])) *
2098 (double)bf16_to_float(w_row[k + 1]);
2099 }
2100 if (k < K) {
2101 even += (double)bf16_to_float(float_to_bf16(a_row[k])) *
2102 (double)bf16_to_float(w_row[k]);
2103 }
2104 const float sum = (float)((even + odd) + (bias ? bias[col] : 0.0f));
2105 dst[col] = bf16_to_float(float_to_bf16(sum));
2106 }
2107 }
2108}
2109
2110static void ck_gemm_nt_bf16_storage_exact_rows(int begin, int end, void *opaque)
2111{
2112 const ck_gemm_nt_bf16_exact_args_t *args =
2113 (const ck_gemm_nt_bf16_exact_args_t *)opaque;
2115 args->A, args->B, args->bias, args->C,
2116 args->M, args->N, args->K, begin, end);
2117}
2118
2119static void ck_gemm_nt_bf16_accurate_storage_rows(int begin, int end, void *opaque)
2120{
2121 const ck_gemm_nt_bf16_exact_args_t *args =
2122 (const ck_gemm_nt_bf16_exact_args_t *)opaque;
2124 args->A, args->B, args->bias, args->C,
2125 args->M, args->N, args->K, begin, end);
2126}
2127
2129 const void *B,
2130 const float *bias,
2131 float *C,
2132 int M, int N, int K)
2133{
2134 ck_threadpool_t *pool = ck_threadpool_global();
2135 if (!pool || ck_threadpool_n_threads(pool) <= 1 || M < 2 ||
2136 (size_t)M * (size_t)N <= 4096) {
2137 gemm_nt_bf16_bf16_storage_row_range(A, B, bias, C, M, N, K, 0, M);
2138 return;
2139 }
2140
2141 ck_gemm_nt_bf16_exact_args_t args = {
2142 .A = A, .B = B, .bias = bias, .C = C, .M = M, .N = N, .K = K,
2143 };
2144 int active = ck_threadpool_n_threads(pool);
2145 if (active > M) active = M;
2146 int grain = M / (active * 4);
2147 if (grain < 1) grain = 1;
2149 pool, active, 0, M, grain, ck_gemm_nt_bf16_storage_exact_rows, &args);
2150}
2151
2152void gemm_nt_bf16_bf16_storage(const float *A,
2153 const void *B,
2154 const float *bias,
2155 float *C,
2156 int M, int N, int K)
2157{
2158 gemm_nt_bf16_bf16_storage_parallel_dispatch(A, B, bias, C, M, N, K);
2159}
2160
2162 const void *B,
2163 const float *bias,
2164 float *C,
2165 int M, int N, int K)
2166{
2167 ck_threadpool_t *pool = ck_threadpool_global();
2168 if (!pool || ck_threadpool_n_threads(pool) <= 1 || M < 2 ||
2169 (size_t)M * (size_t)N <= 4096) {
2170 gemm_nt_bf16_accurate_storage_row_range(A, B, bias, C, M, N, K, 0, M);
2171 return;
2172 }
2173
2174 ck_gemm_nt_bf16_exact_args_t args = {
2175 .A = A, .B = B, .bias = bias, .C = C, .M = M, .N = N, .K = K,
2176 };
2177 int active = ck_threadpool_n_threads(pool);
2178 if (active > M) active = M;
2179 int grain = M / (active * 4);
2180 if (grain < 1) grain = 1;
2182 pool, active, 0, M, grain, ck_gemm_nt_bf16_accurate_storage_rows, &args);
2183}
2184
2186 const void *B,
2187 const float *bias,
2188 float *C,
2189 int M, int N, int K)
2190{
2192}
2193
2194void gemm_backward_bf16_mixed(const uint16_t *d_output,
2195 const uint16_t *input,
2196 const uint16_t *weight,
2197 float *d_input,
2198 float *d_weight,
2199 float *d_bias,
2200 int tokens,
2201 int in_dim,
2202 int out_dim)
2203{
2204 if (!d_output || !input || !weight || tokens <= 0 || in_dim <= 0 || out_dim <= 0) {
2205 return;
2206 }
2207
2208 if (d_input) {
2209 for (int t = 0; t < tokens; ++t) {
2210 for (int i = 0; i < in_dim; ++i) {
2211 float sum = 0.0f;
2212 for (int o = 0; o < out_dim; ++o) {
2213 const float dy = bf16_to_float(d_output[(size_t)t * (size_t)out_dim + (size_t)o]);
2214 const float w = bf16_to_float(weight[(size_t)o * (size_t)in_dim + (size_t)i]);
2215 sum += dy * w;
2216 }
2217 d_input[(size_t)t * (size_t)in_dim + (size_t)i] = sum;
2218 }
2219 }
2220 }
2221
2222 if (d_weight) {
2223 for (int o = 0; o < out_dim; ++o) {
2224 for (int i = 0; i < in_dim; ++i) {
2225 float sum = 0.0f;
2226 for (int t = 0; t < tokens; ++t) {
2227 const float dy = bf16_to_float(d_output[(size_t)t * (size_t)out_dim + (size_t)o]);
2228 const float x = bf16_to_float(input[(size_t)t * (size_t)in_dim + (size_t)i]);
2229 sum += dy * x;
2230 }
2231 d_weight[(size_t)o * (size_t)in_dim + (size_t)i] = sum;
2232 }
2233 }
2234 }
2235
2236 if (d_bias) {
2237 for (int o = 0; o < out_dim; ++o) {
2238 float sum = 0.0f;
2239 for (int t = 0; t < tokens; ++t) {
2240 sum += bf16_to_float(d_output[(size_t)t * (size_t)out_dim + (size_t)o]);
2241 }
2242 d_bias[o] = sum;
2243 }
2244 }
2245}
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
Persistent pthread thread pool for CK-Engine inference.
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)
void ck_threadpool_dispatch_n(ck_threadpool_t *pool, int active_threads, ck_work_fn_t fn, void *args)
ck_threadpool_t * ck_threadpool_global(void)
int ck_threadpool_n_threads(const ck_threadpool_t *pool)
#define BLK_M
void gemm_nt_bf16_pytorch_onednn_3_12_brgemm_bf16_storage(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static void gemm_nt_bf16_pytorch_onednn_brgemm_bf16_storage_workspace_impl(const float *A, const void *B, const float *bias, float *C, int M, int N, int K, uint16_t *input_bf16, size_t input_bf16_bytes, uint16_t *output_bf16, size_t output_bf16_bytes, uint16_t *bias_bf16, size_t bias_bf16_bytes)
void gemm_tn_bf16(const uint16_t *A, const uint16_t *B, const uint16_t *bias, uint16_t *C, int M, int N, int K)
void gemm_nt_bf16_accurate_storage_row_range(const float *A, const void *B, const float *bias, float *C, int M, int N, int K, int row_begin, int row_end)
void gemm_nt_bf16_pytorch_onednn_3_12_brgemm_bf16_storage_workspace(const float *A, const void *B, const float *bias, float *C, int M, int N, int K, uint16_t *input_bf16, size_t input_bf16_bytes, uint16_t *output_bf16, size_t output_bf16_bytes, uint16_t *bias_bf16, size_t bias_bf16_bytes)
void gemm_nt_bf16_pytorch_onednn_brgemm_bf16_storage(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static void ck_gemv_bf16_rows(int begin, int end, void *opaque)
void gemv_bf16_bf16_storage(float *y, const void *W, const float *x, int M, int K)
int ck_gemm_bf16_amx_available(void)
#define BLK_N
void gemm_nt_bf16_bf16_storage_parallel_dispatch(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static void ck_gemm_nt_bf16_accurate_storage_rows(int begin, int end, void *opaque)
void gemm_nt_bf16_native_bf16_storage(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
void gemm_nt_bf16_accurate_storage(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
void gemm_nt_bf16_bf16_storage_row_range(const float *A, const void *B, const float *bias, float *C, int M, int N, int K, int row_begin, int row_end)
void gemm_nt_bf16_amx_bf16_storage(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
void patch_projection_bf16_pytorch_onednn_conv3d_storage(const float *input, const void *weights, const float *bias, float *output, int batch, int out_channels, int in_channels, int temporal, int patch_h, int patch_w)
void gemm_bf16_fp32out(const uint16_t *A, const uint16_t *B, const float *bias, float *C, int M, int N, int K)
void gemv_bf16_bf16_storage_parallel_dispatch(float *y, const void *W, const float *x, int M, int K)
void gemm_blocked_serial_bf16(const uint16_t *A, const uint16_t *B, const uint16_t *bias, uint16_t *C, int M, int N, int K)
void patch_projection_image_bf16_native_storage(const float *image, const void *weights_t0, const void *weights_t1, const float *bias, float *output, int channels, int image_h, int image_w, int patch_size, int out_channels, int merge_size)
int ck_gemm_bf16_fp32out_amx_raw(const uint16_t *A, const uint16_t *B, float *C, int M, int N, int K, int accumulate)
void gemm_nt_bf16(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
void gemm_nt_bf16_parallel_dispatch(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static void ck_bf16_convert_work(int ith, int nth, void *opaque)
static int ck_min_i(int a, int b)
#define BLK_K
void gemv_bf16(float *y, const void *W, const float *x, int M, int K)
void gemm_nn_bf16(const uint16_t *A, const uint16_t *B, const uint16_t *bias, uint16_t *C, int M, int N, int K)
static void ck_gemm_bf16_native_work(int ith, int nth, void *opaque)
static void ck_gemm_nt_bf16_exact_rows(int begin, int end, void *opaque)
static void ck_bf16_round_work(int ith, int nth, void *opaque)
void gemm_nt_bf16_accurate_storage_parallel_dispatch(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static void gemm_nt_bf16_pytorch_onednn_brgemm_bf16_storage_impl(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
void gemm_backward_bf16_mixed(const uint16_t *d_output, const uint16_t *input, const uint16_t *weight, float *d_input, float *d_weight, float *d_bias, int tokens, int in_dim, int out_dim)
static void gemv_bf16_row_range(float *y, const uint16_t *w, const float *x, int M, int K, int row_begin, int row_end)
void gemv_bf16_parallel_dispatch(float *y, const void *W, const float *x, int M, int K)
static void ck_gemm_nt_bf16_storage_exact_rows(int begin, int end, void *opaque)
static void ck_pytorch_brgemm_fault(const char *message, int M, int N, int K)
static void ck_patch_projection_bf16_native_work(int ith, int nth, void *opaque)
static void ck_gemm_bf16_amx_work(int ith, int nth, void *opaque)
void patch_projection_image_bf16_pytorch_onednn_conv3d_storage(const float *image, const void *weights_t0, const void *weights_t1, const float *bias, float *output, int channels, int image_h, int image_w, int patch_size, int out_channels, int merge_size)
void gemm_nt_bf16_bf16_storage(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
void gemm_nt_bf16_prefill_shape_safe_bf16_storage(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static void gemv_bf16_bf16_storage_row_range(float *y, const uint16_t *w, const float *x, int M, int K, int row_begin, int row_end)
void gemm_nt_bf16_prefill_shape_safe_bf16_storage_workspace(const float *A, const void *B, const float *bias, float *C, int M, int N, int K, uint16_t *a_bf16, size_t a_bf16_bytes)
void gemm_nt_bf16_row_range(const float *A, const void *B, const float *bias, float *C, int M, int N, int K, int row_begin, int row_end)
void gemm_nt_bf16_amx_bf16_storage_workspace(const float *A, const void *B, const float *bias, float *C, int M, int N, int K, uint16_t *a_bf16, size_t a_bf16_bytes)
static void ck_pytorch_brgemm_workspace_sizes(int M, int N, int K, size_t *input_bytes, size_t *output_bytes, size_t *bias_bytes)
static void ck_gemv_bf16_storage_rows(int begin, int end, void *opaque)
#define C(color)
Definition show_config.c:39
__attribute__((visibility("default"))) CKTokenizer *ck_tokenizer_create(CKTokenizerType type)
const uint8_t size_t count
Definition true_bpe.h:209
uint32_t end
Definition utf8.c:215