* hex-mm: fold mm quant tasks into the main matmul threads * hex-mm: minor formatting fixes * hex-mm: cleanup is_quant checks in dma dispatch * hex-mm: fix dst-spad alignment * hex-mm: move fp kernels in the hvx-mm-kernels header * hex-mm: fuse with ADD * hex-fa: factor out ukernels into separate headers and unify the rest * hex-fa: move kernel-params compute into the host * hex-fa: refactor vtcm alloc for consistency * hex-fa: add support for FA_SELECT * hex-fa: update tracing insrumentation to cover all functions * hex-fa: update hvx fallback thresholds to recover t/g regressions * hex-fa: update tracing instrumentation * hex-fa: improved tracing with additional events * hex-fa: optimize mask processing (fastdiv, etc) * hex-fa: improve mask dma caching * hmx-fa: change loop order to maximize mask cache hits * hex-fa: remove over instrumentation * hex-fa: breakdown QKV prep trace events * hmx-fa: further mask proc optimizations * hex-fa: mask broadcast is the common case, optimize for that * hex-fa: use aligned loads where possible * hex-fa: update loops to use uint32_t indices * hmx-fa: fold vtcm init into q prep task * hex-fa: update rest of the hmx funcs to use uint32_t * hmx-fa: fold build_d into the main softmax loop * hmx-fa: start kv dmas earlier * hmx-fa: start mask dma a bit earlier * hex-fa: precompute rows per task to avoid divs * hmx-fa: specialize fa_o_store for f16 and f32 * hmx-fa: prelim support for Sinks * hmx-fa: keep softmax accumulators in fp32 * hex-fa: add tanh_f16 and exp2_f16 and use that in FA * hex-fa: use fp16 math in the hvx kernel * hex-fa: avoid expensive float -> __fp16 cast for slopes and softcap * hex-fa: replace most vec_exp_f32 with vec_exp2_f16 * hmx-fa: vectorize sinks update * hex-fa: minor formatting * hmx-fa: fold softcap loop into the tile load * hmx-fa: use vectoralias to populate sinks * hex-fa: remove redudant check * hex-fa: fix vtcm size compute to use fp32 for accumulators * hex-mm: fix trailing spaces * hmx-fa: dont use -inf to init mask to avoid conversion overflows * hex-fa: no need to explicitly guard -inf in the f16->f32 converter now * hmx-fa: cleanup fa sinks handling * hex-mm: fixed src2 stride handling when mm is fused with add * hex-fa: make lto happy
233 lines
8.8 KiB
C
233 lines
8.8 KiB
C
#ifndef HVX_FA_KERNELS_H
|
|
#define HVX_FA_KERNELS_H
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include "hvx-utils.h"
|
|
|
|
// Little inner kernels for HVX
|
|
|
|
#if __HVX_ARCH__ < 79
|
|
#define HVX_OP_ADD_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(a, b))
|
|
#define HVX_OP_SUB_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(a, b))
|
|
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(a, b))
|
|
#else
|
|
#define HVX_OP_ADD_F32(a, b) Q6_Vsf_vadd_VsfVsf(a, b)
|
|
#define HVX_OP_SUB_F32(a, b) Q6_Vsf_vsub_VsfVsf(a, b)
|
|
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_vmpy_VsfVsf(a, b)
|
|
#endif
|
|
|
|
// This is a bit of a hack because the compiler is struggling to properly inline
|
|
// the default hvx_vec_f32_to_f16 with output into the local array.
|
|
static __attribute__((unused)) __attribute__((noinline)) void hvx_vec_f32_to_f16_a(void *ptr, HVX_Vector v0, HVX_Vector v1)
|
|
{
|
|
*(HVX_Vector *) ptr = hvx_vec_f32_to_f16(v0, v1);
|
|
}
|
|
|
|
// Dot product of two F16 vectors, accumulating to float
|
|
static inline void hvx_dot_f16_f16_aa(float * restrict r, const void * restrict x, const void * restrict y, unsigned int n, float s) {
|
|
const HVX_Vector * restrict vx = (const HVX_Vector * restrict) x; // fp16
|
|
const HVX_Vector * restrict vy = (const HVX_Vector * restrict) y; // fp16
|
|
|
|
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
|
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
|
|
|
HVX_VectorPair rsum_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
|
|
|
uint32_t i = 0;
|
|
|
|
#pragma unroll(4)
|
|
for (i = 0; i < nvec; i++) {
|
|
rsum_p = hvx_vec_mpyacc_f32_f16(rsum_p, vx[i], vy[i]);
|
|
}
|
|
|
|
if (nloe) {
|
|
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 2);
|
|
HVX_Vector y_hf = Q6_V_vand_QV(bmask, vy[i]);
|
|
HVX_Vector x_hf = Q6_V_vand_QV(bmask, vx[i]);
|
|
|
|
rsum_p = hvx_vec_mpyacc_f32_f16(rsum_p, x_hf, y_hf);
|
|
}
|
|
|
|
HVX_Vector rsum = HVX_OP_ADD_F32(Q6_V_lo_W(rsum_p), Q6_V_hi_W(rsum_p));
|
|
rsum = HVX_OP_MUL_F32(hvx_vec_splat_f32(s), hvx_vec_reduce_sum_f32(rsum));
|
|
hvx_vec_store_u(r, 4, rsum);
|
|
}
|
|
|
|
static inline HVX_Vector hvx_dot_f16_f16_aa_rx4(const void * restrict y,
|
|
const uint8_t * restrict x,
|
|
const size_t stride_x,
|
|
const size_t nvec,
|
|
const size_t nloe) {
|
|
const HVX_Vector * restrict vx0 = (const HVX_Vector * restrict) x; // fp16
|
|
const HVX_Vector * restrict vx1 = (const HVX_Vector * restrict) (x + stride_x); // fp16
|
|
const HVX_Vector * restrict vx2 = (const HVX_Vector * restrict) (x + stride_x * 2); // fp16
|
|
const HVX_Vector * restrict vx3 = (const HVX_Vector * restrict) (x + stride_x * 3); // fp16
|
|
const HVX_Vector * restrict vy = (const HVX_Vector * restrict) y; // fp16
|
|
|
|
HVX_VectorPair rsum0_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
|
HVX_VectorPair rsum1_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
|
HVX_VectorPair rsum2_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
|
HVX_VectorPair rsum3_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
|
|
|
uint32_t i = 0;
|
|
|
|
for (i = 0; i < nvec; i++) {
|
|
HVX_Vector y_hf = vy[i];
|
|
HVX_Vector x0_hf = vx0[i];
|
|
HVX_Vector x1_hf = vx1[i];
|
|
HVX_Vector x2_hf = vx2[i];
|
|
HVX_Vector x3_hf = vx3[i];
|
|
|
|
rsum0_p = hvx_vec_mpyacc_f32_f16(rsum0_p, x0_hf, y_hf);
|
|
rsum1_p = hvx_vec_mpyacc_f32_f16(rsum1_p, x1_hf, y_hf);
|
|
rsum2_p = hvx_vec_mpyacc_f32_f16(rsum2_p, x2_hf, y_hf);
|
|
rsum3_p = hvx_vec_mpyacc_f32_f16(rsum3_p, x3_hf, y_hf);
|
|
}
|
|
|
|
if (nloe) {
|
|
// Load x (fp16) and zero-out unused elements
|
|
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 2);
|
|
HVX_Vector y_hf = Q6_V_vand_QV(bmask, vy[i]);
|
|
HVX_Vector x0_hf = Q6_V_vand_QV(bmask, vx0[i]);
|
|
HVX_Vector x1_hf = Q6_V_vand_QV(bmask, vx1[i]);
|
|
HVX_Vector x2_hf = Q6_V_vand_QV(bmask, vx2[i]);
|
|
HVX_Vector x3_hf = Q6_V_vand_QV(bmask, vx3[i]);
|
|
|
|
rsum0_p = hvx_vec_mpyacc_f32_f16(rsum0_p, x0_hf, y_hf);
|
|
rsum1_p = hvx_vec_mpyacc_f32_f16(rsum1_p, x1_hf, y_hf);
|
|
rsum2_p = hvx_vec_mpyacc_f32_f16(rsum2_p, x2_hf, y_hf);
|
|
rsum3_p = hvx_vec_mpyacc_f32_f16(rsum3_p, x3_hf, y_hf);
|
|
}
|
|
|
|
HVX_Vector rsum0 = HVX_OP_ADD_F32(Q6_V_lo_W(rsum0_p), Q6_V_hi_W(rsum0_p));
|
|
HVX_Vector rsum1 = HVX_OP_ADD_F32(Q6_V_lo_W(rsum1_p), Q6_V_hi_W(rsum1_p));
|
|
HVX_Vector rsum2 = HVX_OP_ADD_F32(Q6_V_lo_W(rsum2_p), Q6_V_hi_W(rsum2_p));
|
|
HVX_Vector rsum3 = HVX_OP_ADD_F32(Q6_V_lo_W(rsum3_p), Q6_V_hi_W(rsum3_p));
|
|
|
|
HVX_Vector_x4 rsum0123 = { .v = { rsum0, rsum1, rsum2, rsum3 } };
|
|
return hvx_vec_reduce_sum_f32x4(rsum0123);
|
|
}
|
|
|
|
static inline HVX_Vector hvx_dot_f16_f16_aa_rx32(const void * restrict y,
|
|
const uint8_t * restrict x,
|
|
const size_t stride_x,
|
|
const size_t n,
|
|
float s) {
|
|
|
|
const size_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
|
const size_t nloe = n % VLEN_FP16; // leftover elements
|
|
|
|
HVX_Vector sums = Q6_V_vzero();
|
|
const size_t stride_x_4 = stride_x * 4;
|
|
for (uint32_t j = 0; j < VLEN_FP32; j += 4) {
|
|
HVX_Vector sums_x4 = hvx_dot_f16_f16_aa_rx4(y, x, stride_x, nvec, nloe);
|
|
HVX_VectorPred pred = Q6_Q_vsetq_R(j * SIZEOF_FP32);
|
|
sums = Q6_V_vmux_QVV(pred, sums, sums_x4);
|
|
x += stride_x_4;
|
|
}
|
|
|
|
return HVX_OP_MUL_F32(hvx_vec_splat_f32(s), sums);
|
|
}
|
|
|
|
// MAD: y (F32) += x (F16) * s (F16)
|
|
static inline void hvx_mad_f32_f16_aa(float * restrict y, const void * restrict x, const __fp16 * restrict s, uint32_t n) {
|
|
const HVX_Vector * restrict vx0 = (const HVX_Vector *) x;
|
|
|
|
HVX_VectorPair * restrict vy_p = (HVX_VectorPair *) y;
|
|
HVX_Vector * restrict vy = (HVX_Vector *) y;
|
|
|
|
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
|
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
|
|
|
HVX_Vector S0 = hvx_vec_splat_f16(*s);
|
|
|
|
uint32_t i = 0;
|
|
|
|
#pragma unroll(2)
|
|
for (i = 0; i < nvec; ++i) {
|
|
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
|
}
|
|
|
|
if (nloe) {
|
|
HVX_VectorPair xy_p = vy_p[i];
|
|
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
|
|
|
HVX_Vector xy = Q6_V_lo_W(xy_p);
|
|
i = 2 * i; // index for vy
|
|
|
|
if (nloe >= VLEN_FP32) {
|
|
vy[i] = xy;
|
|
nloe -= VLEN_FP32; ++i; xy = Q6_V_hi_W(xy_p);
|
|
}
|
|
|
|
if (nloe) {
|
|
hvx_vec_store_a(&vy[i], nloe * 4, xy);
|
|
}
|
|
}
|
|
}
|
|
|
|
// MAD: y (F32) += x0 (F16) * s0 (F16) + x1 (F16) * s1 (F16)
|
|
static inline void hvx_mad_f32_f16_aa_rx2(float * restrict y, const void * restrict x0, const void * restrict x1,
|
|
const __fp16 * restrict s0, const __fp16 * restrict s1, uint32_t n) {
|
|
const HVX_Vector * restrict vx0 = (const HVX_Vector *) x0;
|
|
const HVX_Vector * restrict vx1 = (const HVX_Vector *) x1;
|
|
|
|
HVX_VectorPair * restrict vy_p = (HVX_VectorPair *) y;
|
|
HVX_Vector * restrict vy = (HVX_Vector *) y;
|
|
|
|
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
|
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
|
|
|
HVX_Vector S0 = hvx_vec_splat_f16(*s0);
|
|
HVX_Vector S1 = hvx_vec_splat_f16(*s1);
|
|
|
|
uint32_t i = 0;
|
|
|
|
#pragma unroll(2)
|
|
for (i = 0; i < nvec; ++i) {
|
|
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
|
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx1[i]), S1);
|
|
}
|
|
|
|
if (nloe) {
|
|
HVX_VectorPair xy_p = vy_p[i];
|
|
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
|
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx1[i]), S1);
|
|
|
|
HVX_Vector xy = Q6_V_lo_W(xy_p);
|
|
i = 2 * i; // index for vy
|
|
|
|
if (nloe >= VLEN_FP32) {
|
|
vy[i] = xy;
|
|
nloe -= VLEN_FP32; ++i; xy = Q6_V_hi_W(xy_p);
|
|
}
|
|
|
|
if (nloe) {
|
|
hvx_vec_store_a(&vy[i], nloe * 4, xy);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void hvx_scale_vec_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, const uint32_t n, HVX_Vector vs) {
|
|
assert((size_t) dst % 128 == 0);
|
|
assert((size_t) src % 128 == 0);
|
|
|
|
const HVX_Vector * restrict vsrc = (const HVX_Vector * restrict) src;
|
|
HVX_Vector * restrict vdst = (HVX_Vector * restrict) dst;
|
|
|
|
const uint32_t nvec = n / VLEN_FP32;
|
|
const uint32_t nloe = n % VLEN_FP32;
|
|
|
|
uint32_t i = 0;
|
|
#pragma unroll(4)
|
|
for (; i < nvec; ++i) {
|
|
vdst[i] = HVX_OP_MUL_F32(vsrc[i], vs);
|
|
}
|
|
if (nloe) {
|
|
hvx_vec_store_a(&vdst[i], nloe * sizeof(float), HVX_OP_MUL_F32(vsrc[i], vs));
|
|
}
|
|
}
|
|
|
|
#endif /* HVX_FA_KERNELS_H */
|