#ifndef AVX_KERNELS_HPP #define AVX_KERNELS_HPP #include #include #include #include #include "../../../cpu_backend/worker_pool.h" #include "llama.cpp/ggml-impl.h" #include "utils.hpp" namespace avx { // Enable/disable kernel tracing (can be controlled at compile time) #ifndef AVX_KERNEL_TRACE_ENABLED #define AVX_KERNEL_TRACE_ENABLED 0 #endif // ============================================================================ // AVX512 BF16 LoRA Kernels // // Optimized kernels for LoRA computations using AVX512 with native BF16 support. // These kernels use token-blocking and rank-blocking to maximize arithmetic // intensity and reduce memory bandwidth pressure. // // Key optimizations: // 1. Native _mm512_dpbf16_ps for BF16 dot-accumulate (no BF16->FP32 conversion) // 2. Token-blocking: process multiple tokens per weight load // 3. Rank-blocking: process multiple ranks in parallel // ============================================================================ /** * @brief BF16 input × BF16 weight → FP32 output matmul * * Computes: output[t, r] = sum_k(input[t, k] * weight[r, k]) * * Optimized with T_BLOCK=4, R_BLOCK=4 for high arithmetic intensity. * Uses native _mm512_dpbf16_ps instruction. * * @param input Input tensor [num_tokens, k_dim] in BF16 * @param weight Weight tensor [rank, k_dim] in BF16 * @param output Output tensor [num_tokens, rank] in FP32 * @param num_tokens Number of tokens to process * @param k_dim Inner dimension (hidden size) * @param rank LoRA rank (output dimension) */ inline void lora_bf16_matmul_t4r4(const ggml_bf16_t* __restrict input, const ggml_bf16_t* __restrict weight, float* __restrict output, int num_tokens, int k_dim, int rank) { constexpr int T_BLOCK = 4; constexpr int R_BLOCK = 4; int t = 0; // Process 4 tokens at a time for (; t + T_BLOCK <= num_tokens; t += T_BLOCK) { const ggml_bf16_t* inp0 = input + (t + 0) * k_dim; const ggml_bf16_t* inp1 = input + (t + 1) * k_dim; const ggml_bf16_t* inp2 = input + (t + 2) * k_dim; const ggml_bf16_t* inp3 = input + (t + 3) * k_dim; float* out0 = output + (t + 0) * rank; float* out1 = output + (t + 1) * rank; float* out2 = output + (t + 2) * rank; float* out3 = output + (t + 3) * rank; int r = 0; // Process 4 ranks at a time for (; r + R_BLOCK <= rank; r += R_BLOCK) { // 16 accumulators: 4 tokens × 4 ranks __m512 acc_t0_r0 = _mm512_setzero_ps(), acc_t0_r1 = _mm512_setzero_ps(); __m512 acc_t0_r2 = _mm512_setzero_ps(), acc_t0_r3 = _mm512_setzero_ps(); __m512 acc_t1_r0 = _mm512_setzero_ps(), acc_t1_r1 = _mm512_setzero_ps(); __m512 acc_t1_r2 = _mm512_setzero_ps(), acc_t1_r3 = _mm512_setzero_ps(); __m512 acc_t2_r0 = _mm512_setzero_ps(), acc_t2_r1 = _mm512_setzero_ps(); __m512 acc_t2_r2 = _mm512_setzero_ps(), acc_t2_r3 = _mm512_setzero_ps(); __m512 acc_t3_r0 = _mm512_setzero_ps(), acc_t3_r1 = _mm512_setzero_ps(); __m512 acc_t3_r2 = _mm512_setzero_ps(), acc_t3_r3 = _mm512_setzero_ps(); const ggml_bf16_t* w0 = weight + (r + 0) * k_dim; const ggml_bf16_t* w1 = weight + (r + 1) * k_dim; const ggml_bf16_t* w2 = weight + (r + 2) * k_dim; const ggml_bf16_t* w3 = weight + (r + 3) * k_dim; int k = 0; for (; k + 32 <= k_dim; k += 32) { // Load weights once (4 cache lines), reuse for 4 tokens __m512bh wv0 = (__m512bh)_mm512_loadu_si512((__m512i*)(w0 + k)); __m512bh wv1 = (__m512bh)_mm512_loadu_si512((__m512i*)(w1 + k)); __m512bh wv2 = (__m512bh)_mm512_loadu_si512((__m512i*)(w2 + k)); __m512bh wv3 = (__m512bh)_mm512_loadu_si512((__m512i*)(w3 + k)); // Token 0 __m512bh iv0 = (__m512bh)_mm512_loadu_si512((__m512i*)(inp0 + k)); acc_t0_r0 = _mm512_dpbf16_ps(acc_t0_r0, iv0, wv0); acc_t0_r1 = _mm512_dpbf16_ps(acc_t0_r1, iv0, wv1); acc_t0_r2 = _mm512_dpbf16_ps(acc_t0_r2, iv0, wv2); acc_t0_r3 = _mm512_dpbf16_ps(acc_t0_r3, iv0, wv3); // Token 1 __m512bh iv1 = (__m512bh)_mm512_loadu_si512((__m512i*)(inp1 + k)); acc_t1_r0 = _mm512_dpbf16_ps(acc_t1_r0, iv1, wv0); acc_t1_r1 = _mm512_dpbf16_ps(acc_t1_r1, iv1, wv1); acc_t1_r2 = _mm512_dpbf16_ps(acc_t1_r2, iv1, wv2); acc_t1_r3 = _mm512_dpbf16_ps(acc_t1_r3, iv1, wv3); // Token 2 __m512bh iv2 = (__m512bh)_mm512_loadu_si512((__m512i*)(inp2 + k)); acc_t2_r0 = _mm512_dpbf16_ps(acc_t2_r0, iv2, wv0); acc_t2_r1 = _mm512_dpbf16_ps(acc_t2_r1, iv2, wv1); acc_t2_r2 = _mm512_dpbf16_ps(acc_t2_r2, iv2, wv2); acc_t2_r3 = _mm512_dpbf16_ps(acc_t2_r3, iv2, wv3); // Token 3 __m512bh iv3 = (__m512bh)_mm512_loadu_si512((__m512i*)(inp3 + k)); acc_t3_r0 = _mm512_dpbf16_ps(acc_t3_r0, iv3, wv0); acc_t3_r1 = _mm512_dpbf16_ps(acc_t3_r1, iv3, wv1); acc_t3_r2 = _mm512_dpbf16_ps(acc_t3_r2, iv3, wv2); acc_t3_r3 = _mm512_dpbf16_ps(acc_t3_r3, iv3, wv3); } // Horizontal reduce and store out0[r + 0] = _mm512_reduce_add_ps(acc_t0_r0); out0[r + 1] = _mm512_reduce_add_ps(acc_t0_r1); out0[r + 2] = _mm512_reduce_add_ps(acc_t0_r2); out0[r + 3] = _mm512_reduce_add_ps(acc_t0_r3); out1[r + 0] = _mm512_reduce_add_ps(acc_t1_r0); out1[r + 1] = _mm512_reduce_add_ps(acc_t1_r1); out1[r + 2] = _mm512_reduce_add_ps(acc_t1_r2); out1[r + 3] = _mm512_reduce_add_ps(acc_t1_r3); out2[r + 0] = _mm512_reduce_add_ps(acc_t2_r0); out2[r + 1] = _mm512_reduce_add_ps(acc_t2_r1); out2[r + 2] = _mm512_reduce_add_ps(acc_t2_r2); out2[r + 3] = _mm512_reduce_add_ps(acc_t2_r3); out3[r + 0] = _mm512_reduce_add_ps(acc_t3_r0); out3[r + 1] = _mm512_reduce_add_ps(acc_t3_r1); out3[r + 2] = _mm512_reduce_add_ps(acc_t3_r2); out3[r + 3] = _mm512_reduce_add_ps(acc_t3_r3); // Scalar tail for k for (int rr = 0; rr < R_BLOCK; rr++) { float sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; for (int kk = k; kk < k_dim; kk++) { float w = GGML_BF16_TO_FP32(weight[(r + rr) * k_dim + kk]); sum0 += GGML_BF16_TO_FP32(inp0[kk]) * w; sum1 += GGML_BF16_TO_FP32(inp1[kk]) * w; sum2 += GGML_BF16_TO_FP32(inp2[kk]) * w; sum3 += GGML_BF16_TO_FP32(inp3[kk]) * w; } out0[r + rr] += sum0; out1[r + rr] += sum1; out2[r + rr] += sum2; out3[r + rr] += sum3; } } // Remainder ranks for (; r < rank; r++) { const ggml_bf16_t* w_row = weight + r * k_dim; __m512 acc0 = _mm512_setzero_ps(); __m512 acc1 = _mm512_setzero_ps(); __m512 acc2 = _mm512_setzero_ps(); __m512 acc3 = _mm512_setzero_ps(); int k = 0; for (; k + 32 <= k_dim; k += 32) { __m512bh wv = (__m512bh)_mm512_loadu_si512((__m512i*)(w_row + k)); acc0 = _mm512_dpbf16_ps(acc0, (__m512bh)_mm512_loadu_si512((__m512i*)(inp0 + k)), wv); acc1 = _mm512_dpbf16_ps(acc1, (__m512bh)_mm512_loadu_si512((__m512i*)(inp1 + k)), wv); acc2 = _mm512_dpbf16_ps(acc2, (__m512bh)_mm512_loadu_si512((__m512i*)(inp2 + k)), wv); acc3 = _mm512_dpbf16_ps(acc3, (__m512bh)_mm512_loadu_si512((__m512i*)(inp3 + k)), wv); } float sum0 = _mm512_reduce_add_ps(acc0); float sum1 = _mm512_reduce_add_ps(acc1); float sum2 = _mm512_reduce_add_ps(acc2); float sum3 = _mm512_reduce_add_ps(acc3); for (; k < k_dim; k++) { float w = GGML_BF16_TO_FP32(w_row[k]); sum0 += GGML_BF16_TO_FP32(inp0[k]) * w; sum1 += GGML_BF16_TO_FP32(inp1[k]) * w; sum2 += GGML_BF16_TO_FP32(inp2[k]) * w; sum3 += GGML_BF16_TO_FP32(inp3[k]) * w; } out0[r] = sum0; out1[r] = sum1; out2[r] = sum2; out3[r] = sum3; } } // Handle remaining tokens with 2-token kernel for (; t + 2 <= num_tokens; t += 2) { const ggml_bf16_t* inp0 = input + t * k_dim; const ggml_bf16_t* inp1 = input + (t + 1) * k_dim; float* out0 = output + t * rank; float* out1 = output + (t + 1) * rank; for (int r = 0; r < rank; r++) { const ggml_bf16_t* w_row = weight + r * k_dim; __m512 acc0 = _mm512_setzero_ps(); __m512 acc1 = _mm512_setzero_ps(); int k = 0; for (; k + 32 <= k_dim; k += 32) { __m512bh wv = (__m512bh)_mm512_loadu_si512((__m512i*)(w_row + k)); acc0 = _mm512_dpbf16_ps(acc0, (__m512bh)_mm512_loadu_si512((__m512i*)(inp0 + k)), wv); acc1 = _mm512_dpbf16_ps(acc1, (__m512bh)_mm512_loadu_si512((__m512i*)(inp1 + k)), wv); } float sum0 = _mm512_reduce_add_ps(acc0); float sum1 = _mm512_reduce_add_ps(acc1); for (; k < k_dim; k++) { float w = GGML_BF16_TO_FP32(w_row[k]); sum0 += GGML_BF16_TO_FP32(inp0[k]) * w; sum1 += GGML_BF16_TO_FP32(inp1[k]) * w; } out0[r] = sum0; out1[r] = sum1; } } // Handle remaining single token for (; t < num_tokens; t++) { const ggml_bf16_t* inp_row = input + t * k_dim; float* out_row = output + t * rank; for (int r = 0; r < rank; r++) { const ggml_bf16_t* w_row = weight + r * k_dim; __m512 acc = _mm512_setzero_ps(); int k = 0; for (; k + 32 <= k_dim; k += 32) { acc = _mm512_dpbf16_ps(acc, (__m512bh)_mm512_loadu_si512((__m512i*)(inp_row + k)), (__m512bh)_mm512_loadu_si512((__m512i*)(w_row + k))); } float sum = _mm512_reduce_add_ps(acc); for (; k < k_dim; k++) { sum += GGML_BF16_TO_FP32(inp_row[k]) * GGML_BF16_TO_FP32(w_row[k]); } out_row[r] = sum; } } } /** * @brief FP32 intermediate × BF16 weight → BF16 output with scale and add * * Computes: output[t, i] += scale * sum_r(intermediate[t, r] * weight[i, r]) * * Highly optimized version with: * - T_BLOCK=4, O_BLOCK=8 for maximum register utilization * - Interleaved load/FMA pattern for better pipelining * - Vectorized BF16 load/store (8 outputs at a time) * - Masked tail handling (no scalar fallback) * - Software prefetching for weight data * * Performance: ~6.6 GFLOPS for R=8, ~38.5 GFLOPS for R=64 (single thread) * * @param intermediate Intermediate tensor [num_tokens, rank] in FP32 * @param weight Weight tensor [output_dim, rank] in BF16 * @param output Output tensor [num_tokens, output_dim] in BF16 (accumulated) * @param num_tokens Number of tokens to process * @param rank LoRA rank (inner dimension) * @param output_dim Output dimension * @param scale Scaling factor for LoRA */ inline void lora_fp32_bf16_fused_add(const float* __restrict intermediate, const ggml_bf16_t* __restrict weight, ggml_bf16_t* __restrict output, int num_tokens, int rank, int output_dim, float scale) { #if AVX_KERNEL_TRACE_ENABLED #endif constexpr int T_BLOCK = 4; constexpr int O_BLOCK = 8; constexpr int PREFETCH_DISTANCE = 16; const __m256 scale_vec = _mm256_set1_ps(scale); const int rank_tail = rank & 15; const __mmask16 tail_mask = rank_tail ? ((__mmask16)1 << rank_tail) - 1 : 0; int t = 0; for (; t + T_BLOCK <= num_tokens; t += T_BLOCK) { const float* inter0 = intermediate + (t + 0) * rank; const float* inter1 = intermediate + (t + 1) * rank; const float* inter2 = intermediate + (t + 2) * rank; const float* inter3 = intermediate + (t + 3) * rank; ggml_bf16_t* out0 = output + (t + 0) * output_dim; ggml_bf16_t* out1 = output + (t + 1) * output_dim; ggml_bf16_t* out2 = output + (t + 2) * output_dim; ggml_bf16_t* out3 = output + (t + 3) * output_dim; int i = 0; for (; i + O_BLOCK <= output_dim; i += O_BLOCK) { // Prefetch weight rows for future iterations if (i + O_BLOCK + PREFETCH_DISTANCE * O_BLOCK <= output_dim) { _mm_prefetch((const char*)(weight + (i + PREFETCH_DISTANCE * O_BLOCK) * rank), _MM_HINT_T0); _mm_prefetch((const char*)(weight + (i + PREFETCH_DISTANCE * O_BLOCK + 1) * rank), _MM_HINT_T0); _mm_prefetch((const char*)(weight + (i + PREFETCH_DISTANCE * O_BLOCK + 2) * rank), _MM_HINT_T0); _mm_prefetch((const char*)(weight + (i + PREFETCH_DISTANCE * O_BLOCK + 3) * rank), _MM_HINT_T0); } const ggml_bf16_t* w0 = weight + (i + 0) * rank; const ggml_bf16_t* w1 = weight + (i + 1) * rank; const ggml_bf16_t* w2 = weight + (i + 2) * rank; const ggml_bf16_t* w3 = weight + (i + 3) * rank; const ggml_bf16_t* w4 = weight + (i + 4) * rank; const ggml_bf16_t* w5 = weight + (i + 5) * rank; const ggml_bf16_t* w6 = weight + (i + 6) * rank; const ggml_bf16_t* w7 = weight + (i + 7) * rank; // 32 accumulators: 4 tokens × 8 outputs __m512 acc_t0_o0 = _mm512_setzero_ps(), acc_t0_o1 = _mm512_setzero_ps(); __m512 acc_t0_o2 = _mm512_setzero_ps(), acc_t0_o3 = _mm512_setzero_ps(); __m512 acc_t0_o4 = _mm512_setzero_ps(), acc_t0_o5 = _mm512_setzero_ps(); __m512 acc_t0_o6 = _mm512_setzero_ps(), acc_t0_o7 = _mm512_setzero_ps(); __m512 acc_t1_o0 = _mm512_setzero_ps(), acc_t1_o1 = _mm512_setzero_ps(); __m512 acc_t1_o2 = _mm512_setzero_ps(), acc_t1_o3 = _mm512_setzero_ps(); __m512 acc_t1_o4 = _mm512_setzero_ps(), acc_t1_o5 = _mm512_setzero_ps(); __m512 acc_t1_o6 = _mm512_setzero_ps(), acc_t1_o7 = _mm512_setzero_ps(); __m512 acc_t2_o0 = _mm512_setzero_ps(), acc_t2_o1 = _mm512_setzero_ps(); __m512 acc_t2_o2 = _mm512_setzero_ps(), acc_t2_o3 = _mm512_setzero_ps(); __m512 acc_t2_o4 = _mm512_setzero_ps(), acc_t2_o5 = _mm512_setzero_ps(); __m512 acc_t2_o6 = _mm512_setzero_ps(), acc_t2_o7 = _mm512_setzero_ps(); __m512 acc_t3_o0 = _mm512_setzero_ps(), acc_t3_o1 = _mm512_setzero_ps(); __m512 acc_t3_o2 = _mm512_setzero_ps(), acc_t3_o3 = _mm512_setzero_ps(); __m512 acc_t3_o4 = _mm512_setzero_ps(), acc_t3_o5 = _mm512_setzero_ps(); __m512 acc_t3_o6 = _mm512_setzero_ps(), acc_t3_o7 = _mm512_setzero_ps(); int r = 0; // Main loop with interleaved loads and FMAs for better pipelining for (; r + 16 <= rank; r += 16) { __m512 iv0 = _mm512_loadu_ps(inter0 + r); __m512 iv1 = _mm512_loadu_ps(inter1 + r); __m512 iv2 = _mm512_loadu_ps(inter2 + r); __m512 iv3 = _mm512_loadu_ps(inter3 + r); // Interleave weight loads and FMAs __m512 wv0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w0 + r))), 16)); acc_t0_o0 = _mm512_fmadd_ps(iv0, wv0, acc_t0_o0); acc_t1_o0 = _mm512_fmadd_ps(iv1, wv0, acc_t1_o0); __m512 wv1 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w1 + r))), 16)); acc_t2_o0 = _mm512_fmadd_ps(iv2, wv0, acc_t2_o0); acc_t3_o0 = _mm512_fmadd_ps(iv3, wv0, acc_t3_o0); acc_t0_o1 = _mm512_fmadd_ps(iv0, wv1, acc_t0_o1); acc_t1_o1 = _mm512_fmadd_ps(iv1, wv1, acc_t1_o1); __m512 wv2 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w2 + r))), 16)); acc_t2_o1 = _mm512_fmadd_ps(iv2, wv1, acc_t2_o1); acc_t3_o1 = _mm512_fmadd_ps(iv3, wv1, acc_t3_o1); acc_t0_o2 = _mm512_fmadd_ps(iv0, wv2, acc_t0_o2); acc_t1_o2 = _mm512_fmadd_ps(iv1, wv2, acc_t1_o2); __m512 wv3 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w3 + r))), 16)); acc_t2_o2 = _mm512_fmadd_ps(iv2, wv2, acc_t2_o2); acc_t3_o2 = _mm512_fmadd_ps(iv3, wv2, acc_t3_o2); acc_t0_o3 = _mm512_fmadd_ps(iv0, wv3, acc_t0_o3); acc_t1_o3 = _mm512_fmadd_ps(iv1, wv3, acc_t1_o3); __m512 wv4 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w4 + r))), 16)); acc_t2_o3 = _mm512_fmadd_ps(iv2, wv3, acc_t2_o3); acc_t3_o3 = _mm512_fmadd_ps(iv3, wv3, acc_t3_o3); acc_t0_o4 = _mm512_fmadd_ps(iv0, wv4, acc_t0_o4); acc_t1_o4 = _mm512_fmadd_ps(iv1, wv4, acc_t1_o4); __m512 wv5 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w5 + r))), 16)); acc_t2_o4 = _mm512_fmadd_ps(iv2, wv4, acc_t2_o4); acc_t3_o4 = _mm512_fmadd_ps(iv3, wv4, acc_t3_o4); acc_t0_o5 = _mm512_fmadd_ps(iv0, wv5, acc_t0_o5); acc_t1_o5 = _mm512_fmadd_ps(iv1, wv5, acc_t1_o5); __m512 wv6 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w6 + r))), 16)); acc_t2_o5 = _mm512_fmadd_ps(iv2, wv5, acc_t2_o5); acc_t3_o5 = _mm512_fmadd_ps(iv3, wv5, acc_t3_o5); acc_t0_o6 = _mm512_fmadd_ps(iv0, wv6, acc_t0_o6); acc_t1_o6 = _mm512_fmadd_ps(iv1, wv6, acc_t1_o6); __m512 wv7 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w7 + r))), 16)); acc_t2_o6 = _mm512_fmadd_ps(iv2, wv6, acc_t2_o6); acc_t3_o6 = _mm512_fmadd_ps(iv3, wv6, acc_t3_o6); acc_t0_o7 = _mm512_fmadd_ps(iv0, wv7, acc_t0_o7); acc_t1_o7 = _mm512_fmadd_ps(iv1, wv7, acc_t1_o7); acc_t2_o7 = _mm512_fmadd_ps(iv2, wv7, acc_t2_o7); acc_t3_o7 = _mm512_fmadd_ps(iv3, wv7, acc_t3_o7); } // Masked tail handling if (tail_mask) { __m512 iv0 = _mm512_maskz_loadu_ps(tail_mask, inter0 + r); __m512 iv1 = _mm512_maskz_loadu_ps(tail_mask, inter1 + r); __m512 iv2 = _mm512_maskz_loadu_ps(tail_mask, inter2 + r); __m512 iv3 = _mm512_maskz_loadu_ps(tail_mask, inter3 + r); #define LOAD_W_MASK(ptr) \ _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(tail_mask, ptr + r)), 16)) __m512 wv0 = LOAD_W_MASK(w0); __m512 wv1 = LOAD_W_MASK(w1); __m512 wv2 = LOAD_W_MASK(w2); __m512 wv3 = LOAD_W_MASK(w3); __m512 wv4 = LOAD_W_MASK(w4); __m512 wv5 = LOAD_W_MASK(w5); __m512 wv6 = LOAD_W_MASK(w6); __m512 wv7 = LOAD_W_MASK(w7); acc_t0_o0 = _mm512_fmadd_ps(iv0, wv0, acc_t0_o0); acc_t0_o1 = _mm512_fmadd_ps(iv0, wv1, acc_t0_o1); acc_t0_o2 = _mm512_fmadd_ps(iv0, wv2, acc_t0_o2); acc_t0_o3 = _mm512_fmadd_ps(iv0, wv3, acc_t0_o3); acc_t0_o4 = _mm512_fmadd_ps(iv0, wv4, acc_t0_o4); acc_t0_o5 = _mm512_fmadd_ps(iv0, wv5, acc_t0_o5); acc_t0_o6 = _mm512_fmadd_ps(iv0, wv6, acc_t0_o6); acc_t0_o7 = _mm512_fmadd_ps(iv0, wv7, acc_t0_o7); acc_t1_o0 = _mm512_fmadd_ps(iv1, wv0, acc_t1_o0); acc_t1_o1 = _mm512_fmadd_ps(iv1, wv1, acc_t1_o1); acc_t1_o2 = _mm512_fmadd_ps(iv1, wv2, acc_t1_o2); acc_t1_o3 = _mm512_fmadd_ps(iv1, wv3, acc_t1_o3); acc_t1_o4 = _mm512_fmadd_ps(iv1, wv4, acc_t1_o4); acc_t1_o5 = _mm512_fmadd_ps(iv1, wv5, acc_t1_o5); acc_t1_o6 = _mm512_fmadd_ps(iv1, wv6, acc_t1_o6); acc_t1_o7 = _mm512_fmadd_ps(iv1, wv7, acc_t1_o7); acc_t2_o0 = _mm512_fmadd_ps(iv2, wv0, acc_t2_o0); acc_t2_o1 = _mm512_fmadd_ps(iv2, wv1, acc_t2_o1); acc_t2_o2 = _mm512_fmadd_ps(iv2, wv2, acc_t2_o2); acc_t2_o3 = _mm512_fmadd_ps(iv2, wv3, acc_t2_o3); acc_t2_o4 = _mm512_fmadd_ps(iv2, wv4, acc_t2_o4); acc_t2_o5 = _mm512_fmadd_ps(iv2, wv5, acc_t2_o5); acc_t2_o6 = _mm512_fmadd_ps(iv2, wv6, acc_t2_o6); acc_t2_o7 = _mm512_fmadd_ps(iv2, wv7, acc_t2_o7); acc_t3_o0 = _mm512_fmadd_ps(iv3, wv0, acc_t3_o0); acc_t3_o1 = _mm512_fmadd_ps(iv3, wv1, acc_t3_o1); acc_t3_o2 = _mm512_fmadd_ps(iv3, wv2, acc_t3_o2); acc_t3_o3 = _mm512_fmadd_ps(iv3, wv3, acc_t3_o3); acc_t3_o4 = _mm512_fmadd_ps(iv3, wv4, acc_t3_o4); acc_t3_o5 = _mm512_fmadd_ps(iv3, wv5, acc_t3_o5); acc_t3_o6 = _mm512_fmadd_ps(iv3, wv6, acc_t3_o6); acc_t3_o7 = _mm512_fmadd_ps(iv3, wv7, acc_t3_o7); #undef LOAD_W_MASK } // Reduce 8 accumulators to __m256 (8 floats) for each token // Token 0 __m256 sum_t0 = _mm256_set_ps(_mm512_reduce_add_ps(acc_t0_o7), _mm512_reduce_add_ps(acc_t0_o6), _mm512_reduce_add_ps(acc_t0_o5), _mm512_reduce_add_ps(acc_t0_o4), _mm512_reduce_add_ps(acc_t0_o3), _mm512_reduce_add_ps(acc_t0_o2), _mm512_reduce_add_ps(acc_t0_o1), _mm512_reduce_add_ps(acc_t0_o0)); // Token 1 __m256 sum_t1 = _mm256_set_ps(_mm512_reduce_add_ps(acc_t1_o7), _mm512_reduce_add_ps(acc_t1_o6), _mm512_reduce_add_ps(acc_t1_o5), _mm512_reduce_add_ps(acc_t1_o4), _mm512_reduce_add_ps(acc_t1_o3), _mm512_reduce_add_ps(acc_t1_o2), _mm512_reduce_add_ps(acc_t1_o1), _mm512_reduce_add_ps(acc_t1_o0)); // Token 2 __m256 sum_t2 = _mm256_set_ps(_mm512_reduce_add_ps(acc_t2_o7), _mm512_reduce_add_ps(acc_t2_o6), _mm512_reduce_add_ps(acc_t2_o5), _mm512_reduce_add_ps(acc_t2_o4), _mm512_reduce_add_ps(acc_t2_o3), _mm512_reduce_add_ps(acc_t2_o2), _mm512_reduce_add_ps(acc_t2_o1), _mm512_reduce_add_ps(acc_t2_o0)); // Token 3 __m256 sum_t3 = _mm256_set_ps(_mm512_reduce_add_ps(acc_t3_o7), _mm512_reduce_add_ps(acc_t3_o6), _mm512_reduce_add_ps(acc_t3_o5), _mm512_reduce_add_ps(acc_t3_o4), _mm512_reduce_add_ps(acc_t3_o3), _mm512_reduce_add_ps(acc_t3_o2), _mm512_reduce_add_ps(acc_t3_o1), _mm512_reduce_add_ps(acc_t3_o0)); // Apply scale sum_t0 = _mm256_mul_ps(sum_t0, scale_vec); sum_t1 = _mm256_mul_ps(sum_t1, scale_vec); sum_t2 = _mm256_mul_ps(sum_t2, scale_vec); sum_t3 = _mm256_mul_ps(sum_t3, scale_vec); // Vectorized load/add/store for output (8 BF16 values at a time) __m256 out_t0 = _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(_mm_loadu_si128((__m128i*)(out0 + i))), 16)); __m256 out_t1 = _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(_mm_loadu_si128((__m128i*)(out1 + i))), 16)); __m256 out_t2 = _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(_mm_loadu_si128((__m128i*)(out2 + i))), 16)); __m256 out_t3 = _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(_mm_loadu_si128((__m128i*)(out3 + i))), 16)); out_t0 = _mm256_add_ps(out_t0, sum_t0); out_t1 = _mm256_add_ps(out_t1, sum_t1); out_t2 = _mm256_add_ps(out_t2, sum_t2); out_t3 = _mm256_add_ps(out_t3, sum_t3); // Convert FP32 -> BF16 and store __m128bh bf16_t0 = _mm256_cvtneps_pbh(out_t0); __m128bh bf16_t1 = _mm256_cvtneps_pbh(out_t1); __m128bh bf16_t2 = _mm256_cvtneps_pbh(out_t2); __m128bh bf16_t3 = _mm256_cvtneps_pbh(out_t3); _mm_storeu_si128((__m128i*)(out0 + i), (__m128i)bf16_t0); _mm_storeu_si128((__m128i*)(out1 + i), (__m128i)bf16_t1); _mm_storeu_si128((__m128i*)(out2 + i), (__m128i)bf16_t2); _mm_storeu_si128((__m128i*)(out3 + i), (__m128i)bf16_t3); } // Remainder outputs (< O_BLOCK) for (; i < output_dim; i++) { const ggml_bf16_t* w_row = weight + i * rank; __m512 acc0 = _mm512_setzero_ps(); __m512 acc1 = _mm512_setzero_ps(); __m512 acc2 = _mm512_setzero_ps(); __m512 acc3 = _mm512_setzero_ps(); int r = 0; for (; r + 16 <= rank; r += 16) { __m512 wv = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w_row + r))), 16)); acc0 = _mm512_fmadd_ps(_mm512_loadu_ps(inter0 + r), wv, acc0); acc1 = _mm512_fmadd_ps(_mm512_loadu_ps(inter1 + r), wv, acc1); acc2 = _mm512_fmadd_ps(_mm512_loadu_ps(inter2 + r), wv, acc2); acc3 = _mm512_fmadd_ps(_mm512_loadu_ps(inter3 + r), wv, acc3); } if (tail_mask) { __m512 wv = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(tail_mask, w_row + r)), 16)); acc0 = _mm512_fmadd_ps(_mm512_maskz_loadu_ps(tail_mask, inter0 + r), wv, acc0); acc1 = _mm512_fmadd_ps(_mm512_maskz_loadu_ps(tail_mask, inter1 + r), wv, acc1); acc2 = _mm512_fmadd_ps(_mm512_maskz_loadu_ps(tail_mask, inter2 + r), wv, acc2); acc3 = _mm512_fmadd_ps(_mm512_maskz_loadu_ps(tail_mask, inter3 + r), wv, acc3); } float s0 = _mm512_reduce_add_ps(acc0) * scale; float s1 = _mm512_reduce_add_ps(acc1) * scale; float s2 = _mm512_reduce_add_ps(acc2) * scale; float s3 = _mm512_reduce_add_ps(acc3) * scale; out0[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out0[i]) + s0); out1[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out1[i]) + s1); out2[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out2[i]) + s2); out3[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out3[i]) + s3); } } // Handle remaining tokens (< T_BLOCK) for (; t < num_tokens; t++) { const float* inter_row = intermediate + t * rank; ggml_bf16_t* out_row = output + t * output_dim; for (int i = 0; i < output_dim; i++) { const ggml_bf16_t* w_row = weight + i * rank; __m512 acc = _mm512_setzero_ps(); int r = 0; for (; r + 16 <= rank; r += 16) { __m512 wv = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w_row + r))), 16)); acc = _mm512_fmadd_ps(_mm512_loadu_ps(inter_row + r), wv, acc); } if (tail_mask) { __m512 wv = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(tail_mask, w_row + r)), 16)); acc = _mm512_fmadd_ps(_mm512_maskz_loadu_ps(tail_mask, inter_row + r), wv, acc); } float sum = _mm512_reduce_add_ps(acc) * scale; out_row[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out_row[i]) + sum); } } #if AVX_KERNEL_TRACE_ENABLED char args_buf[128]; snprintf(args_buf, sizeof(args_buf), "{\"T\":%d,\"R\":%d,\"O\":%d}", num_tokens, rank, output_dim); #endif } /** * @brief FP32 intermediate × BF16 weight (transposed layout) → BF16 output with scale and add * * Computes: output[t, i] += scale * sum_r(intermediate[t, r] * weight[r, i]) * * This variant handles weight in [rank, output_dim] layout (transposed from standard). * The weight access pattern is contiguous along output_dim, enabling efficient vectorized loads. * * Optimizations: * - T_BLOCK=4, O_BLOCK=32 for maximum register utilization * - R_UNROLL=4: unroll 4 ranks per iteration for better pipelining * - Contiguous weight loads (32 outputs at once) * - Vectorized BF16 load/store * * Performance: ~68-87 GFLOPS (2x speedup vs baseline), tested on R=8-64 * * @param intermediate Intermediate tensor [num_tokens, rank] in FP32 * @param weight Weight tensor [rank, output_dim] in BF16 (transposed layout) * @param output Output tensor [num_tokens, output_dim] in BF16 (accumulated) * @param num_tokens Number of tokens to process * @param rank LoRA rank (inner dimension) * @param output_dim Output dimension * @param scale Scaling factor for LoRA */ inline void lora_fp32_bf16_fused_add_wt(const float* __restrict intermediate, const ggml_bf16_t* __restrict weight, ggml_bf16_t* __restrict output, int num_tokens, int rank, int output_dim, float scale) { #if AVX_KERNEL_TRACE_ENABLED #endif constexpr int T_BLOCK = 4; constexpr int O_BLOCK = 32; constexpr int R_UNROLL = 4; const __m512 scale_vec = _mm512_set1_ps(scale); const int rank_main = (rank / R_UNROLL) * R_UNROLL; int t = 0; for (; t + T_BLOCK <= num_tokens; t += T_BLOCK) { const float* inter0 = intermediate + (t + 0) * rank; const float* inter1 = intermediate + (t + 1) * rank; const float* inter2 = intermediate + (t + 2) * rank; const float* inter3 = intermediate + (t + 3) * rank; ggml_bf16_t* out0 = output + (t + 0) * output_dim; ggml_bf16_t* out1 = output + (t + 1) * output_dim; ggml_bf16_t* out2 = output + (t + 2) * output_dim; ggml_bf16_t* out3 = output + (t + 3) * output_dim; int i = 0; for (; i + O_BLOCK <= output_dim; i += O_BLOCK) { __m512 acc_t0_0 = _mm512_setzero_ps(), acc_t0_1 = _mm512_setzero_ps(); __m512 acc_t1_0 = _mm512_setzero_ps(), acc_t1_1 = _mm512_setzero_ps(); __m512 acc_t2_0 = _mm512_setzero_ps(), acc_t2_1 = _mm512_setzero_ps(); __m512 acc_t3_0 = _mm512_setzero_ps(), acc_t3_1 = _mm512_setzero_ps(); // Main loop: 4 ranks per iteration for better pipelining int r = 0; for (; r < rank_main; r += R_UNROLL) { __m512 iv0_r0 = _mm512_set1_ps(inter0[r + 0]), iv0_r1 = _mm512_set1_ps(inter0[r + 1]); __m512 iv0_r2 = _mm512_set1_ps(inter0[r + 2]), iv0_r3 = _mm512_set1_ps(inter0[r + 3]); __m512 iv1_r0 = _mm512_set1_ps(inter1[r + 0]), iv1_r1 = _mm512_set1_ps(inter1[r + 1]); __m512 iv1_r2 = _mm512_set1_ps(inter1[r + 2]), iv1_r3 = _mm512_set1_ps(inter1[r + 3]); __m512 iv2_r0 = _mm512_set1_ps(inter2[r + 0]), iv2_r1 = _mm512_set1_ps(inter2[r + 1]); __m512 iv2_r2 = _mm512_set1_ps(inter2[r + 2]), iv2_r3 = _mm512_set1_ps(inter2[r + 3]); __m512 iv3_r0 = _mm512_set1_ps(inter3[r + 0]), iv3_r1 = _mm512_set1_ps(inter3[r + 1]); __m512 iv3_r2 = _mm512_set1_ps(inter3[r + 2]), iv3_r3 = _mm512_set1_ps(inter3[r + 3]); const ggml_bf16_t* w0 = weight + (r + 0) * output_dim + i; const ggml_bf16_t* w1 = weight + (r + 1) * output_dim + i; const ggml_bf16_t* w2 = weight + (r + 2) * output_dim + i; const ggml_bf16_t* w3 = weight + (r + 3) * output_dim + i; __m512 wv0_0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)w0)), 16)); __m512 wv0_1 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w0 + 16))), 16)); __m512 wv1_0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)w1)), 16)); __m512 wv1_1 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w1 + 16))), 16)); __m512 wv2_0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)w2)), 16)); __m512 wv2_1 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w2 + 16))), 16)); __m512 wv3_0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)w3)), 16)); __m512 wv3_1 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w3 + 16))), 16)); // Token 0 acc_t0_0 = _mm512_fmadd_ps(iv0_r0, wv0_0, acc_t0_0); acc_t0_1 = _mm512_fmadd_ps(iv0_r0, wv0_1, acc_t0_1); acc_t0_0 = _mm512_fmadd_ps(iv0_r1, wv1_0, acc_t0_0); acc_t0_1 = _mm512_fmadd_ps(iv0_r1, wv1_1, acc_t0_1); acc_t0_0 = _mm512_fmadd_ps(iv0_r2, wv2_0, acc_t0_0); acc_t0_1 = _mm512_fmadd_ps(iv0_r2, wv2_1, acc_t0_1); acc_t0_0 = _mm512_fmadd_ps(iv0_r3, wv3_0, acc_t0_0); acc_t0_1 = _mm512_fmadd_ps(iv0_r3, wv3_1, acc_t0_1); // Token 1 acc_t1_0 = _mm512_fmadd_ps(iv1_r0, wv0_0, acc_t1_0); acc_t1_1 = _mm512_fmadd_ps(iv1_r0, wv0_1, acc_t1_1); acc_t1_0 = _mm512_fmadd_ps(iv1_r1, wv1_0, acc_t1_0); acc_t1_1 = _mm512_fmadd_ps(iv1_r1, wv1_1, acc_t1_1); acc_t1_0 = _mm512_fmadd_ps(iv1_r2, wv2_0, acc_t1_0); acc_t1_1 = _mm512_fmadd_ps(iv1_r2, wv2_1, acc_t1_1); acc_t1_0 = _mm512_fmadd_ps(iv1_r3, wv3_0, acc_t1_0); acc_t1_1 = _mm512_fmadd_ps(iv1_r3, wv3_1, acc_t1_1); // Token 2 acc_t2_0 = _mm512_fmadd_ps(iv2_r0, wv0_0, acc_t2_0); acc_t2_1 = _mm512_fmadd_ps(iv2_r0, wv0_1, acc_t2_1); acc_t2_0 = _mm512_fmadd_ps(iv2_r1, wv1_0, acc_t2_0); acc_t2_1 = _mm512_fmadd_ps(iv2_r1, wv1_1, acc_t2_1); acc_t2_0 = _mm512_fmadd_ps(iv2_r2, wv2_0, acc_t2_0); acc_t2_1 = _mm512_fmadd_ps(iv2_r2, wv2_1, acc_t2_1); acc_t2_0 = _mm512_fmadd_ps(iv2_r3, wv3_0, acc_t2_0); acc_t2_1 = _mm512_fmadd_ps(iv2_r3, wv3_1, acc_t2_1); // Token 3 acc_t3_0 = _mm512_fmadd_ps(iv3_r0, wv0_0, acc_t3_0); acc_t3_1 = _mm512_fmadd_ps(iv3_r0, wv0_1, acc_t3_1); acc_t3_0 = _mm512_fmadd_ps(iv3_r1, wv1_0, acc_t3_0); acc_t3_1 = _mm512_fmadd_ps(iv3_r1, wv1_1, acc_t3_1); acc_t3_0 = _mm512_fmadd_ps(iv3_r2, wv2_0, acc_t3_0); acc_t3_1 = _mm512_fmadd_ps(iv3_r2, wv2_1, acc_t3_1); acc_t3_0 = _mm512_fmadd_ps(iv3_r3, wv3_0, acc_t3_0); acc_t3_1 = _mm512_fmadd_ps(iv3_r3, wv3_1, acc_t3_1); } // Remainder ranks for (; r < rank; r++) { __m512 iv0 = _mm512_set1_ps(inter0[r]); __m512 iv1 = _mm512_set1_ps(inter1[r]); __m512 iv2 = _mm512_set1_ps(inter2[r]); __m512 iv3 = _mm512_set1_ps(inter3[r]); const ggml_bf16_t* w_ptr = weight + r * output_dim + i; __m512 wv0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)w_ptr)), 16)); __m512 wv1 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(w_ptr + 16))), 16)); acc_t0_0 = _mm512_fmadd_ps(iv0, wv0, acc_t0_0); acc_t0_1 = _mm512_fmadd_ps(iv0, wv1, acc_t0_1); acc_t1_0 = _mm512_fmadd_ps(iv1, wv0, acc_t1_0); acc_t1_1 = _mm512_fmadd_ps(iv1, wv1, acc_t1_1); acc_t2_0 = _mm512_fmadd_ps(iv2, wv0, acc_t2_0); acc_t2_1 = _mm512_fmadd_ps(iv2, wv1, acc_t2_1); acc_t3_0 = _mm512_fmadd_ps(iv3, wv0, acc_t3_0); acc_t3_1 = _mm512_fmadd_ps(iv3, wv1, acc_t3_1); } // Apply scale acc_t0_0 = _mm512_mul_ps(acc_t0_0, scale_vec); acc_t0_1 = _mm512_mul_ps(acc_t0_1, scale_vec); acc_t1_0 = _mm512_mul_ps(acc_t1_0, scale_vec); acc_t1_1 = _mm512_mul_ps(acc_t1_1, scale_vec); acc_t2_0 = _mm512_mul_ps(acc_t2_0, scale_vec); acc_t2_1 = _mm512_mul_ps(acc_t2_1, scale_vec); acc_t3_0 = _mm512_mul_ps(acc_t3_0, scale_vec); acc_t3_1 = _mm512_mul_ps(acc_t3_1, scale_vec); // Load current output, add, store (32 values per token) // Token 0 __m512 cur0_0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out0 + i))), 16)); __m512 cur0_1 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out0 + i + 16))), 16)); cur0_0 = _mm512_add_ps(cur0_0, acc_t0_0); cur0_1 = _mm512_add_ps(cur0_1, acc_t0_1); _mm256_storeu_si256((__m256i*)(out0 + i), (__m256i)_mm512_cvtneps_pbh(cur0_0)); _mm256_storeu_si256((__m256i*)(out0 + i + 16), (__m256i)_mm512_cvtneps_pbh(cur0_1)); // Token 1 __m512 cur1_0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out1 + i))), 16)); __m512 cur1_1 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out1 + i + 16))), 16)); cur1_0 = _mm512_add_ps(cur1_0, acc_t1_0); cur1_1 = _mm512_add_ps(cur1_1, acc_t1_1); _mm256_storeu_si256((__m256i*)(out1 + i), (__m256i)_mm512_cvtneps_pbh(cur1_0)); _mm256_storeu_si256((__m256i*)(out1 + i + 16), (__m256i)_mm512_cvtneps_pbh(cur1_1)); // Token 2 __m512 cur2_0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out2 + i))), 16)); __m512 cur2_1 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out2 + i + 16))), 16)); cur2_0 = _mm512_add_ps(cur2_0, acc_t2_0); cur2_1 = _mm512_add_ps(cur2_1, acc_t2_1); _mm256_storeu_si256((__m256i*)(out2 + i), (__m256i)_mm512_cvtneps_pbh(cur2_0)); _mm256_storeu_si256((__m256i*)(out2 + i + 16), (__m256i)_mm512_cvtneps_pbh(cur2_1)); // Token 3 __m512 cur3_0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out3 + i))), 16)); __m512 cur3_1 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out3 + i + 16))), 16)); cur3_0 = _mm512_add_ps(cur3_0, acc_t3_0); cur3_1 = _mm512_add_ps(cur3_1, acc_t3_1); _mm256_storeu_si256((__m256i*)(out3 + i), (__m256i)_mm512_cvtneps_pbh(cur3_0)); _mm256_storeu_si256((__m256i*)(out3 + i + 16), (__m256i)_mm512_cvtneps_pbh(cur3_1)); } // Handle remaining outputs (< O_BLOCK, process 16 at a time) for (; i + 16 <= output_dim; i += 16) { __m512 acc_t0 = _mm512_setzero_ps(); __m512 acc_t1 = _mm512_setzero_ps(); __m512 acc_t2 = _mm512_setzero_ps(); __m512 acc_t3 = _mm512_setzero_ps(); for (int r = 0; r < rank; r++) { __m512 iv0 = _mm512_set1_ps(inter0[r]); __m512 iv1 = _mm512_set1_ps(inter1[r]); __m512 iv2 = _mm512_set1_ps(inter2[r]); __m512 iv3 = _mm512_set1_ps(inter3[r]); const ggml_bf16_t* w_ptr = weight + r * output_dim + i; __m512 wv = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)w_ptr)), 16)); acc_t0 = _mm512_fmadd_ps(iv0, wv, acc_t0); acc_t1 = _mm512_fmadd_ps(iv1, wv, acc_t1); acc_t2 = _mm512_fmadd_ps(iv2, wv, acc_t2); acc_t3 = _mm512_fmadd_ps(iv3, wv, acc_t3); } acc_t0 = _mm512_mul_ps(acc_t0, scale_vec); acc_t1 = _mm512_mul_ps(acc_t1, scale_vec); acc_t2 = _mm512_mul_ps(acc_t2, scale_vec); acc_t3 = _mm512_mul_ps(acc_t3, scale_vec); __m512 cur0 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out0 + i))), 16)); __m512 cur1 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out1 + i))), 16)); __m512 cur2 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out2 + i))), 16)); __m512 cur3 = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out3 + i))), 16)); cur0 = _mm512_add_ps(cur0, acc_t0); cur1 = _mm512_add_ps(cur1, acc_t1); cur2 = _mm512_add_ps(cur2, acc_t2); cur3 = _mm512_add_ps(cur3, acc_t3); _mm256_storeu_si256((__m256i*)(out0 + i), (__m256i)_mm512_cvtneps_pbh(cur0)); _mm256_storeu_si256((__m256i*)(out1 + i), (__m256i)_mm512_cvtneps_pbh(cur1)); _mm256_storeu_si256((__m256i*)(out2 + i), (__m256i)_mm512_cvtneps_pbh(cur2)); _mm256_storeu_si256((__m256i*)(out3 + i), (__m256i)_mm512_cvtneps_pbh(cur3)); } // Scalar remainder for tail outputs for (; i < output_dim; i++) { float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f; for (int r = 0; r < rank; r++) { float w = GGML_BF16_TO_FP32(weight[r * output_dim + i]); sum0 += inter0[r] * w; sum1 += inter1[r] * w; sum2 += inter2[r] * w; sum3 += inter3[r] * w; } out0[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out0[i]) + sum0 * scale); out1[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out1[i]) + sum1 * scale); out2[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out2[i]) + sum2 * scale); out3[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out3[i]) + sum3 * scale); } } // Handle remaining tokens (< T_BLOCK) for (; t < num_tokens; t++) { const float* inter_row = intermediate + t * rank; ggml_bf16_t* out_row = output + t * output_dim; int i = 0; for (; i + 16 <= output_dim; i += 16) { __m512 acc = _mm512_setzero_ps(); for (int r = 0; r < rank; r++) { __m512 iv = _mm512_set1_ps(inter_row[r]); const ggml_bf16_t* w_ptr = weight + r * output_dim + i; __m512 wv = _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)w_ptr)), 16)); acc = _mm512_fmadd_ps(iv, wv, acc); } acc = _mm512_mul_ps(acc, scale_vec); __m512 cur = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out_row + i))), 16)); cur = _mm512_add_ps(cur, acc); _mm256_storeu_si256((__m256i*)(out_row + i), (__m256i)_mm512_cvtneps_pbh(cur)); } for (; i < output_dim; i++) { float sum = 0.0f; for (int r = 0; r < rank; r++) { sum += inter_row[r] * GGML_BF16_TO_FP32(weight[r * output_dim + i]); } out_row[i] = GGML_FP32_TO_BF16(GGML_BF16_TO_FP32(out_row[i]) + sum * scale); } } #if AVX_KERNEL_TRACE_ENABLED char args_buf[128]; snprintf(args_buf, sizeof(args_buf), "{\"T\":%d,\"R\":%d,\"O\":%d}", num_tokens, rank, output_dim); #endif } // ============================================================================ // Pre-transposed weight utilities and optimized kernel // // Transpose weight from [output_dim][rank] to [rank][output_dim] // This enables contiguous memory access in the inner loop for better cache efficiency. // ============================================================================ // ============================================================================ // AVX-512 In-Register Transpose Kernels (4x4, 8x8, 16x16) // ============================================================================ /** * @brief Transpose 4x4 BF16 block in-register */ inline void transpose_4x4_bf16(const ggml_bf16_t* src, int src_stride, ggml_bf16_t* dst, int dst_stride) { __m128i r0 = _mm_loadl_epi64((__m128i*)(src + 0 * src_stride)); __m128i r1 = _mm_loadl_epi64((__m128i*)(src + 1 * src_stride)); __m128i r2 = _mm_loadl_epi64((__m128i*)(src + 2 * src_stride)); __m128i r3 = _mm_loadl_epi64((__m128i*)(src + 3 * src_stride)); __m128i t0 = _mm_unpacklo_epi16(r0, r1); __m128i t1 = _mm_unpacklo_epi16(r2, r3); __m128i s0 = _mm_unpacklo_epi32(t0, t1); __m128i s1 = _mm_unpackhi_epi32(t0, t1); _mm_storel_epi64((__m128i*)(dst + 0 * dst_stride), s0); _mm_storel_epi64((__m128i*)(dst + 1 * dst_stride), _mm_srli_si128(s0, 8)); _mm_storel_epi64((__m128i*)(dst + 2 * dst_stride), s1); _mm_storel_epi64((__m128i*)(dst + 3 * dst_stride), _mm_srli_si128(s1, 8)); } /** * @brief Transpose 8x8 BF16 block in-register using SSE */ inline void transpose_8x8_bf16(const ggml_bf16_t* src, int src_stride, ggml_bf16_t* dst, int dst_stride) { __m128i r0 = _mm_loadu_si128((__m128i*)(src + 0 * src_stride)); __m128i r1 = _mm_loadu_si128((__m128i*)(src + 1 * src_stride)); __m128i r2 = _mm_loadu_si128((__m128i*)(src + 2 * src_stride)); __m128i r3 = _mm_loadu_si128((__m128i*)(src + 3 * src_stride)); __m128i r4 = _mm_loadu_si128((__m128i*)(src + 4 * src_stride)); __m128i r5 = _mm_loadu_si128((__m128i*)(src + 5 * src_stride)); __m128i r6 = _mm_loadu_si128((__m128i*)(src + 6 * src_stride)); __m128i r7 = _mm_loadu_si128((__m128i*)(src + 7 * src_stride)); // Step 1: Interleave 16-bit __m128i t0 = _mm_unpacklo_epi16(r0, r1); __m128i t1 = _mm_unpackhi_epi16(r0, r1); __m128i t2 = _mm_unpacklo_epi16(r2, r3); __m128i t3 = _mm_unpackhi_epi16(r2, r3); __m128i t4 = _mm_unpacklo_epi16(r4, r5); __m128i t5 = _mm_unpackhi_epi16(r4, r5); __m128i t6 = _mm_unpacklo_epi16(r6, r7); __m128i t7 = _mm_unpackhi_epi16(r6, r7); // Step 2: Interleave 32-bit r0 = _mm_unpacklo_epi32(t0, t2); r1 = _mm_unpackhi_epi32(t0, t2); r2 = _mm_unpacklo_epi32(t1, t3); r3 = _mm_unpackhi_epi32(t1, t3); r4 = _mm_unpacklo_epi32(t4, t6); r5 = _mm_unpackhi_epi32(t4, t6); r6 = _mm_unpacklo_epi32(t5, t7); r7 = _mm_unpackhi_epi32(t5, t7); // Step 3: Interleave 64-bit t0 = _mm_unpacklo_epi64(r0, r4); t1 = _mm_unpackhi_epi64(r0, r4); t2 = _mm_unpacklo_epi64(r1, r5); t3 = _mm_unpackhi_epi64(r1, r5); t4 = _mm_unpacklo_epi64(r2, r6); t5 = _mm_unpackhi_epi64(r2, r6); t6 = _mm_unpacklo_epi64(r3, r7); t7 = _mm_unpackhi_epi64(r3, r7); _mm_storeu_si128((__m128i*)(dst + 0 * dst_stride), t0); _mm_storeu_si128((__m128i*)(dst + 1 * dst_stride), t1); _mm_storeu_si128((__m128i*)(dst + 2 * dst_stride), t2); _mm_storeu_si128((__m128i*)(dst + 3 * dst_stride), t3); _mm_storeu_si128((__m128i*)(dst + 4 * dst_stride), t4); _mm_storeu_si128((__m128i*)(dst + 5 * dst_stride), t5); _mm_storeu_si128((__m128i*)(dst + 6 * dst_stride), t6); _mm_storeu_si128((__m128i*)(dst + 7 * dst_stride), t7); } /** * @brief Transpose 16x16 BF16 block in-register using AVX2 */ inline void transpose_16x16_bf16(const ggml_bf16_t* src, int src_stride, ggml_bf16_t* dst, int dst_stride) { __m256i r0 = _mm256_loadu_si256((__m256i*)(src + 0 * src_stride)); __m256i r1 = _mm256_loadu_si256((__m256i*)(src + 1 * src_stride)); __m256i r2 = _mm256_loadu_si256((__m256i*)(src + 2 * src_stride)); __m256i r3 = _mm256_loadu_si256((__m256i*)(src + 3 * src_stride)); __m256i r4 = _mm256_loadu_si256((__m256i*)(src + 4 * src_stride)); __m256i r5 = _mm256_loadu_si256((__m256i*)(src + 5 * src_stride)); __m256i r6 = _mm256_loadu_si256((__m256i*)(src + 6 * src_stride)); __m256i r7 = _mm256_loadu_si256((__m256i*)(src + 7 * src_stride)); __m256i r8 = _mm256_loadu_si256((__m256i*)(src + 8 * src_stride)); __m256i r9 = _mm256_loadu_si256((__m256i*)(src + 9 * src_stride)); __m256i r10 = _mm256_loadu_si256((__m256i*)(src + 10 * src_stride)); __m256i r11 = _mm256_loadu_si256((__m256i*)(src + 11 * src_stride)); __m256i r12 = _mm256_loadu_si256((__m256i*)(src + 12 * src_stride)); __m256i r13 = _mm256_loadu_si256((__m256i*)(src + 13 * src_stride)); __m256i r14 = _mm256_loadu_si256((__m256i*)(src + 14 * src_stride)); __m256i r15 = _mm256_loadu_si256((__m256i*)(src + 15 * src_stride)); // Step 1: Interleave 16-bit __m256i t0 = _mm256_unpacklo_epi16(r0, r1); __m256i t1 = _mm256_unpackhi_epi16(r0, r1); __m256i t2 = _mm256_unpacklo_epi16(r2, r3); __m256i t3 = _mm256_unpackhi_epi16(r2, r3); __m256i t4 = _mm256_unpacklo_epi16(r4, r5); __m256i t5 = _mm256_unpackhi_epi16(r4, r5); __m256i t6 = _mm256_unpacklo_epi16(r6, r7); __m256i t7 = _mm256_unpackhi_epi16(r6, r7); __m256i t8 = _mm256_unpacklo_epi16(r8, r9); __m256i t9 = _mm256_unpackhi_epi16(r8, r9); __m256i t10 = _mm256_unpacklo_epi16(r10, r11); __m256i t11 = _mm256_unpackhi_epi16(r10, r11); __m256i t12 = _mm256_unpacklo_epi16(r12, r13); __m256i t13 = _mm256_unpackhi_epi16(r12, r13); __m256i t14 = _mm256_unpacklo_epi16(r14, r15); __m256i t15 = _mm256_unpackhi_epi16(r14, r15); // Step 2: Interleave 32-bit r0 = _mm256_unpacklo_epi32(t0, t2); r1 = _mm256_unpackhi_epi32(t0, t2); r2 = _mm256_unpacklo_epi32(t1, t3); r3 = _mm256_unpackhi_epi32(t1, t3); r4 = _mm256_unpacklo_epi32(t4, t6); r5 = _mm256_unpackhi_epi32(t4, t6); r6 = _mm256_unpacklo_epi32(t5, t7); r7 = _mm256_unpackhi_epi32(t5, t7); r8 = _mm256_unpacklo_epi32(t8, t10); r9 = _mm256_unpackhi_epi32(t8, t10); r10 = _mm256_unpacklo_epi32(t9, t11); r11 = _mm256_unpackhi_epi32(t9, t11); r12 = _mm256_unpacklo_epi32(t12, t14); r13 = _mm256_unpackhi_epi32(t12, t14); r14 = _mm256_unpacklo_epi32(t13, t15); r15 = _mm256_unpackhi_epi32(t13, t15); // Step 3: Interleave 64-bit t0 = _mm256_unpacklo_epi64(r0, r4); t1 = _mm256_unpackhi_epi64(r0, r4); t2 = _mm256_unpacklo_epi64(r1, r5); t3 = _mm256_unpackhi_epi64(r1, r5); t4 = _mm256_unpacklo_epi64(r2, r6); t5 = _mm256_unpackhi_epi64(r2, r6); t6 = _mm256_unpacklo_epi64(r3, r7); t7 = _mm256_unpackhi_epi64(r3, r7); t8 = _mm256_unpacklo_epi64(r8, r12); t9 = _mm256_unpackhi_epi64(r8, r12); t10 = _mm256_unpacklo_epi64(r9, r13); t11 = _mm256_unpackhi_epi64(r9, r13); t12 = _mm256_unpacklo_epi64(r10, r14); t13 = _mm256_unpackhi_epi64(r10, r14); t14 = _mm256_unpacklo_epi64(r11, r15); t15 = _mm256_unpackhi_epi64(r11, r15); // Step 4: Permute 128-bit lanes r0 = _mm256_permute2x128_si256(t0, t8, 0x20); r8 = _mm256_permute2x128_si256(t0, t8, 0x31); r1 = _mm256_permute2x128_si256(t1, t9, 0x20); r9 = _mm256_permute2x128_si256(t1, t9, 0x31); r2 = _mm256_permute2x128_si256(t2, t10, 0x20); r10 = _mm256_permute2x128_si256(t2, t10, 0x31); r3 = _mm256_permute2x128_si256(t3, t11, 0x20); r11 = _mm256_permute2x128_si256(t3, t11, 0x31); r4 = _mm256_permute2x128_si256(t4, t12, 0x20); r12 = _mm256_permute2x128_si256(t4, t12, 0x31); r5 = _mm256_permute2x128_si256(t5, t13, 0x20); r13 = _mm256_permute2x128_si256(t5, t13, 0x31); r6 = _mm256_permute2x128_si256(t6, t14, 0x20); r14 = _mm256_permute2x128_si256(t6, t14, 0x31); r7 = _mm256_permute2x128_si256(t7, t15, 0x20); r15 = _mm256_permute2x128_si256(t7, t15, 0x31); _mm256_storeu_si256((__m256i*)(dst + 0 * dst_stride), r0); _mm256_storeu_si256((__m256i*)(dst + 1 * dst_stride), r1); _mm256_storeu_si256((__m256i*)(dst + 2 * dst_stride), r2); _mm256_storeu_si256((__m256i*)(dst + 3 * dst_stride), r3); _mm256_storeu_si256((__m256i*)(dst + 4 * dst_stride), r4); _mm256_storeu_si256((__m256i*)(dst + 5 * dst_stride), r5); _mm256_storeu_si256((__m256i*)(dst + 6 * dst_stride), r6); _mm256_storeu_si256((__m256i*)(dst + 7 * dst_stride), r7); _mm256_storeu_si256((__m256i*)(dst + 8 * dst_stride), r8); _mm256_storeu_si256((__m256i*)(dst + 9 * dst_stride), r9); _mm256_storeu_si256((__m256i*)(dst + 10 * dst_stride), r10); _mm256_storeu_si256((__m256i*)(dst + 11 * dst_stride), r11); _mm256_storeu_si256((__m256i*)(dst + 12 * dst_stride), r12); _mm256_storeu_si256((__m256i*)(dst + 13 * dst_stride), r13); _mm256_storeu_si256((__m256i*)(dst + 14 * dst_stride), r14); _mm256_storeu_si256((__m256i*)(dst + 15 * dst_stride), r15); } /** * @brief Transpose LoRA B weight from [output_dim][rank] to [rank][output_dim] * * Uses AVX2/SSE in-register transpose kernels (16x16, 8x8, 4x4) for high performance. * Achieves 2-6x speedup over naive implementation. * * @param src Source weight [output_dim][rank] * @param dst Destination weight [rank][output_dim] * @param output_dim Output dimension * @param rank LoRA rank */ inline void transpose_lora_weight(const ggml_bf16_t* __restrict src, ggml_bf16_t* __restrict dst, int output_dim, int rank) { int r = 0; // Process 16x16 blocks for (; r + 16 <= rank; r += 16) { int i = 0; for (; i + 16 <= output_dim; i += 16) { transpose_16x16_bf16(src + i * rank + r, rank, dst + r * output_dim + i, output_dim); } // 8x8 for remainder columns for (; i + 8 <= output_dim; i += 8) { transpose_8x8_bf16(src + i * rank + r, rank, dst + r * output_dim + i, output_dim); transpose_8x8_bf16(src + i * rank + r + 8, rank, dst + (r + 8) * output_dim + i, output_dim); } // 4x4 for remainder columns for (; i + 4 <= output_dim; i += 4) { transpose_4x4_bf16(src + i * rank + r, rank, dst + r * output_dim + i, output_dim); transpose_4x4_bf16(src + i * rank + r + 4, rank, dst + (r + 4) * output_dim + i, output_dim); transpose_4x4_bf16(src + i * rank + r + 8, rank, dst + (r + 8) * output_dim + i, output_dim); transpose_4x4_bf16(src + i * rank + r + 12, rank, dst + (r + 12) * output_dim + i, output_dim); } // Scalar remainder for (; i < output_dim; i++) { for (int rr = 0; rr < 16; rr++) { dst[(r + rr) * output_dim + i] = src[i * rank + (r + rr)]; } } } // Process 8x8 blocks for remaining rows for (; r + 8 <= rank; r += 8) { int i = 0; for (; i + 8 <= output_dim; i += 8) { transpose_8x8_bf16(src + i * rank + r, rank, dst + r * output_dim + i, output_dim); } for (; i + 4 <= output_dim; i += 4) { transpose_4x4_bf16(src + i * rank + r, rank, dst + r * output_dim + i, output_dim); transpose_4x4_bf16(src + i * rank + r + 4, rank, dst + (r + 4) * output_dim + i, output_dim); } for (; i < output_dim; i++) { for (int rr = 0; rr < 8; rr++) { dst[(r + rr) * output_dim + i] = src[i * rank + (r + rr)]; } } } // Process 4x4 blocks for remaining rows for (; r + 4 <= rank; r += 4) { int i = 0; for (; i + 4 <= output_dim; i += 4) { transpose_4x4_bf16(src + i * rank + r, rank, dst + r * output_dim + i, output_dim); } for (; i < output_dim; i++) { for (int rr = 0; rr < 4; rr++) { dst[(r + rr) * output_dim + i] = src[i * rank + (r + rr)]; } } } // Scalar remainder rows for (; r < rank; r++) { for (int i = 0; i < output_dim; i++) { dst[r * output_dim + i] = src[i * rank + r]; } } } /** * @brief Fused LoRA add with pre-transposed weight (optimized version) * * Computes: output[t, i] += scale * sum_r(intermediate[t, r] * weight_t[r, i]) * * Key optimization: weight_t is pre-transposed to [rank][output_dim], allowing * contiguous memory access for 16 outputs at a time in the inner loop. * This eliminates the horizontal reduction overhead and maximizes cache efficiency. * * @param intermediate FP32 input [num_tokens, rank] * @param weight_t Pre-transposed BF16 weight [rank][output_dim] * @param output BF16 output [num_tokens, output_dim] (accumulated) * @param num_tokens Number of tokens * @param rank LoRA rank * @param output_dim Output dimension * @param scale LoRA scaling factor */ inline void lora_fp32_bf16_fused_add_transposed(const float* __restrict intermediate, const ggml_bf16_t* __restrict weight_t, ggml_bf16_t* __restrict output, int num_tokens, int rank, int output_dim, float scale) { #if AVX_KERNEL_TRACE_ENABLED #endif constexpr int T_BLOCK = 4; constexpr int PANEL = 5; // 5 output vectors = 80 outputs per panel constexpr int PANEL_WIDTH = PANEL * 16; constexpr int O_BLOCK = 16; // Fallback for panel remainder const __m512 scale_vec = _mm512_set1_ps(scale); const int output_tail = output_dim & 15; const __mmask16 output_tail_mask = output_tail ? ((__mmask16)1 << output_tail) - 1 : 0; // Epilogue helper: scale, load BF16 output, add, convert back, store #define LORA_FUSED_ADD_EPILOGUE(out_ptr, acc, offset) \ do { \ __m512 _ov = _mm512_castsi512_ps( \ _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)((out_ptr) + (offset)))), 16)); \ _ov = _mm512_add_ps(_ov, _mm512_mul_ps(scale_vec, acc)); \ _mm256_storeu_si256((__m256i*)((out_ptr) + (offset)), (__m256i)_mm512_cvtneps_pbh(_ov)); \ } while (0) int t = 0; for (; t + T_BLOCK <= num_tokens; t += T_BLOCK) { const float* inter0 = intermediate + (t + 0) * rank; const float* inter1 = intermediate + (t + 1) * rank; const float* inter2 = intermediate + (t + 2) * rank; const float* inter3 = intermediate + (t + 3) * rank; ggml_bf16_t* out0 = output + (t + 0) * output_dim; ggml_bf16_t* out1 = output + (t + 1) * output_dim; ggml_bf16_t* out2 = output + (t + 2) * output_dim; ggml_bf16_t* out3 = output + (t + 3) * output_dim; // Panel-5 rank-outer: each broadcast drives 5 weight vectors int i = 0; for (; i + PANEL_WIDTH <= output_dim; i += PANEL_WIDTH) { // 20 accumulators: 4 tokens × 5 output vectors __m512 a00 = _mm512_setzero_ps(), a01 = _mm512_setzero_ps(), a02 = _mm512_setzero_ps(), a03 = _mm512_setzero_ps(), a04 = _mm512_setzero_ps(); __m512 a10 = _mm512_setzero_ps(), a11 = _mm512_setzero_ps(), a12 = _mm512_setzero_ps(), a13 = _mm512_setzero_ps(), a14 = _mm512_setzero_ps(); __m512 a20 = _mm512_setzero_ps(), a21 = _mm512_setzero_ps(), a22 = _mm512_setzero_ps(), a23 = _mm512_setzero_ps(), a24 = _mm512_setzero_ps(); __m512 a30 = _mm512_setzero_ps(), a31 = _mm512_setzero_ps(), a32 = _mm512_setzero_ps(), a33 = _mm512_setzero_ps(), a34 = _mm512_setzero_ps(); for (int r = 0; r < rank; r++) { __m512 v0 = _mm512_set1_ps(inter0[r]); __m512 v1 = _mm512_set1_ps(inter1[r]); __m512 v2 = _mm512_set1_ps(inter2[r]); __m512 v3 = _mm512_set1_ps(inter3[r]); const ggml_bf16_t* wp = weight_t + r * output_dim + i; #define LOAD_BF16_FP32(off) \ _mm512_castsi512_ps(_mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(wp + (off)))), 16)) __m512 w0 = LOAD_BF16_FP32(0), w1 = LOAD_BF16_FP32(16), w2 = LOAD_BF16_FP32(32), w3 = LOAD_BF16_FP32(48), w4 = LOAD_BF16_FP32(64); #undef LOAD_BF16_FP32 a00 = _mm512_fmadd_ps(v0, w0, a00); a01 = _mm512_fmadd_ps(v0, w1, a01); a02 = _mm512_fmadd_ps(v0, w2, a02); a03 = _mm512_fmadd_ps(v0, w3, a03); a04 = _mm512_fmadd_ps(v0, w4, a04); a10 = _mm512_fmadd_ps(v1, w0, a10); a11 = _mm512_fmadd_ps(v1, w1, a11); a12 = _mm512_fmadd_ps(v1, w2, a12); a13 = _mm512_fmadd_ps(v1, w3, a13); a14 = _mm512_fmadd_ps(v1, w4, a14); a20 = _mm512_fmadd_ps(v2, w0, a20); a21 = _mm512_fmadd_ps(v2, w1, a21); a22 = _mm512_fmadd_ps(v2, w2, a22); a23 = _mm512_fmadd_ps(v2, w3, a23); a24 = _mm512_fmadd_ps(v2, w4, a24); a30 = _mm512_fmadd_ps(v3, w0, a30); a31 = _mm512_fmadd_ps(v3, w1, a31); a32 = _mm512_fmadd_ps(v3, w2, a32); a33 = _mm512_fmadd_ps(v3, w3, a33); a34 = _mm512_fmadd_ps(v3, w4, a34); } // Epilogue: scale + BF16 read-modify-write LORA_FUSED_ADD_EPILOGUE(out0, a00, i); LORA_FUSED_ADD_EPILOGUE(out0, a01, i + 16); LORA_FUSED_ADD_EPILOGUE(out0, a02, i + 32); LORA_FUSED_ADD_EPILOGUE(out0, a03, i + 48); LORA_FUSED_ADD_EPILOGUE(out0, a04, i + 64); LORA_FUSED_ADD_EPILOGUE(out1, a10, i); LORA_FUSED_ADD_EPILOGUE(out1, a11, i + 16); LORA_FUSED_ADD_EPILOGUE(out1, a12, i + 32); LORA_FUSED_ADD_EPILOGUE(out1, a13, i + 48); LORA_FUSED_ADD_EPILOGUE(out1, a14, i + 64); LORA_FUSED_ADD_EPILOGUE(out2, a20, i); LORA_FUSED_ADD_EPILOGUE(out2, a21, i + 16); LORA_FUSED_ADD_EPILOGUE(out2, a22, i + 32); LORA_FUSED_ADD_EPILOGUE(out2, a23, i + 48); LORA_FUSED_ADD_EPILOGUE(out2, a24, i + 64); LORA_FUSED_ADD_EPILOGUE(out3, a30, i); LORA_FUSED_ADD_EPILOGUE(out3, a31, i + 16); LORA_FUSED_ADD_EPILOGUE(out3, a32, i + 32); LORA_FUSED_ADD_EPILOGUE(out3, a33, i + 48); LORA_FUSED_ADD_EPILOGUE(out3, a34, i + 64); } // Remainder outputs: O_BLOCK=16 fallback (rank-inner, same as before) for (; i + O_BLOCK <= output_dim; i += O_BLOCK) { __m512 acc0 = _mm512_setzero_ps(), acc1 = _mm512_setzero_ps(), acc2 = _mm512_setzero_ps(), acc3 = _mm512_setzero_ps(); for (int r = 0; r < rank; r++) { __m512 wv = _mm512_castsi512_ps(_mm512_slli_epi32( _mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(weight_t + r * output_dim + i))), 16)); acc0 = _mm512_fmadd_ps(_mm512_set1_ps(inter0[r]), wv, acc0); acc1 = _mm512_fmadd_ps(_mm512_set1_ps(inter1[r]), wv, acc1); acc2 = _mm512_fmadd_ps(_mm512_set1_ps(inter2[r]), wv, acc2); acc3 = _mm512_fmadd_ps(_mm512_set1_ps(inter3[r]), wv, acc3); } LORA_FUSED_ADD_EPILOGUE(out0, acc0, i); LORA_FUSED_ADD_EPILOGUE(out1, acc1, i); LORA_FUSED_ADD_EPILOGUE(out2, acc2, i); LORA_FUSED_ADD_EPILOGUE(out3, acc3, i); } // Handle remaining outputs (< 16) if (output_tail_mask) { __m512 acc0 = _mm512_setzero_ps(), acc1 = _mm512_setzero_ps(), acc2 = _mm512_setzero_ps(), acc3 = _mm512_setzero_ps(); for (int r = 0; r < rank; r++) { __m512 wv = _mm512_castsi512_ps(_mm512_slli_epi32( _mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(output_tail_mask, weight_t + r * output_dim + i)), 16)); acc0 = _mm512_fmadd_ps(_mm512_set1_ps(inter0[r]), wv, acc0); acc1 = _mm512_fmadd_ps(_mm512_set1_ps(inter1[r]), wv, acc1); acc2 = _mm512_fmadd_ps(_mm512_set1_ps(inter2[r]), wv, acc2); acc3 = _mm512_fmadd_ps(_mm512_set1_ps(inter3[r]), wv, acc3); } acc0 = _mm512_mul_ps(acc0, scale_vec); acc1 = _mm512_mul_ps(acc1, scale_vec); acc2 = _mm512_mul_ps(acc2, scale_vec); acc3 = _mm512_mul_ps(acc3, scale_vec); __m512 out_v0 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(output_tail_mask, out0 + i)), 16)); __m512 out_v1 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(output_tail_mask, out1 + i)), 16)); __m512 out_v2 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(output_tail_mask, out2 + i)), 16)); __m512 out_v3 = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(output_tail_mask, out3 + i)), 16)); _mm256_mask_storeu_epi16(out0 + i, output_tail_mask, (__m256i)_mm512_cvtneps_pbh(_mm512_add_ps(out_v0, acc0))); _mm256_mask_storeu_epi16(out1 + i, output_tail_mask, (__m256i)_mm512_cvtneps_pbh(_mm512_add_ps(out_v1, acc1))); _mm256_mask_storeu_epi16(out2 + i, output_tail_mask, (__m256i)_mm512_cvtneps_pbh(_mm512_add_ps(out_v2, acc2))); _mm256_mask_storeu_epi16(out3 + i, output_tail_mask, (__m256i)_mm512_cvtneps_pbh(_mm512_add_ps(out_v3, acc3))); } } #undef LORA_FUSED_ADD_EPILOGUE // Remaining tokens (< T_BLOCK) for (; t < num_tokens; t++) { const float* inter_row = intermediate + t * rank; ggml_bf16_t* out_row = output + t * output_dim; int i = 0; for (; i + O_BLOCK <= output_dim; i += O_BLOCK) { __m512 acc = _mm512_setzero_ps(); for (int r = 0; r < rank; r++) { __m512 wv = _mm512_castsi512_ps(_mm512_slli_epi32( _mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(weight_t + r * output_dim + i))), 16)); __m512 iv = _mm512_set1_ps(inter_row[r]); acc = _mm512_fmadd_ps(iv, wv, acc); } acc = _mm512_mul_ps(acc, scale_vec); __m512 out_v = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_loadu_si256((__m256i*)(out_row + i))), 16)); out_v = _mm512_add_ps(out_v, acc); _mm256_storeu_si256((__m256i*)(out_row + i), (__m256i)_mm512_cvtneps_pbh(out_v)); } if (output_tail_mask) { __m512 acc = _mm512_setzero_ps(); for (int r = 0; r < rank; r++) { __m512 wv = _mm512_castsi512_ps(_mm512_slli_epi32( _mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(output_tail_mask, weight_t + r * output_dim + i)), 16)); __m512 iv = _mm512_set1_ps(inter_row[r]); acc = _mm512_fmadd_ps(iv, wv, acc); } acc = _mm512_mul_ps(acc, scale_vec); __m512 out_v = _mm512_castsi512_ps( _mm512_slli_epi32(_mm512_cvtepu16_epi32(_mm256_maskz_loadu_epi16(output_tail_mask, out_row + i)), 16)); out_v = _mm512_add_ps(out_v, acc); _mm256_mask_storeu_epi16(out_row + i, output_tail_mask, (__m256i)_mm512_cvtneps_pbh(out_v)); } } #if AVX_KERNEL_TRACE_ENABLED char args_buf[128]; snprintf(args_buf, sizeof(args_buf), "{\"T\":%d,\"R\":%d,\"O\":%d}", num_tokens, rank, output_dim); #endif } /** * @brief Optimized matmul for backward: grad @ lora_B_transposed -> result * * Computes result[t, r] = Σ_h grad[t, h] * lora_B_t[r, h] * Using pre-transposed lora_B with layout [rank, hidden] for contiguous access. * * @param grad Input gradient [num_tokens, hidden] BF16 * @param lora_b_t Pre-transposed lora_B [rank, hidden] BF16 * @param result Output [num_tokens, rank] FP32 * @param num_tokens Number of tokens * @param hidden Hidden dimension (input dim) * @param rank LoRA rank (output dim) */ inline void lora_backward_matmul_transposed(const ggml_bf16_t* __restrict grad, const ggml_bf16_t* __restrict lora_b_t, float* __restrict result, int num_tokens, int hidden, int rank) { #if AVX_KERNEL_TRACE_ENABLED #endif constexpr int H_BLOCK = 32; for (int t = 0; t < num_tokens; t++) { const ggml_bf16_t* g_row = grad + t * hidden; for (int r = 0; r < rank; r++) { const ggml_bf16_t* b_row = lora_b_t + r * hidden; __m512 acc0 = _mm512_setzero_ps(); __m512 acc1 = _mm512_setzero_ps(); int h = 0; for (; h + H_BLOCK <= hidden; h += H_BLOCK) { // Load 32 weights (contiguous access from transposed layout) __m512 b0, b1; avx512_32xbf16_to_32xfp32((__m512i*)(b_row + h), &b0, &b1); // Load 32 gradient values __m512 g0, g1; avx512_32xbf16_to_32xfp32((__m512i*)(g_row + h), &g0, &g1); acc0 = _mm512_fmadd_ps(g0, b0, acc0); acc1 = _mm512_fmadd_ps(g1, b1, acc1); } float sum = _mm512_reduce_add_ps(acc0) + _mm512_reduce_add_ps(acc1); // Handle remaining elements for (; h < hidden; h++) { sum += GGML_BF16_TO_FP32(g_row[h]) * GGML_BF16_TO_FP32(b_row[h]); } result[t * rank + r] = sum; } } #if AVX_KERNEL_TRACE_ENABLED char args_buf[128]; snprintf(args_buf, sizeof(args_buf), "{\"T\":%d,\"H\":%d,\"R\":%d}", num_tokens, hidden, rank); #endif } } // namespace avx #endif // AVX_KERNELS_HPP