#if defined(__ANDROID__) || defined(__aarch64__) #include #include #include "Arm82Functions.hpp" #include "Arm82OptFunc.hpp" #include "Arm82WinogradOptFunc.hpp" #include "Arm82Vec.hpp" #include "Arm82Binary.hpp" #include "Arm82Unary.hpp" #include "Arm82Relu.hpp" #include "backend/cpu/compute/CommonOptFunction.h" #include "backend/cpu/CPUPool.hpp" #include "backend/cpu/CPURuntime.hpp" #define FLOAT FLOAT16 #define PACK 8 using Vec = MNN::Math::Vec; #include "backend/cpu/GridSampler.hpp" #if defined(MNN_USE_NEON) #include #endif extern "C" { // (UP_DIV(l,8), e, 8) -> (UP_DIV(e,eP), l, eP) void Arm82MNNPackForMatMul_A(float* destOrigin, float const** sourceGroup, const int32_t* info, const int32_t* el); // void MNNPackTransposeInt16C8(int16_t* dst, const int16_t* src, size_t area, size_t depth, int32_t* areaOffset); // C(UP_DIV(h,8), e, h8) = B(UP_DIV(h,hP), l, hP) * A(l, eP), hP = 24 // parameter: [aStride, l, h, cStride, bExtraStride] // aStride in parameter is deprecated (useless), but for code clean, just retain it void MNNPackedMatMulFP16(float* C, const float* A, const float* B, const size_t* parameter, const float* postParameters, const float* bias, const float* k, const float* b); // C(UP_DIV(h,8), e, h8) = B(UP_DIV(h,hP), l, hP) * A(l, e), hP = 24, e >= 1 // parameter: [aStride, l, h, cStride, bExtraStride] void MNNPackedMatMulRemainFP16(float* C, const float* A, const float* B, size_t eSize, const size_t* parameter, const float* postParameters, const float* bias, const float* k, const float* b); #ifdef __aarch64__ #ifdef MNN_LOW_MEMORY void MNNAbsMaxFP16_Pack8(const float* source, float* absmax, size_t src_depth_quad, size_t realSize, int pack); void MNNAbsMaxFP16_Pack4(const float* source, float* absmax, size_t src_depth_quad, size_t realSize, int pack); void MNNQuantScaleFP16(float* sum, float* absmax, float* quant_scale, float* dequant_scale, size_t thread, size_t batch); void MNNDynamicQuantFP16_Pack8(const float* src, int8_t* dst, const float* scale, size_t src_depth_quad, size_t realSize, const float* bias, size_t pack); void MNNDynamicQuantFP16_Pack4(const float* src, int8_t* dst, const float* scale, size_t src_depth_quad, size_t realSize, const float* bias, size_t pack); void MNNGeneralIm2col_Arm82(float* destOrigin, float const** sourceGroup, const int32_t* info, const int32_t* el, int32_t LP, int32_t pack); void MNNGeneralIm2col_Arm86(float* destOrigin, float const** sourceGroup, const int32_t* info, const int32_t* el, int32_t LP, int32_t pack); #ifdef MNN_SME2 void MNNGeneralIm2col_Fp16Sme2(float* destOrigin, float const** sourceGroup, const int32_t* info, const int32_t* el, int32_t LP, int32_t pack); #endif void MNNLocalMinMaxFP16_Pack4(float* dstMin, float* dstMax, const float* source, size_t blockNum, size_t blockLU, size_t EP, size_t LP, size_t loadDstBuffer); void MNNLocalMinMaxFP16_Pack8(float* dstMin, float* dstMax, const float* source, size_t blockNum, size_t blockLU, size_t EP, size_t LP, size_t loadDstBuffer); #endif // MNN_LOW_MEMORY void CountMinMaxValue_FP16(float* source, float* minVal, float* maxVal, size_t sizeQuad); #ifdef MNN_SME2 void MNNPackedMatMulRemainFP16_SME2(float* C, const float* A, const float* B, size_t eSize, const size_t* parameter, const float* postParameters, const float* bias, const float* k, const float* b); #endif #endif #if defined(__aarch64__) void MNNDepthwiseConvFastKernelFP16(float* dst, const float* src, const float* weight, size_t width, size_t src_w_setup, size_t fw, size_t fh, size_t dilateX_step, size_t dilateY_step, size_t height, size_t srcHStep, size_t dstHStep, const float* bias, const float* parameters); #endif void MNNConvRunForLineDepthwiseFP16(float* dst, const float* src, const float* weight, size_t width, size_t src_w_setup, size_t fw, size_t fh, size_t dilateX_step, size_t dilateY_step, size_t height, size_t srcHStep, size_t dstHStep); // LinearAttention fp16 kernels void MNNRankOneUpdateFp16(float* S, const float* k, const float* delta, size_t dk, size_t dv); void MNNDualMatVecFp16(const float* S, const float* k, const float* q, float* out_k, float* out_q, size_t dk, size_t dv); void MNNDecayRankOneUpdateFp16(float* S, const float* k, const float* delta, float decay, size_t dk, size_t dv); } #if defined(__aarch64__) && defined(MNN_USE_NEON) // ────────────────────────────────────────────────────────────────────────── // MNNFusedGatedDeltaFp16 — FP16 specialization of the fused gated-delta-rule // kernel. See documentation in CommonOptFunction.h for the math. // // Pointers are typed `float*` for ABI uniformity with the FP32 version (the // dispatch table holds a single function pointer signature), but the // underlying memory is fp16. d_v is processed in chunks of 32 (= 4 v.8h // registers per accumulator) to keep all per-chunk state in registers, // which matters at d_v=128 where holding both out_k and out_q for the // whole row would otherwise exceed the 32 NEON v register budget. // ────────────────────────────────────────────────────────────────────────── static void MNNFusedGatedDeltaFp16(float* S_, const float* k_, const float* q_, const float* v_, float* out_, float decay, float beta, float kq, size_t dk, size_t dv) { auto S = reinterpret_cast<__fp16*>(S_); auto k = reinterpret_cast(k_); auto q = reinterpret_cast(q_); auto vIn = reinterpret_cast(v_); auto out = reinterpret_cast<__fp16*>(out_); const __fp16 decayH = static_cast<__fp16>(decay); const __fp16 betaH = static_cast<__fp16>(beta); const __fp16 kqH = static_cast<__fp16>(kq); const float16x8_t vDecay = vdupq_n_f16(decayH); const float16x8_t vBeta = vdupq_n_f16(betaH); const float16x8_t vKq = vdupq_n_f16(kqH); const size_t kChunk = 32; size_t j = 0; for (; j + kChunk <= dv; j += kChunk) { // ── Pass 1: out_k = S^T @ k, out_q = S^T @ q for this column chunk ── float16x8_t ok0 = vdupq_n_f16((__fp16)0), ok1 = vdupq_n_f16((__fp16)0), ok2 = vdupq_n_f16((__fp16)0), ok3 = vdupq_n_f16((__fp16)0); float16x8_t oq0 = vdupq_n_f16((__fp16)0), oq1 = vdupq_n_f16((__fp16)0), oq2 = vdupq_n_f16((__fp16)0), oq3 = vdupq_n_f16((__fp16)0); size_t i = 0; // Unroll i by 8: load 8 k & q scalars at once, then use fma-by-lane // to amortize the scalar broadcast across 8 row iterations. for (; i + 8 <= dk; i += 8) { float16x8_t kVec = vld1q_f16(k + i); float16x8_t qVec = vld1q_f16(q + i); #define LANE_STEP(lane) \ { \ const __fp16* row = S + (i + (lane)) * dv + j; \ float16x8_t s0 = vld1q_f16(row); \ float16x8_t s1 = vld1q_f16(row + 8); \ float16x8_t s2 = vld1q_f16(row + 16); \ float16x8_t s3 = vld1q_f16(row + 24); \ ok0 = vfmaq_laneq_f16(ok0, s0, kVec, (lane)); \ ok1 = vfmaq_laneq_f16(ok1, s1, kVec, (lane)); \ ok2 = vfmaq_laneq_f16(ok2, s2, kVec, (lane)); \ ok3 = vfmaq_laneq_f16(ok3, s3, kVec, (lane)); \ oq0 = vfmaq_laneq_f16(oq0, s0, qVec, (lane)); \ oq1 = vfmaq_laneq_f16(oq1, s1, qVec, (lane)); \ oq2 = vfmaq_laneq_f16(oq2, s2, qVec, (lane)); \ oq3 = vfmaq_laneq_f16(oq3, s3, qVec, (lane)); \ } LANE_STEP(0); LANE_STEP(1); LANE_STEP(2); LANE_STEP(3); LANE_STEP(4); LANE_STEP(5); LANE_STEP(6); LANE_STEP(7); #undef LANE_STEP } // Tail rows (dk % 8) — fall back to the broadcast form. for (; i < dk; ++i) { const __fp16* row = S + i * dv + j; float16x8_t s0 = vld1q_f16(row); float16x8_t s1 = vld1q_f16(row + 8); float16x8_t s2 = vld1q_f16(row + 16); float16x8_t s3 = vld1q_f16(row + 24); __fp16 ki = k[i]; __fp16 qi = q[i]; ok0 = vfmaq_n_f16(ok0, s0, ki); ok1 = vfmaq_n_f16(ok1, s1, ki); ok2 = vfmaq_n_f16(ok2, s2, ki); ok3 = vfmaq_n_f16(ok3, s3, ki); oq0 = vfmaq_n_f16(oq0, s0, qi); oq1 = vfmaq_n_f16(oq1, s1, qi); oq2 = vfmaq_n_f16(oq2, s2, qi); oq3 = vfmaq_n_f16(oq3, s3, qi); } // ── Inline analytic correction (regs only) ── float16x8_t v0 = vld1q_f16(vIn + j); float16x8_t v1 = vld1q_f16(vIn + j + 8); float16x8_t v2 = vld1q_f16(vIn + j + 16); float16x8_t v3 = vld1q_f16(vIn + j + 24); // delta = beta * (v - decay * out_k) float16x8_t d0 = vmulq_f16(vBeta, vsubq_f16(v0, vmulq_f16(vDecay, ok0))); float16x8_t d1 = vmulq_f16(vBeta, vsubq_f16(v1, vmulq_f16(vDecay, ok1))); float16x8_t d2 = vmulq_f16(vBeta, vsubq_f16(v2, vmulq_f16(vDecay, ok2))); float16x8_t d3 = vmulq_f16(vBeta, vsubq_f16(v3, vmulq_f16(vDecay, ok3))); // out = decay * out_q + kq * delta float16x8_t o0 = vfmaq_f16(vmulq_f16(vDecay, oq0), vKq, d0); float16x8_t o1 = vfmaq_f16(vmulq_f16(vDecay, oq1), vKq, d1); float16x8_t o2 = vfmaq_f16(vmulq_f16(vDecay, oq2), vKq, d2); float16x8_t o3 = vfmaq_f16(vmulq_f16(vDecay, oq3), vKq, d3); vst1q_f16(out + j, o0); vst1q_f16(out + j + 8, o1); vst1q_f16(out + j + 16, o2); vst1q_f16(out + j + 24, o3); // ── Pass 2: S = decay * S + k ⊗ delta (delta still in regs) ── size_t i2 = 0; for (; i2 + 8 <= dk; i2 += 8) { float16x8_t kVec = vld1q_f16(k + i2); #define ROW_UPDATE(lane) \ { \ __fp16* row = S + (i2 + (lane)) * dv + j; \ float16x8_t s0 = vld1q_f16(row); \ float16x8_t s1 = vld1q_f16(row + 8); \ float16x8_t s2 = vld1q_f16(row + 16); \ float16x8_t s3 = vld1q_f16(row + 24); \ float16x8_t r0 = vfmaq_laneq_f16(vmulq_f16(vDecay, s0), d0, kVec, (lane)); \ float16x8_t r1 = vfmaq_laneq_f16(vmulq_f16(vDecay, s1), d1, kVec, (lane)); \ float16x8_t r2 = vfmaq_laneq_f16(vmulq_f16(vDecay, s2), d2, kVec, (lane)); \ float16x8_t r3 = vfmaq_laneq_f16(vmulq_f16(vDecay, s3), d3, kVec, (lane)); \ vst1q_f16(row, r0); \ vst1q_f16(row + 8, r1); \ vst1q_f16(row + 16, r2); \ vst1q_f16(row + 24, r3); \ } ROW_UPDATE(0); ROW_UPDATE(1); ROW_UPDATE(2); ROW_UPDATE(3); ROW_UPDATE(4); ROW_UPDATE(5); ROW_UPDATE(6); ROW_UPDATE(7); #undef ROW_UPDATE } for (; i2 < dk; ++i2) { __fp16* row = S + i2 * dv + j; float16x8_t s0 = vld1q_f16(row); float16x8_t s1 = vld1q_f16(row + 8); float16x8_t s2 = vld1q_f16(row + 16); float16x8_t s3 = vld1q_f16(row + 24); __fp16 ki = k[i2]; float16x8_t r0 = vfmaq_n_f16(vmulq_f16(vDecay, s0), d0, ki); float16x8_t r1 = vfmaq_n_f16(vmulq_f16(vDecay, s1), d1, ki); float16x8_t r2 = vfmaq_n_f16(vmulq_f16(vDecay, s2), d2, ki); float16x8_t r3 = vfmaq_n_f16(vmulq_f16(vDecay, s3), d3, ki); vst1q_f16(row, r0); vst1q_f16(row + 8, r1); vst1q_f16(row + 16, r2); vst1q_f16(row + 24, r3); } } // ── Tail (chunks of 8) ── for (; j + 8 <= dv; j += 8) { float16x8_t ok = vdupq_n_f16((__fp16)0); float16x8_t oq = vdupq_n_f16((__fp16)0); for (size_t i = 0; i < dk; ++i) { float16x8_t s = vld1q_f16(S + i * dv + j); ok = vfmaq_n_f16(ok, s, k[i]); oq = vfmaq_n_f16(oq, s, q[i]); } float16x8_t vv = vld1q_f16(vIn + j); float16x8_t d = vmulq_f16(vBeta, vsubq_f16(vv, vmulq_f16(vDecay, ok))); float16x8_t o = vfmaq_f16(vmulq_f16(vDecay, oq), vKq, d); vst1q_f16(out + j, o); for (size_t i = 0; i < dk; ++i) { float16x8_t s = vld1q_f16(S + i * dv + j); float16x8_t r = vfmaq_n_f16(vmulq_f16(vDecay, s), d, k[i]); vst1q_f16(S + i * dv + j, r); } } // ── Scalar tail (defensive; d_v < multiple of 8 not used in current models) ── for (; j < dv; ++j) { float ok = 0.0f, oq = 0.0f; for (size_t i = 0; i < dk; ++i) { float s = (float)S[i * dv + j]; ok += s * (float)k[i]; oq += s * (float)q[i]; } float delta_j = beta * ((float)vIn[j] - decay * ok); out[j] = (__fp16)(decay * oq + kq * delta_j); for (size_t i = 0; i < dk; ++i) { float s = (float)S[i * dv + j]; S[i * dv + j] = (__fp16)(decay * s + (float)k[i] * delta_j); } } } #endif // __aarch64__ && MNN_USE_NEON namespace MNN { #define FP16_SME2_MATMUL_EP 16 #define FP16_SME2_MATMUL_LP 2 #define FP16_SME2_MATMUL_HP 64 static void Sme2MNNGetMatMulPackMode(int* eP, int *lP, int* hP) { *hP = FP16_SME2_MATMUL_HP; *eP = FP16_SME2_MATMUL_EP; *lP = FP16_SME2_MATMUL_LP; } static void MNNMatrixAddFP16(FLOAT16* C, const FLOAT16* A, const FLOAT16* B, size_t widthC8, size_t cStride, size_t aStride, size_t bStride, size_t height) { for (int y = 0; y < height; ++y) { auto a = A + aStride * y, b = B + bStride * y; auto c = C + cStride * y; for (int x = 0; x < widthC8; ++x) { vst1q_f16(c + x * 8, vaddq_f16(vld1q_f16(a + x * 8), vld1q_f16(b + x * 8))); } } } static void MNNMatrixSubFP16(FLOAT16* C, const FLOAT16* A, const FLOAT16* B, size_t widthC8, size_t cStride, size_t aStride, size_t bStride, size_t height) { for (int y = 0; y < height; ++y) { auto a = A + aStride * y, b = B + bStride * y; auto c = C + cStride * y; for (int x = 0; x < widthC8; ++x) { vst1q_f16(c + x * 8, vsubq_f16(vld1q_f16(a + x * 8), vld1q_f16(b + x * 8))); } } } #if defined(__aarch64__) static void ARM82CountMinMaxValue(float* source, float* minVal, float* maxVal, size_t size) { if (size % 8 == 0) { CountMinMaxValue_FP16(source, minVal, maxVal, size / 8); } else { auto remain = size - 8 * (size / 8); auto max_ = ((__fp16*)source)[0]; auto min_ = max_; if (size >= 8) { CountMinMaxValue_FP16(source, minVal, maxVal, size / 8); max_ = ((__fp16*)maxVal)[0]; min_ = ((__fp16*)minVal)[0]; } auto srcPtr = reinterpret_cast<__fp16*>(source); while (remain) { max_ = ALIMAX(srcPtr[0], max_); min_ = ALIMIN(srcPtr[0], min_); srcPtr += 1; remain--; } reinterpret_cast<__fp16*>(minVal)[0] = min_; reinterpret_cast<__fp16*>(maxVal)[0] = max_; } } #ifdef MNN_SME2 //(float* C, const float* A, const float* B, const size_t* parameter, const float* postParameters, const float* bias) static void MNNPackedMatMulFP16_SME2(float* C, const float* A, const float* B, const size_t* parameter, const float* postParameters, const float* bias, const float* k, const float* b) { MNNPackedMatMulRemainFP16_SME2(C, A, B, 16, parameter, postParameters, bias, k, b); } #endif #else static void ARM82CountMinMaxValue(float* source, float* minVal, float* maxVal, size_t size) { auto srcPtr = (FLOAT16*)source; auto minPtr = (FLOAT16*)minVal; auto maxPtr = (FLOAT16*)maxVal; auto max_ = srcPtr[0], min_ = srcPtr[0]; for (int i = 1; i < size; ++i) { if (max_ < srcPtr[i]) { max_ = srcPtr[i]; } if (min_ > srcPtr[i]) { min_ = srcPtr[i]; } } minPtr[0] = min_; maxPtr[0] = max_; } #endif static void Arm82MNNPackForMatMul_B(float* destC, const float* sourceC, size_t h, size_t kernelsize, size_t ic, bool transpose) { auto l = kernelsize * ic; auto dest = (int16_t*)destC; auto source = (int16_t*)sourceC; int ePack, lPack, hPack; Arm82MNNGetMatMulPackMode(&ePack, &lPack, &hPack); auto hP = (int)h / hPack; auto hR = (int)hP * hPack; if (hR != h) { ::memset(dest, 0, UP_DIV(h, hPack) * hPack * l * sizeof(FLOAT16)); } if (!transpose) { for (int y = 0; y < hP; ++y) { auto destY = dest + y * hPack * l; auto sourceY = source + y * hPack; for (int x = 0; x < l; ++x) { ::memcpy(destY + hPack * x, sourceY + x * h, hPack * sizeof(FLOAT16)); } } auto hRemain = h - hR; if (hRemain > 0) { auto destY = dest + hP * hPack * l; auto sourceY = source + hP * hPack; for (int x = 0; x < l; ++x) { ::memcpy(destY + hPack * x, sourceY + x * h, hRemain * sizeof(FLOAT16)); } } return; } for (int y = 0; y < h; ++y) { for (int x = 0; x < l; ++x) { dest[(y / hPack * l + x) * hPack + y % hPack] = source[y * l + x]; } } } static void Sme2MNNPackForMatMul_B(float* destC, const float* sourceC, size_t h, size_t kernelsize, size_t ic ,bool transpose) { auto dest = (int16_t*)destC; auto source = (int16_t*)sourceC; int LP = FP16_SME2_MATMUL_LP; int HP = FP16_SME2_MATMUL_HP; auto l = kernelsize * ic; memset(dest, 0, ROUND_UP(h, HP) * ROUND_UP(ic, LP) * kernelsize * sizeof(FLOAT16)); auto stride0 = ROUND_UP(ic, LP) * kernelsize * HP; auto stride1 = HP * ROUND_UP(ic, LP); auto stride2 = HP * LP; size_t srcStride0 = l; // [h,k2,ic]->[hu,k2,ic/lp,hp,lp] size_t srcStride1 = 1; if (!transpose) { // [k2,ic,h]->[hu,k2,ic/lp,hp,lp] srcStride0 = 1; srcStride1 = h; } for (int y = 0; y < h; ++y) { auto yHu = y / HP; auto yHp = y % HP; for (int k = 0; k < kernelsize; ++k) { for (int x = 0; x < ic; ++x) { auto xLu = x / LP; auto xLp = x % LP; dest[yHu * stride0 + k * stride1 + xLu * stride2 + yHp * LP + xLp] = source[y * srcStride0 + (x + k * ic) * srcStride1]; } } } } static void MNNScaleAndAddBiasFP16(FLOAT16* dst, const FLOAT16* src, const FLOAT16* bias, const FLOAT16* alpha, size_t planeNumber, size_t biasNumber) { for (int z = 0; z < biasNumber; ++z) { FLOAT16* dstZ = dst + planeNumber * 8 * z; const FLOAT16* srcZ = src + planeNumber * 8 * z; auto biasZ = vld1q_f16(bias + 8 * z), alphaZ = vld1q_f16(alpha + 8 * z); for (int p = 0; p < planeNumber; ++p) { FLOAT16* dstX = dstZ + 8 * p; const FLOAT16* srcX = srcZ + 8 * p; auto res = vaddq_f16(vmulq_f16(vld1q_f16(srcX), alphaZ), biasZ); vst1q_f16(dstX, res); } } } static void MNNGridSampleComputeCordFP16(FLOAT16* dst, const FLOAT16* src, size_t inH, size_t inW, size_t outH, size_t outW, size_t stride, bool alignCorners) { float16x8_t zero = vdupq_n_f16(0); float16x8_t one = vdupq_n_f16(1); float16x8_t half = vdupq_n_f16(0.5f); float16x8_t a = alignCorners ? one : zero; float16x8_t b = alignCorners ? zero : one; float16x8_t inW_sub_a = vsubq_f16(vdupq_n_f16(inW), a); float16x8_t inH_sub_a = vsubq_f16(vdupq_n_f16(inH), a); int area = outH * outW; int areaC8 = area / 8; int areaRemain = area - areaC8 * 8; for (int i = 0; i < areaC8; ++i) { auto cordH = vld2q_f16(src); // float16x8_t x = cordH.val[0]; // float16x8_t y = cordH.val[1]; cordH.val[0] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[0]), inW_sub_a), b)); cordH.val[1] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[1]), inH_sub_a), b)); vst2q_f16(dst, cordH); src += 16; dst += 16; } if (areaRemain == 0) { return; } // areaRemain FLOAT16 tempDst[16]; ::memcpy(tempDst, src, areaRemain * 2 * sizeof(int16_t)); auto cordH = vld2q_f16(tempDst); cordH.val[0] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[0]), inW_sub_a), b)); cordH.val[1] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[1]), inH_sub_a), b)); vst2q_f16(tempDst, cordH); ::memcpy(dst, tempDst, areaRemain * 2 * sizeof(int16_t)); } static void MNNGridSampleComputeCord3DFp16(FLOAT* dst, const FLOAT* src, size_t inD, size_t inH, size_t inW, size_t outD, size_t outH, size_t outW, bool alignCorners) { float16x8_t zero = vdupq_n_f16(0); float16x8_t one = vdupq_n_f16(1); float16x8_t half = vdupq_n_f16(0.5f); float16x8_t a = alignCorners ? one : zero; float16x8_t b = alignCorners ? zero : one; float16x8_t inW_sub_a = vsubq_f16(vdupq_n_f16(inW), a); float16x8_t inH_sub_a = vsubq_f16(vdupq_n_f16(inH), a); float16x8_t inD_sub_a = vsubq_f16(vdupq_n_f16(inD), a); size_t area = outH * outW * outD; size_t areaC8 = area / 8; size_t areaRemain = area - areaC8 * 8; for (int i = 0; i < areaC8; ++i) { auto cordH = vld3q_f16(src); // float16x8_t x = cordH.val[0]; // float16x8_t y = cordH.val[1]; cordH.val[0] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[0]), inW_sub_a), b)); cordH.val[1] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[1]), inH_sub_a), b)); cordH.val[2] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[2]), inD_sub_a), b)); vst3q_f16(dst, cordH); src += 24; dst += 24; } if (areaRemain == 0) { return; } // areaRemain FLOAT16 tempDst[24]; ::memcpy(tempDst, src, areaRemain * 3 * sizeof(int16_t)); auto cordH = vld3q_f16(tempDst); cordH.val[0] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[0]), inW_sub_a), b)); cordH.val[1] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[1]), inH_sub_a), b)); cordH.val[2] = vmulq_f16(half, vsubq_f16(vmulq_f16(vaddq_f16(one, cordH.val[2]), inD_sub_a), b)); vst3q_f16(tempDst, cordH); ::memcpy(dst, tempDst, areaRemain * 3 * sizeof(int16_t)); } static void MNNRoiPoolingMaxFP16(FLOAT16* dst, const FLOAT16* src, int hLen, int wLen, int iw) { Vec max = Vec(-65504.0f); for (int h = 0; h < hLen; h++, src += iw * 8) { for (int w = 0; w < wLen; w++) { Vec in = Vec::load(src + w * 8); max = Vec::max(max, in); } } Vec::save(dst, max); } static void MNNRoiAlignMaxFP16(FLOAT16* dst, const FLOAT16* src, const std::vector> &vecPos, const std::vector> &vecArea, int samplingRatioArea, int pooledHeight, int pooledWidth) { for (int h = 0; h < pooledHeight; ++h, dst += pooledWidth * 8) { int preCalcIdx = h * pooledWidth * samplingRatioArea; for (int w = 0; w < pooledWidth; ++w) { Vec res = Vec(-65504.0f); for (int i = 0; i < samplingRatioArea; ++i) { const std::vector& pos = vecPos[preCalcIdx]; const std::vector& area = vecArea[preCalcIdx]; Vec val0 = Vec::load(src + pos[0] * 8); Vec val1 = Vec::load(src + pos[1] * 8); Vec val2 = Vec::load(src + pos[2] * 8); Vec val3 = Vec::load(src + pos[3] * 8); Vec mla = val0 * area[0]; mla = Vec::fma(mla, val1, area[1]); mla = Vec::fma(mla, val2, area[2]); mla = Vec::fma(mla, val3, area[3]); res = Vec::max(res, mla); preCalcIdx++; } Vec::save(dst + w * 8, res); } } } static void MNNRoiAlignAvgFP16(FLOAT16* dst, const FLOAT16* src, const std::vector> &vecPos, const std::vector> &vecArea, int samplingRatioArea, int pooledHeight, int pooledWidth) { float invSamplingCnt = 1.f / samplingRatioArea; for (int h = 0; h < pooledHeight; ++h, dst += pooledWidth * 8) { int preCalcIdx = h * pooledWidth * samplingRatioArea; for (int w = 0; w < pooledWidth; ++w) { Vec res = Vec(0.f); for (int i = 0; i < samplingRatioArea; ++i) { const std::vector& pos = vecPos[preCalcIdx]; const std::vector& area = vecArea[preCalcIdx]; Vec val0 = Vec::load(src + pos[0] * 8); Vec val1 = Vec::load(src + pos[1] * 8); Vec val2 = Vec::load(src + pos[2] * 8); Vec val3 = Vec::load(src + pos[3] * 8); Vec mla = val0 * area[0]; mla = Vec::fma(mla, val1, area[1]); mla = Vec::fma(mla, val2, area[2]); mla = Vec::fma(mla, val3, area[3]); res += mla; preCalcIdx++; } res = res * invSamplingCnt; Vec::save(dst + w * 8, res); } } } static void MNNCopyC8WithStrideFP16(const FLOAT16* source, FLOAT16* dest, size_t srcStride, size_t dstStride, size_t count) { using Vec = MNN::Math::Vec; for (int i = 0; i < count; ++i) { auto srcPtr = source + i * srcStride; auto dstPtr = dest + i * dstStride; Vec::save(dstPtr, Vec::load(srcPtr)); } } static void MNNAddC8WithStrideFP16(const FLOAT16* source, FLOAT16* dest, size_t srcStride, size_t dstStride, size_t count) { for (int i = 0; i < count; ++i) { auto srcPtr = source + i * srcStride; auto dstPtr = dest + i * dstStride; auto value = Vec::load(dstPtr) + Vec::load(srcPtr); Vec::save(dstPtr, value); } } static void MNNAxByClampBroadcastC8FP16(float* CF, const float* AF, const float* BF, size_t width, size_t cStride, size_t aStride, size_t height, const float* parameters) { auto C = (FLOAT16*)CF; auto A = (FLOAT16*)AF; auto B = (FLOAT16*)BF; using Vec = MNN::Math::Vec; auto minF = Vec(parameters[2]); auto maxF = Vec(parameters[3]); auto beta = Vec(parameters[1]); for (int y = 0; y < height; ++y) { auto a = A + aStride * y; auto b = B + 8 * y; auto bv = Vec::load(b); auto c = C + cStride * y; for (int x = 0; x < width; ++x) { auto av = Vec::load(a + 8 * x); auto cv = av + bv * beta; cv = Vec::min(cv, maxF); cv = Vec::max(cv, minF); Vec::save(c + 8 * x, cv); } } } void ARM82StrassenMerge(FLOAT16* c11, FLOAT16* c12, FLOAT16* c21, FLOAT16* c22, FLOAT16* xAddr, size_t cStride, size_t eSub, size_t hSub) { const int pack = 8; for (int y = 0; y < hSub; ++y) { auto c11Y = c11 + y * cStride; auto c12Y = c12 + y * cStride; auto c22Y = c22 + y * cStride; auto c21Y = c21 + y * cStride; auto xY = xAddr + y * eSub * pack; for (int x = 0; x < eSub; ++x) { auto xv = vld1q_f16(xY + x * pack); auto c21v = vld1q_f16(c21Y + x * pack); auto c11v = vld1q_f16(c11Y + x * pack); auto c22v = vld1q_f16(c22Y + x * pack); auto c12v = vld1q_f16(c12Y + x * pack); c12v = c12v + xv; c21v = c12v + c21v; c12v = c22v + c12v; c22v = c22v + c21v; c12v = c11v + c12v; vst1q_f16(c12Y + x * pack, c12v); vst1q_f16(c22Y + x * pack, c22v); vst1q_f16(c21Y + x * pack, c21v); } } } void MNNUnpackTransposeInt16C8(int16_t* dst, const int16_t* src, size_t area, size_t depth, int32_t* areaOffset) { // [depth/8, srcAreaOffset, 8] -> [area, dstAreaOffset] int srcAreaOffset = areaOffset[0]; int dstAreaOffset = areaOffset[1]; int c = (int)depth; int cDiv8 = c / 8; int cAlign = cDiv8 * 8; int areaDiv4 = area / 4; int areaAlign = areaDiv4 * 4; if (areaAlign > 0) { for (int ci = 0; ci < cDiv8; ++ci) { auto srcH = src + ci * 8 * srcAreaOffset; auto dstH = dst + ci * 8; for (int hi = 0; hi < areaAlign; hi+=4) { auto src0 = srcH + hi * 8; auto src1 = srcH + hi * 8 + 8; auto src2 = srcH + hi * 8 + 16; auto src3 = srcH + hi * 8 + 24; auto dst0 = dstH + hi * dstAreaOffset; auto dst1 = dstH + hi * dstAreaOffset + dstAreaOffset; auto dst2 = dstH + hi * dstAreaOffset + 2 * dstAreaOffset; auto dst3 = dstH + hi * dstAreaOffset + 3 * dstAreaOffset; vst1q_s16(dst0, vld1q_s16(src0)); vst1q_s16(dst1, vld1q_s16(src1)); vst1q_s16(dst2, vld1q_s16(src2)); vst1q_s16(dst3, vld1q_s16(src3)); } } } if (areaAlign < area) { for (int ci = 0; ci < cDiv8; ++ci) { auto srcH = src + 8 * ci * srcAreaOffset; auto dstH = dst + ci * 8; for (int hi = areaAlign; hi < area; ++hi) { auto src0 = srcH + hi * 8; auto dst0 = dstH + hi * dstAreaOffset; vst1q_s16(dst0, vld1q_s16(src0)); } } } if (c == cAlign) { return; } int cReamin = c - cAlign; auto srcAlign = src + srcAreaOffset * cAlign; auto dstAlign = dst + cAlign; for (int hi = 0; hi < area; ++hi) { auto srcHeight = srcAlign + hi * 8; auto dstHeight = dstAlign + hi * dstAreaOffset; for (int ci = 0; ci < cReamin; ++ci) { dstHeight[ci] = srcHeight[ci]; } } } void MNNPackTransposeInt16C8(int16_t* dst, const int16_t* src, size_t area, size_t depth, int32_t* areaOffset) { if (depth == 8) { ::memcpy(dst, src, area * depth * sizeof(int16_t)); return; } int dstAreaOffset = areaOffset[1]; int c = (int)depth; int cDiv4 = c / 8; int cAlign = cDiv4 * 8; int areaDiv4 = area / 4; int areaAlign = areaDiv4 * 4; if (areaAlign > 0) { for (int ci = 0; ci < cDiv4; ++ci) { auto srcH = src + ci * 8; auto dstH = dst + ci * dstAreaOffset * 8; for (int hi = 0; hi < areaAlign; hi+=4) { auto src0 = srcH + hi * c; auto src1 = srcH + hi * c + c; auto src2 = srcH + hi * c + 2 * c; auto src3 = srcH + hi * c + 3 * c; auto dst0 = dstH + hi * 8; auto dst1 = dstH + hi * 8 + 8; auto dst2 = dstH + hi * 8 + 16; auto dst3 = dstH + hi * 8 + 24; vst1q_s16(dst0, vld1q_s16(src0)); vst1q_s16(dst1, vld1q_s16(src1)); vst1q_s16(dst2, vld1q_s16(src2)); vst1q_s16(dst3, vld1q_s16(src3)); } } } if (areaAlign < area) { for (int ci = 0; ci < cDiv4; ++ci) { auto srcH = src + ci * 8; auto dstH = dst + ci * dstAreaOffset * 8; for (int hi = areaAlign; hi < area; ++hi) { auto src0 = srcH + hi * c; auto dst0 = dstH + hi * 8; vst1q_s16(dst0, vld1q_s16(src0)); } } } if (cAlign == c) { return; } int cReamin = c - cAlign; auto srcAlign = src + cAlign; auto dstAlign = dst + dstAreaOffset * cAlign; for (int hi = 0; hi < area; ++hi) { auto srcHeight = srcAlign + hi * c; auto dstHeight = dstAlign + hi * 8; for (int i = 0; i < 8; ++i) { dstHeight[i] = 0; } for (int ci = 0; ci < cReamin; ++ci) { dstHeight[ci] = srcHeight[ci]; } } } static void _MNNDeconvRunForUnitDepthWise(const FLOAT16* dst, FLOAT16* src, const FLOAT16* weight, size_t fw, size_t fh, size_t weight_y_step, size_t dilateX_step, size_t dilateY_step) { int fx, fy; auto src_z = src; auto weight_z = weight; Vec dstV = Vec::load(dst); for (fy = 0; fy < fh; ++fy) { auto src_y = src_z + fy * dilateY_step; auto weight_y = weight_z + fy * weight_y_step; for (fx = 0; fx < fw; ++fx) { Vec weight_x = Vec::load(weight_y + 8 * fx); Vec src_x = Vec::load(src_y + fx * dilateX_step); Vec::save(src_y + fx * dilateX_step, src_x + weight_x * dstV); } } } static void _MNNDeconvRunForLineDepthwise(const FLOAT16* dst, FLOAT16* src, const FLOAT16* weight, size_t width, size_t src_w_setup, size_t fw, size_t fh, size_t dilateX_step, size_t dilateY_step) { int dx; for (dx = 0; dx < width; ++dx) { auto dst_x = dst + dx * 8; auto src_dx = src + src_w_setup * dx; _MNNDeconvRunForUnitDepthWise(dst_x, src_dx, weight, fw, fh, fw * 8, dilateX_step, dilateY_step); } } static void _MNNComputeMatMulForH_1_FP16(const float* AF, const float* BF, float* CF, const float* biasPtrF, const MatMulParam* param, size_t tId) { auto A = (const FLOAT16*)AF; auto B = (const FLOAT16*)BF; auto C = (FLOAT16*)CF; auto biasPtr = (const FLOAT16*)biasPtrF; int e = param->e; int l = param->l; int numberThread = param->numberThread; float biasValue = 0.0f; if (nullptr != biasPtr) { biasValue = biasPtr[0]; } if (param->ATranspose) { auto eC4 = e / 8; auto eR = e % 8; for (int y=tId; y 0) { Vec sumValue = Vec(biasValue); auto srcY = A + eC4 * 8; FLOAT16 AR[8]; for (int x=0; x 0) { FLOAT16 AR[8] = {0, 0, 0, 0, 0, 0, 0, 0}; FLOAT16 BR[8] = {0, 0, 0, 0, 0, 0, 0, 0}; ::memcpy(AR, srcY + lC4 * 8, lR * sizeof(int16_t)); ::memcpy(BR, B + 8 * lC4, lR * sizeof(int16_t)); sumValue = sumValue + Vec::load(AR) * Vec::load(BR); } float sumSingle = sumValue[0] + sumValue[1] + sumValue[2] + sumValue[3] + sumValue[4] + sumValue[5] + sumValue[6] + sumValue[7]; C[y] = sumSingle; } } static void _MNNComputeMatMulForE_1_FP16(const float* AF, const float* BF, float* CF, const float* biasPtrF, const MatMulParam* param, size_t tId) { auto l = param->l; auto h = param->h; auto numberThread = param->numberThread; auto lC4 = l / 8; auto lR = l % 8; auto A = (const FLOAT16*)AF; auto B = (const FLOAT16*)BF; auto C = (FLOAT16*)CF; auto biasPtr = (const FLOAT16*)biasPtrF; if (param->BTranspose) { for (int y=tId; y 0) { FLOAT16 AR[8] = {0, 0, 0, 0, 0, 0, 0, 0}; FLOAT16 BR[8] = {0, 0, 0, 0, 0, 0, 0, 0}; ::memcpy(AR, A + lC4 * 8, lR * sizeof(int16_t)); ::memcpy(BR, by + 8 * lC4, lR * sizeof(int16_t)); sumValue = sumValue + Vec::load(AR) * Vec::load(BR); } float sumRemain = sumValue[0] + sumValue[1] + sumValue[2] + sumValue[3] + sumValue[4] + sumValue[5] + sumValue[6] + sumValue[7]; if (nullptr != biasPtr) { sumRemain += biasPtr[y]; } C[y] = sumRemain; } } else { auto hC4 = h / 8; auto hR = h % 8; auto hC16 = hC4 / 4; auto hC4R = hC4 % 4; for (int y=tId; y 0) { auto bs = B + 8 * hC4; Vec sumValue = Vec(0.0f); if (biasPtr != nullptr) { FLOAT16 biasTemp[8]; ::memcpy(biasTemp, biasPtr + 8 * hC4, hR * sizeof(int16_t)); sumValue = Vec::load(biasTemp); } auto srcY = A + 8 * hC4 * l; FLOAT16 bTemp[8]; for (int x=0; x static void _Arm82MNNPackC4ForMatMul_A(int8_t* destOrigin, int8_t const** sourceGroup, const int32_t* info, const int32_t* el) { const int pack = 8; int number = info[0]; int eReal = info[1]; int xStride = info[3]; int xS4 = xStride * pack / sizeof(int32_t); int PUNIT = pack / LP; int FLOATPACK = pack / sizeof(int32_t); int eOutsideStride = info[2] / sizeof(int32_t); int eDest = EP; int realDstCount = info[4]; for (int n=0; n 0) { int jobsE = realDstCount - eOffset - e; if (jobsE == 0 || (jobsE < (realDstCount % EP))) { lastBag = true; } } auto source = (int32_t*)sourceGroup[n]; auto dest = (int32_t*)(destOrigin + eC * info[2] + eR * LP + lOffset * EP); //printf("e=%d, l=%d, eOffset=%d, lOffset=%d, eDest=%d\n", e, l, eOffset, lOffset, eDest); l = l / 4; // Use float instead of int8 * 4 if (lastBag && e + eR < EP) { int elast = ALIMAX(eR + e, realDstCount % EP); dest = (int32_t*)(destOrigin + lOffset * elast + eC * info[2] + eR * LP); } int offsetLC = lOffset / 4; for (int x = 0; x < l; ++x) { int eRemain = e; auto xR = x % PUNIT; auto xC = x / PUNIT; auto d = dest; auto s = source + xC * eReal * FLOATPACK + xR; if (eR > 0) { int eStep = ALIMIN(eRemain, eS); for (int yi=0; yi= EP) { d += (eOutsideStride - eR); } else { int eFill = ALIMAX(eRemain, realDstCount % EP); // maybe padding>0 eOutsideStride4LastBag = eOutsideStride - (EP * 4 * offsetLC / sizeof(float)); d += (eOutsideStride4LastBag - eR + offsetLC * eFill); } s += eS * xS4; } while (eRemain > 0) { int eStep = ALIMIN(eDest, eRemain); for (int yi=0; yi= EP) { d+= eOutsideStride; } else { int eFill = ALIMAX(eRemain, realDstCount % EP); // maybe padding>0 eOutsideStride4LastBag = eOutsideStride - (EP * 4 * offsetLC / sizeof(float)); d+= (eOutsideStride4LastBag + offsetLC * eFill); } s+= eStep * xS4; } if (lastBag && e + eR < EP) { int efill = ALIMAX(e + eR, realDstCount % EP); dest += efill; } else { dest += eDest; } offsetLC++; } } } template static void _ArmBasicMNNPackC4ForMatMul_A_L8(int8_t* destOrigin, int8_t const** sourceGroup, const int32_t* info, const int32_t* el) { int number = info[0]; int eReal = info[1]; int eDest = EP; int offset = info[3]; const int LP = 8; int eOutsideStride = info[2] / sizeof(int64_t); int realDstCount = info[4]; for (int n=0; n 0) { int jobsE = realDstCount - eOffset - e; if (jobsE == 0 || (jobsE < (realDstCount % EP))) { lastBag = true; } } auto dest = (int64_t*)(destOrigin + lOffset * eDest + eC * info[2] + eR * LP); auto source = (int64_t*)sourceGroup[n]; int lRemain = l / LP; if (lastBag && e + eR < EP) { int elast = ALIMIN(ALIMAX(eR + e, realDstCount % EP), EP); dest = (int64_t*)(destOrigin + lOffset * elast + eC * info[2] + eR * LP); } int offsetLC = lOffset / LP; for (int x = 0; x < lRemain; ++x) { int eRemain = e; auto d = dest; auto s = source; if (1 == offset) { if (eR > 0) { int eStep = ALIMIN(eRemain, eS); ::memcpy(d, s, eStep * sizeof(int64_t)); eRemain-=eStep; if (!lastBag ||eRemain >= EP) { d += (eOutsideStride - eR); } else { int eFill = ALIMAX(eRemain, realDstCount % EP); // maybe padding>0 eOutsideStride4LastBag = eOutsideStride - (EP * offsetLC); d += (eOutsideStride4LastBag - eR + offsetLC * eFill); } s += (eS * offset); } while (eRemain > 0) { int eStep = ALIMIN(eDest, eRemain); ::memcpy(d, s, eStep * sizeof(int64_t)); eRemain-=eStep; if (!lastBag || eRemain >= EP) { d+= eOutsideStride; } else { int eFill = ALIMAX(eRemain, realDstCount % EP); // maybe padding>0 eOutsideStride4LastBag = eOutsideStride - (EP * offsetLC); d+= (eOutsideStride4LastBag + offsetLC * eFill); } s+= (eStep * offset); } } else { if (eR > 0) { int eStep = ALIMIN(eRemain, eS); for (int yi=0; yi= EP) { d += (eOutsideStride - eR); } else { int eFill = ALIMAX(eRemain, realDstCount % EP); // maybe padding>0 eOutsideStride4LastBag = eOutsideStride - (EP * offsetLC); d += (eOutsideStride4LastBag - eR + offsetLC * eFill); } s += eS * offset; } while (eRemain > 0) { int eStep = ALIMIN(eDest, eRemain); for (int yi=0; yi= EP) { d+= eOutsideStride; } else { int eFill = ALIMAX(eRemain, realDstCount % EP); // maybe padding>0 eOutsideStride4LastBag = eOutsideStride - (EP * offsetLC); d+= (eOutsideStride4LastBag + offsetLC * eFill); } s+= eStep * offset; } } source += eReal; if (lastBag && e + eR < EP ) { // eR=0;eR>0 int efill = ALIMAX(e + eR, realDstCount % EP); dest += efill; } else { dest += eDest; } offsetLC++; } } } inline void transpose_4x4_f32(float32x4_t& r0, float32x4_t& r1, float32x4_t& r2, float32x4_t& r3) { // Stage 1: Transpose 2x2 blocks of float32 elements between pairs of adjacent rows. float32x4x2_t temp0 = vtrnq_f32(r0, r1); float32x4x2_t temp1 = vtrnq_f32(r2, r3); // Intermediate state: // temp0.val[0] = [A0, B0, A2, B2] // temp0.val[1] = [A1, B1, A3, B3] // temp1.val[0] = [C0, D0, C2, D2] // temp1.val[1] = [C1, D1, C3, D3] // Stage 2: Manually swap the 64-bit blocks to finalize the transpose. // This correctly simulates the non-existent 64-bit transpose/zip. float64x2_t i0_f64 = vreinterpretq_f64_f32(temp0.val[0]); float64x2_t i1_f64 = vreinterpretq_f64_f32(temp0.val[1]); float64x2_t i2_f64 = vreinterpretq_f64_f32(temp1.val[0]); float64x2_t i3_f64 = vreinterpretq_f64_f32(temp1.val[1]); // Combine the low 64 bits of i0 and i2 to form the first part of the result. float32x4_t t0 = vreinterpretq_f32_f64(vcombine_f64(vget_low_f64(i0_f64), vget_low_f64(i2_f64))); // Combine the low 64 bits of i1 and i3 for the second part. float32x4_t t1 = vreinterpretq_f32_f64(vcombine_f64(vget_low_f64(i1_f64), vget_low_f64(i3_f64))); // Combine the high 64 bits of i0 and i2 for the third part. float32x4_t t2 = vreinterpretq_f32_f64(vcombine_f64(vget_high_f64(i0_f64), vget_high_f64(i2_f64))); // Combine the high 64 bits of i1 and i3 for the final part. float32x4_t t3 = vreinterpretq_f32_f64(vcombine_f64(vget_high_f64(i1_f64), vget_high_f64(i3_f64))); r0 = t0; r1 = t1; r2 = t2; r3 = t3; } static void Sme2MNNPackC4ForMatMul_A_FP16(float* destOrigin, float const** sourceGroup, const int32_t* info, const int32_t* el) { const int lP = FP16_SME2_MATMUL_LP; const int pack = 8; int number = info[0]; int eReal = info[1]; int eDest = info[2]; int offset = info[3]; float32x4_t v0, v1, v2, v3, v4, v5, v6, v7; for (int n = 0; n < number; ++n) { int e = el[4 * n + 0]; int l = el[4 * n + 1]; int eOffset = el[4 * n + 2]; int lOffset = el[4 * n + 3]; auto destBase = (FLOAT16*)destOrigin + lOffset * eDest + eOffset * lP; auto sourceBase = (const FLOAT16*)(sourceGroup[n]); const int eTile = 8; const int lTile = 8; const int eMain = e / eTile; const int lMain = l / lTile; const size_t srcRowStride = (size_t)pack * offset; const size_t srcColBlockStride = (size_t)eReal * pack; const size_t dstColBlockStride = (size_t)eDest * lP; for (int y0 = 0; y0 < eMain; ++y0) { const int yBase = y0 * eTile; for (int x0 = 0; x0 < lMain; ++x0) { const int xBase = x0 * lTile; const auto srcBlockBase = sourceBase + yBase * srcRowStride + x0 * srcColBlockStride; v0 = vld1q_f32((const float*)(srcBlockBase + 0 * srcRowStride)); v1 = vld1q_f32((const float*)(srcBlockBase + 1 * srcRowStride)); v2 = vld1q_f32((const float*)(srcBlockBase + 2 * srcRowStride)); v3 = vld1q_f32((const float*)(srcBlockBase + 3 * srcRowStride)); v4 = vld1q_f32((const float*)(srcBlockBase + 4 * srcRowStride)); v5 = vld1q_f32((const float*)(srcBlockBase + 5 * srcRowStride)); v6 = vld1q_f32((const float *)(srcBlockBase + 6 * srcRowStride)); v7 = vld1q_f32((const float *)(srcBlockBase + 7 * srcRowStride)); transpose_4x4_f32(v0, v1, v2, v3); transpose_4x4_f32(v4, v5, v6, v7); float* addr0 = (float*)(destBase + yBase * lP + (xBase / lP) * dstColBlockStride); float* addr1= (float*)(destBase + yBase * lP + (xBase / lP + 1) * dstColBlockStride); float* addr2= (float*)(destBase + yBase * lP + (xBase / lP + 2) * dstColBlockStride); float* addr3= (float*)(destBase + yBase * lP + (xBase / lP + 3) * dstColBlockStride); vst1q_f32(addr0, v0); vst1q_f32(addr0 + 4, v4); vst1q_f32(addr1, v1); vst1q_f32(addr1 + 4, v5); vst1q_f32(addr2, v2); vst1q_f32(addr2 + 4, v6); vst1q_f32(addr3, v3); vst1q_f32(addr3 + 4, v7); } } const int eHandled = eMain * eTile; const int lHandled = lMain * lTile; // Process remaining rows for (int y = eHandled; y < e; ++y) { int yR = y % eDest; for (int x = 0; x < l; ++x) { int xR = x % pack; int xC = x / pack; destBase[(x / lP) * dstColBlockStride + yR * lP + (x % lP)] = sourceBase[xC * srcColBlockStride + y * srcRowStride + xR]; } } // Process remaining columns for the already handled rows for (int y = 0; y < eHandled; ++y) { int yR = y % eDest; for (int x = lHandled; x < l; ++x) { int xR = x % pack; int xC = x / pack; destBase[(x / lP) * dstColBlockStride + yR * lP + (x % lP)] = sourceBase[xC * srcColBlockStride + y * srcRowStride + xR]; } } } } #ifdef MNN_SUPPORT_TRANSFORMER_FUSE static void MNNAttenPackAndScaleSingleHead(float* dst, const float* srcHeadBase, size_t srcRowStride, const float* scale, const int32_t* units, size_t seqLen, size_t headDim) { const int32_t eP = units[0]; const int32_t lP = units[1]; if (lP != 1 && lP != 2) { MNN_ERROR("This function only supports lP=1 or 2\n"); return; } const float scaleVal = scale[0]; const float16x8_t vScale = vdupq_n_f16(scaleVal); const size_t packedHeadDim = UP_DIV(headDim, lP); const size_t dstStrideDOuter = (size_t)eP * lP; const size_t dstStrideSOuter = packedHeadDim * dstStrideDOuter; for (int s = 0; s < seqLen; ++s) { const int sOuter = s / eP; const int sInner = s % eP; const FLOAT16* srcRowPtr = (FLOAT16*)srcHeadBase + s * srcRowStride; FLOAT16* dstBasePtr = (FLOAT16*)dst + sOuter * dstStrideSOuter + sInner * lP; if (lP == 1) { size_t d = 0; for (; d + 7 < headDim; d += 8) { float16x8_t sVec = vld1q_f16(srcRowPtr + d); sVec = vmulq_f16(sVec, vScale); dstBasePtr[(d + 0) * dstStrideDOuter] = sVec[0]; dstBasePtr[(d + 1) * dstStrideDOuter] = sVec[1]; dstBasePtr[(d + 2) * dstStrideDOuter] = sVec[2]; dstBasePtr[(d + 3) * dstStrideDOuter] = sVec[3]; dstBasePtr[(d + 4) * dstStrideDOuter] = sVec[4]; dstBasePtr[(d + 5) * dstStrideDOuter] = sVec[5]; dstBasePtr[(d + 6) * dstStrideDOuter] = sVec[6]; dstBasePtr[(d + 7) * dstStrideDOuter] = sVec[7]; } for (; d < headDim; ++d) { dstBasePtr[d * dstStrideDOuter] = srcRowPtr[d] * scaleVal; } } else { // lP == 2 const FLOAT16* srcDPtr = srcRowPtr; FLOAT16* dstDPtr = dstBasePtr; size_t dRealSize = headDim; while (dRealSize >= 16) { float16x8_t s0 = vld1q_f16(srcDPtr); float16x8_t s1 = vld1q_f16(srcDPtr + 8); s0 = vmulq_f16(s0, vScale); s1 = vmulq_f16(s1, vScale); float16x4_t lowS0_f16 = vget_low_f16(s0); // {s0, s1, s2, s3} float16x4_t highS0_f16 = vget_high_f16(s0); // {s4, s5, s6, s7} uint32x2_t lowS0_u32 = vreinterpret_u32_f16(lowS0_f16); uint32x2_t highS0_u32 = vreinterpret_u32_f16(highS0_f16); *((uint32_t*)(dstDPtr + 0 * dstStrideDOuter)) = vget_lane_u32(lowS0_u32, 0); // Store pair {s0, s1} *((uint32_t*)(dstDPtr + 1 * dstStrideDOuter)) = vget_lane_u32(lowS0_u32, 1); // Store pair {s2, s3} *((uint32_t*)(dstDPtr + 2 * dstStrideDOuter)) = vget_lane_u32(highS0_u32, 0); // Store pair {s4, s5} *((uint32_t*)(dstDPtr + 3 * dstStrideDOuter)) = vget_lane_u32(highS0_u32, 1); // Store pair {s6, s7} float16x4_t lowS1_f16 = vget_low_f16(s1); // {s8, s9, s10, s11} float16x4_t highS1_f16 = vget_high_f16(s1); // {s12, s13, s14, s15} uint32x2_t lowS1_u32 = vreinterpret_u32_f16(lowS1_f16); uint32x2_t highS1_u32 = vreinterpret_u32_f16(highS1_f16); *((uint32_t*)(dstDPtr + 4 * dstStrideDOuter)) = vget_lane_u32(lowS1_u32, 0); *((uint32_t*)(dstDPtr + 5 * dstStrideDOuter)) = vget_lane_u32(lowS1_u32, 1); *((uint32_t*)(dstDPtr + 6 * dstStrideDOuter)) = vget_lane_u32(highS1_u32, 0); *((uint32_t*)(dstDPtr + 7 * dstStrideDOuter)) = vget_lane_u32(highS1_u32, 1); dRealSize -= 16; srcDPtr += 16; dstDPtr += 8 * dstStrideDOuter; } // Remainder loop with padding while (dRealSize > 0) { if (dRealSize >= 2) { dstDPtr[0] = srcDPtr[0] * scaleVal; dstDPtr[1] = srcDPtr[1] * scaleVal; dRealSize -= 2; srcDPtr += 2; dstDPtr += dstStrideDOuter; } else { // dRealSize == 1 dstDPtr[0] = srcDPtr[0] * scaleVal; dstDPtr[1] = (FLOAT16)0.0f; // Pad with zero dRealSize = 0; } } } } } static void MNNFlashAttentionUpdateBlockOutput(float* dst, float* src, float* scale, float* normalizeScale, int depthQuad, int plane, int pack, int idx, int kvBlocks, int size, int bytes, int seqStart) { auto dstPtr = (float16_t*)dst; auto srcPtr = (float16_t*)src; const auto stride0 = plane * pack; if (idx == 0) { memcpy(dst, src, size * bytes); } else { for (int j = 0; j < depthQuad; ++j) { const auto baseOffset = j * stride0; int i = seqStart; for (; i + 4 < plane; i += 4) { auto pdst0 = dstPtr + baseOffset + (i + 0) * pack; auto psrc0 = srcPtr + baseOffset + (i + 0) * pack; auto pdst1 = dstPtr + baseOffset + (i + 1) * pack; auto psrc1 = srcPtr + baseOffset + (i + 1) * pack; auto pdst2 = dstPtr + baseOffset + (i + 2) * pack; auto psrc2 = srcPtr + baseOffset + (i + 2) * pack; auto pdst3 = dstPtr + baseOffset + (i + 3) * pack; auto psrc3 = srcPtr + baseOffset + (i + 3) * pack; float16x8_t src0 = vld1q_f16(psrc0); float16x8_t dst0 = vld1q_f16(pdst0); float16x8_t src1 = vld1q_f16(psrc1); float16x8_t dst1 = vld1q_f16(pdst1); float16x8_t src2 = vld1q_f16(psrc2); float16x8_t dst2 = vld1q_f16(pdst2); float16x8_t src3 = vld1q_f16(psrc3); float16x8_t dst3 = vld1q_f16(pdst3); float32x4_t svec0 = vdupq_n_f32(scale[i + 0]); float32x4_t svec1 = vdupq_n_f32(scale[i + 1]); float32x4_t svec2 = vdupq_n_f32(scale[i + 2]); float32x4_t svec3 = vdupq_n_f32(scale[i + 3]); float32x4_t res00 = vfmaq_f32(vcvt_f32_f16(vget_low_f16(src0)), vcvt_f32_f16(vget_low_f16(dst0)), svec0); float32x4_t res10 = vfmaq_f32(vcvt_f32_f16(vget_high_f16(src0)), vcvt_f32_f16(vget_high_f16(dst0)), svec0); float32x4_t res01 = vfmaq_f32(vcvt_f32_f16(vget_low_f16(src1)), vcvt_f32_f16(vget_low_f16(dst1)), svec1); float32x4_t res11 = vfmaq_f32(vcvt_f32_f16(vget_high_f16(src1)), vcvt_f32_f16(vget_high_f16(dst1)), svec1); float32x4_t res02 = vfmaq_f32(vcvt_f32_f16(vget_low_f16(src2)), vcvt_f32_f16(vget_low_f16(dst2)), svec2); float32x4_t res12 = vfmaq_f32(vcvt_f32_f16(vget_high_f16(src2)), vcvt_f32_f16(vget_high_f16(dst2)), svec2); float32x4_t res03 = vfmaq_f32(vcvt_f32_f16(vget_low_f16(src3)), vcvt_f32_f16(vget_low_f16(dst3)), svec3); float32x4_t res13 = vfmaq_f32(vcvt_f32_f16(vget_high_f16(src3)), vcvt_f32_f16(vget_high_f16(dst3)), svec3); vst1q_f16(pdst0, vcombine_f16(vcvt_f16_f32(res00), vcvt_f16_f32(res10))); vst1q_f16(pdst1, vcombine_f16(vcvt_f16_f32(res01), vcvt_f16_f32(res11))); vst1q_f16(pdst2, vcombine_f16(vcvt_f16_f32(res02), vcvt_f16_f32(res12))); vst1q_f16(pdst3, vcombine_f16(vcvt_f16_f32(res03), vcvt_f16_f32(res13))); } for (; i < plane; ++i) { auto pdst = dstPtr + baseOffset + i * pack; auto psrc = srcPtr + baseOffset + i * pack; float16x8_t srcF16 = vld1q_f16(psrc); float16x8_t dstF16 = vld1q_f16(pdst); float32x4_t svec = vdupq_n_f32(scale[i]); float32x4_t s0 = vcvt_f32_f16(vget_low_f16(srcF16)); float32x4_t s1 = vcvt_f32_f16(vget_high_f16(srcF16)); float32x4_t d0 = vcvt_f32_f16(vget_low_f16(dstF16)); float32x4_t d1 = vcvt_f32_f16(vget_high_f16(dstF16)); float32x4_t res0 = vfmaq_f32(s0, d0, svec); float32x4_t res1 = vfmaq_f32(s1, d1, svec); vst1q_f16(pdst, vcombine_f16(vcvt_f16_f32(res0), vcvt_f16_f32(res1))); } } } if (idx == kvBlocks - 1) { for (int j = 0; j < depthQuad; ++j) { const auto baseOffset = j * stride0; int i = 0; const int plane4 = plane - (plane % 4); for (; i < plane4; i += 4) { auto pdst0 = dstPtr + baseOffset + (i + 0) * pack; auto pdst1 = dstPtr + baseOffset + (i + 1) * pack; auto pdst2 = dstPtr + baseOffset + (i + 2) * pack; auto pdst3 = dstPtr + baseOffset + (i + 3) * pack; float16x8_t dst0 = vld1q_f16(pdst0); float16x8_t dst1 = vld1q_f16(pdst1); float16x8_t dst2 = vld1q_f16(pdst2); float16x8_t dst3 = vld1q_f16(pdst3); float32x4_t ns0 = vdupq_n_f32(1.0f / normalizeScale[i + 0]); float32x4_t ns1 = vdupq_n_f32(1.0f / normalizeScale[i + 1]); float32x4_t ns2 = vdupq_n_f32(1.0f / normalizeScale[i + 2]); float32x4_t ns3 = vdupq_n_f32(1.0f / normalizeScale[i + 3]); float32x4_t d00 = vmulq_f32(vcvt_f32_f16(vget_low_f16(dst0)), ns0); float32x4_t d10 = vmulq_f32(vcvt_f32_f16(vget_high_f16(dst0)), ns0); float32x4_t d01 = vmulq_f32(vcvt_f32_f16(vget_low_f16(dst1)), ns1); float32x4_t d11 = vmulq_f32(vcvt_f32_f16(vget_high_f16(dst1)), ns1); float32x4_t d02 = vmulq_f32(vcvt_f32_f16(vget_low_f16(dst2)), ns2); float32x4_t d12 = vmulq_f32(vcvt_f32_f16(vget_high_f16(dst2)), ns2); float32x4_t d03 = vmulq_f32(vcvt_f32_f16(vget_low_f16(dst3)), ns3); float32x4_t d13 = vmulq_f32(vcvt_f32_f16(vget_high_f16(dst3)), ns3); vst1q_f16(pdst0, vcombine_f16(vcvt_f16_f32(d00), vcvt_f16_f32(d10))); vst1q_f16(pdst1, vcombine_f16(vcvt_f16_f32(d01), vcvt_f16_f32(d11))); vst1q_f16(pdst2, vcombine_f16(vcvt_f16_f32(d02), vcvt_f16_f32(d12))); vst1q_f16(pdst3, vcombine_f16(vcvt_f16_f32(d03), vcvt_f16_f32(d13))); } for (; i < plane; ++i) { auto pdst = dstPtr + baseOffset + i * pack; float32x4_t nsvec = vdupq_n_f32(1.0f / normalizeScale[i]); float16x8_t dstF16 = vld1q_f16(pdst); float32x4_t d0 = vcvt_f32_f16(vget_low_f16(dstF16)); float32x4_t d1 = vcvt_f32_f16(vget_high_f16(dstF16)); d0 = vmulq_f32(d0, nsvec); d1 = vmulq_f32(d1, nsvec); vst1q_f16(pdst, vcombine_f16(vcvt_f16_f32(d0), vcvt_f16_f32(d1))); } } } } static void MNNAttenUnpackAndConvertFp16(float* dst, float* src, size_t depth, size_t planesize, int pack) { // src: (UP_DIV(depth, pack), planesize, pack), float16 // dst: (planesize, depth), float32 // pack=8 if (planesize == 1) { MNNDequantizeFP16((int16_t*)src, dst, depth); return; // no need to convert } const auto depthDiv8 = UP_DIV(depth, pack); const auto srcStep = pack * planesize; const auto dstStep = depth; auto remainDepth = depth % pack; auto depthQuad = depthDiv8; if (remainDepth > 0) { depthQuad -= 1; // last quad is not full } for (int i = 0; i < depthQuad; ++i) { auto realsize = planesize; auto srcPtr = (FLOAT16*)src + i * srcStep; auto dstPtr = (float*)dst + i * pack; while (realsize >= 8) { float16x8_t s0_f16 = vld1q_f16(srcPtr + 0 * pack); float16x8_t s1_f16 = vld1q_f16(srcPtr + 1 * pack); float16x8_t s2_f16 = vld1q_f16(srcPtr + 2 * pack); float16x8_t s3_f16 = vld1q_f16(srcPtr + 3 * pack); float16x8_t s4_f16 = vld1q_f16(srcPtr + 4 * pack); float16x8_t s5_f16 = vld1q_f16(srcPtr + 5 * pack); float16x8_t s6_f16 = vld1q_f16(srcPtr + 6 * pack); float16x8_t s7_f16 = vld1q_f16(srcPtr + 7 * pack); float32x4_t d00_f32 = vcvt_f32_f16(vget_low_f16(s0_f16)); float32x4_t d01_f32 = vcvt_f32_f16(vget_high_f16(s0_f16)); float32x4_t d10_f32 = vcvt_f32_f16(vget_low_f16(s1_f16)); float32x4_t d11_f32 = vcvt_f32_f16(vget_high_f16(s1_f16)); float32x4_t d20_f32 = vcvt_f32_f16(vget_low_f16(s2_f16)); float32x4_t d21_f32 = vcvt_f32_f16(vget_high_f16(s2_f16)); float32x4_t d30_f32 = vcvt_f32_f16(vget_low_f16(s3_f16)); float32x4_t d31_f32 = vcvt_f32_f16(vget_high_f16(s3_f16)); float32x4_t d40_f32 = vcvt_f32_f16(vget_low_f16(s4_f16)); float32x4_t d41_f32 = vcvt_f32_f16(vget_high_f16(s4_f16)); float32x4_t d50_f32 = vcvt_f32_f16(vget_low_f16(s5_f16)); float32x4_t d51_f32 = vcvt_f32_f16(vget_high_f16(s5_f16)); float32x4_t d60_f32 = vcvt_f32_f16(vget_low_f16(s6_f16)); float32x4_t d61_f32 = vcvt_f32_f16(vget_high_f16(s6_f16)); float32x4_t d70_f32 = vcvt_f32_f16(vget_low_f16(s7_f16)); float32x4_t d71_f32 = vcvt_f32_f16(vget_high_f16(s7_f16)); vst1q_f32(dstPtr + 0 * dstStep, d00_f32); vst1q_f32(dstPtr + 0 * dstStep + 4, d01_f32); vst1q_f32(dstPtr + 1 * dstStep, d10_f32); vst1q_f32(dstPtr + 1 * dstStep + 4, d11_f32); vst1q_f32(dstPtr + 2 * dstStep, d20_f32); vst1q_f32(dstPtr + 2 * dstStep + 4, d21_f32); vst1q_f32(dstPtr + 3 * dstStep, d30_f32); vst1q_f32(dstPtr + 3 * dstStep + 4, d31_f32); vst1q_f32(dstPtr + 4 * dstStep, d40_f32); vst1q_f32(dstPtr + 4 * dstStep + 4, d41_f32); vst1q_f32(dstPtr + 5 * dstStep, d50_f32); vst1q_f32(dstPtr + 5 * dstStep + 4, d51_f32); vst1q_f32(dstPtr + 6 * dstStep, d60_f32); vst1q_f32(dstPtr + 6 * dstStep + 4, d61_f32); vst1q_f32(dstPtr + 7 * dstStep, d70_f32); vst1q_f32(dstPtr + 7 * dstStep + 4, d71_f32); srcPtr += 8 * pack; dstPtr += 8 * dstStep; realsize -= 8; } if (realsize >= 4) { float16x8_t s0_f16 = vld1q_f16(srcPtr + 0 * pack); float16x8_t s1_f16 = vld1q_f16(srcPtr + 1 * pack); float16x8_t s2_f16 = vld1q_f16(srcPtr + 2 * pack); float16x8_t s3_f16 = vld1q_f16(srcPtr + 3 * pack); float32x4_t d00_f32 = vcvt_f32_f16(vget_low_f16(s0_f16)); float32x4_t d01_f32 = vcvt_f32_f16(vget_high_f16(s0_f16)); float32x4_t d10_f32 = vcvt_f32_f16(vget_low_f16(s1_f16)); float32x4_t d11_f32 = vcvt_f32_f16(vget_high_f16(s1_f16)); float32x4_t d20_f32 = vcvt_f32_f16(vget_low_f16(s2_f16)); float32x4_t d21_f32 = vcvt_f32_f16(vget_high_f16(s2_f16)); float32x4_t d30_f32 = vcvt_f32_f16(vget_low_f16(s3_f16)); float32x4_t d31_f32 = vcvt_f32_f16(vget_high_f16(s3_f16)); vst1q_f32(dstPtr + 0 * dstStep, d00_f32); vst1q_f32(dstPtr + 0 * dstStep + 4, d01_f32); vst1q_f32(dstPtr + 1 * dstStep, d10_f32); vst1q_f32(dstPtr + 1 * dstStep + 4, d11_f32); vst1q_f32(dstPtr + 2 * dstStep, d20_f32); vst1q_f32(dstPtr + 2 * dstStep + 4, d21_f32); vst1q_f32(dstPtr + 3 * dstStep, d30_f32); vst1q_f32(dstPtr + 3 * dstStep + 4, d31_f32); srcPtr += 4 * pack; dstPtr += 4 * dstStep; realsize -= 4; } while (realsize > 0) { auto s0_fp16 = vld1q_f16(srcPtr); auto s00_fp32 = vcvt_f32_f16(vget_low_f16(s0_fp16)); auto s01_fp32 = vcvt_f32_f16(vget_high_f16(s0_fp16)); vst1q_f32(dstPtr, s00_fp32); vst1q_f32(dstPtr + 4, s01_fp32); srcPtr += pack; dstPtr += dstStep; realsize--; } } // process remain depth < 8 if (remainDepth >= 4) { auto realsize = planesize; auto srcPtr = (FLOAT16*)src + (depthDiv8 - 1) * srcStep; auto dstPtr = (float*)dst + (depthDiv8 - 1) * pack; auto extraDepth = remainDepth - 4; float tmp0[4]; float tmp1[4]; float tmp2[4]; float tmp3[4]; float tmp4[4]; float tmp5[4]; float tmp6[4]; float tmp7[4]; while (realsize >= 8) { float16x8_t s0_f16 = vld1q_f16(srcPtr + 0 * pack); float16x8_t s1_f16 = vld1q_f16(srcPtr + 1 * pack); float16x8_t s2_f16 = vld1q_f16(srcPtr + 2 * pack); float16x8_t s3_f16 = vld1q_f16(srcPtr + 3 * pack); float16x8_t s4_f16 = vld1q_f16(srcPtr + 4 * pack); float16x8_t s5_f16 = vld1q_f16(srcPtr + 5 * pack); float16x8_t s6_f16 = vld1q_f16(srcPtr + 6 * pack); float16x8_t s7_f16 = vld1q_f16(srcPtr + 7 * pack); float32x4_t d00_f32 = vcvt_f32_f16(vget_low_f16(s0_f16)); float32x4_t d01_f32 = vcvt_f32_f16(vget_high_f16(s0_f16)); float32x4_t d10_f32 = vcvt_f32_f16(vget_low_f16(s1_f16)); float32x4_t d11_f32 = vcvt_f32_f16(vget_high_f16(s1_f16)); float32x4_t d20_f32 = vcvt_f32_f16(vget_low_f16(s2_f16)); float32x4_t d21_f32 = vcvt_f32_f16(vget_high_f16(s2_f16)); float32x4_t d30_f32 = vcvt_f32_f16(vget_low_f16(s3_f16)); float32x4_t d31_f32 = vcvt_f32_f16(vget_high_f16(s3_f16)); float32x4_t d40_f32 = vcvt_f32_f16(vget_low_f16(s4_f16)); float32x4_t d41_f32 = vcvt_f32_f16(vget_high_f16(s4_f16)); float32x4_t d50_f32 = vcvt_f32_f16(vget_low_f16(s5_f16)); float32x4_t d51_f32 = vcvt_f32_f16(vget_high_f16(s5_f16)); float32x4_t d60_f32 = vcvt_f32_f16(vget_low_f16(s6_f16)); float32x4_t d61_f32 = vcvt_f32_f16(vget_high_f16(s6_f16)); float32x4_t d70_f32 = vcvt_f32_f16(vget_low_f16(s7_f16)); float32x4_t d71_f32 = vcvt_f32_f16(vget_high_f16(s7_f16)); vst1q_f32(dstPtr + 0 * dstStep, d00_f32); vst1q_f32(tmp0, d01_f32); vst1q_f32(dstPtr + 1 * dstStep, d10_f32); vst1q_f32(tmp1, d11_f32); vst1q_f32(dstPtr + 2 * dstStep, d20_f32); vst1q_f32(tmp2, d21_f32); vst1q_f32(dstPtr + 3 * dstStep, d30_f32); vst1q_f32(tmp3, d31_f32); vst1q_f32(dstPtr + 4 * dstStep, d40_f32); vst1q_f32(tmp4, d41_f32); vst1q_f32(dstPtr + 5 * dstStep, d50_f32); vst1q_f32(tmp5, d51_f32); vst1q_f32(dstPtr + 6 * dstStep, d60_f32); vst1q_f32(tmp6, d61_f32); vst1q_f32(dstPtr + 7 * dstStep, d70_f32); vst1q_f32(tmp7, d71_f32); memcpy(dstPtr + 0 * dstStep + 4, tmp0, sizeof(float) * extraDepth); memcpy(dstPtr + 1 * dstStep + 4, tmp1, sizeof(float) * extraDepth); memcpy(dstPtr + 2 * dstStep + 4, tmp2, sizeof(float) * extraDepth); memcpy(dstPtr + 3 * dstStep + 4, tmp3, sizeof(float) * extraDepth); memcpy(dstPtr + 4 * dstStep + 4, tmp4, sizeof(float) * extraDepth); memcpy(dstPtr + 5 * dstStep + 4, tmp5, sizeof(float) * extraDepth); memcpy(dstPtr + 6 * dstStep + 4, tmp6, sizeof(float) * extraDepth); memcpy(dstPtr + 7 * dstStep + 4, tmp7, sizeof(float) * extraDepth); srcPtr += 8 * pack; dstPtr += 8 * dstStep; realsize -= 8; } if (realsize >= 4) { float16x8_t s0_f16 = vld1q_f16(srcPtr + 0 * pack); float16x8_t s1_f16 = vld1q_f16(srcPtr + 1 * pack); float16x8_t s2_f16 = vld1q_f16(srcPtr + 2 * pack); float16x8_t s3_f16 = vld1q_f16(srcPtr + 3 * pack); float32x4_t d00_f32 = vcvt_f32_f16(vget_low_f16(s0_f16)); float32x4_t d01_f32 = vcvt_f32_f16(vget_high_f16(s0_f16)); float32x4_t d10_f32 = vcvt_f32_f16(vget_low_f16(s1_f16)); float32x4_t d11_f32 = vcvt_f32_f16(vget_high_f16(s1_f16)); float32x4_t d20_f32 = vcvt_f32_f16(vget_low_f16(s2_f16)); float32x4_t d21_f32 = vcvt_f32_f16(vget_high_f16(s2_f16)); float32x4_t d30_f32 = vcvt_f32_f16(vget_low_f16(s3_f16)); float32x4_t d31_f32 = vcvt_f32_f16(vget_high_f16(s3_f16)); vst1q_f32(dstPtr + 0 * dstStep, d00_f32); vst1q_f32(tmp0, d01_f32); vst1q_f32(dstPtr + 1 * dstStep, d10_f32); vst1q_f32(tmp1, d11_f32); vst1q_f32(dstPtr + 2 * dstStep, d20_f32); vst1q_f32(tmp2, d21_f32); vst1q_f32(dstPtr + 3 * dstStep, d30_f32); vst1q_f32(tmp3, d31_f32); memcpy(dstPtr + 0 * dstStep + 4, tmp0, sizeof(float) * extraDepth); memcpy(dstPtr + 1 * dstStep + 4, tmp1, sizeof(float) * extraDepth); memcpy(dstPtr + 2 * dstStep + 4, tmp2, sizeof(float) * extraDepth); memcpy(dstPtr + 3 * dstStep + 4, tmp3, sizeof(float) * extraDepth); srcPtr += 4 * pack; dstPtr += 4 * dstStep; realsize -= 4; } while (realsize > 0) { auto s0_fp16 = vld1q_f16(srcPtr); auto d00_fp32 = vcvt_f32_f16(vget_low_f16(s0_fp16)); auto d01_fp32 = vcvt_f32_f16(vget_high_f16(s0_fp16)); vst1q_f32(dstPtr, d00_fp32); vst1q_f32(tmp0, d01_fp32); memcpy(dstPtr + 4, tmp0, sizeof(float) * extraDepth); srcPtr += pack; dstPtr += dstStep; realsize--; } } if (remainDepth > 0 && remainDepth < 4) { auto realsize = planesize; auto srcPtr = (FLOAT16*)src + (depthDiv8 - 1) * srcStep; auto dstPtr = (float*)dst + (depthDiv8 - 1) * pack; float tmp0[4]; float tmp1[4]; float tmp2[4]; float tmp3[4]; float tmp4[4]; float tmp5[4]; float tmp6[4]; float tmp7[4]; while (realsize >= 8) { float16x8_t s0_f16 = vld1q_f16(srcPtr + 0 * pack); float16x8_t s1_f16 = vld1q_f16(srcPtr + 1 * pack); float16x8_t s2_f16 = vld1q_f16(srcPtr + 2 * pack); float16x8_t s3_f16 = vld1q_f16(srcPtr + 3 * pack); float16x8_t s4_f16 = vld1q_f16(srcPtr + 4 * pack); float16x8_t s5_f16 = vld1q_f16(srcPtr + 5 * pack); float16x8_t s6_f16 = vld1q_f16(srcPtr + 6 * pack); float16x8_t s7_f16 = vld1q_f16(srcPtr + 7 * pack); float32x4_t d00_f32 = vcvt_f32_f16(vget_low_f16(s0_f16)); float32x4_t d10_f32 = vcvt_f32_f16(vget_low_f16(s1_f16)); float32x4_t d20_f32 = vcvt_f32_f16(vget_low_f16(s2_f16)); float32x4_t d30_f32 = vcvt_f32_f16(vget_low_f16(s3_f16)); float32x4_t d40_f32 = vcvt_f32_f16(vget_low_f16(s4_f16)); float32x4_t d50_f32 = vcvt_f32_f16(vget_low_f16(s5_f16)); float32x4_t d60_f32 = vcvt_f32_f16(vget_low_f16(s6_f16)); float32x4_t d70_f32 = vcvt_f32_f16(vget_low_f16(s7_f16)); vst1q_f32(tmp0, d00_f32); vst1q_f32(tmp1, d10_f32); vst1q_f32(tmp2, d20_f32); vst1q_f32(tmp3, d30_f32); vst1q_f32(tmp4, d40_f32); vst1q_f32(tmp5, d50_f32); vst1q_f32(tmp6, d60_f32); vst1q_f32(tmp7, d70_f32); memcpy(dstPtr + 0 * dstStep, tmp0, sizeof(float) * remainDepth); memcpy(dstPtr + 1 * dstStep, tmp1, sizeof(float) * remainDepth); memcpy(dstPtr + 2 * dstStep, tmp2, sizeof(float) * remainDepth); memcpy(dstPtr + 3 * dstStep, tmp3, sizeof(float) * remainDepth); memcpy(dstPtr + 4 * dstStep, tmp4, sizeof(float) * remainDepth); memcpy(dstPtr + 5 * dstStep, tmp5, sizeof(float) * remainDepth); memcpy(dstPtr + 6 * dstStep, tmp6, sizeof(float) * remainDepth); memcpy(dstPtr + 7 * dstStep, tmp7, sizeof(float) * remainDepth); srcPtr += 8 * pack; dstPtr += 8 * dstStep; realsize -= 8; } if (realsize >= 4) { float16x8_t s0_f16 = vld1q_f16(srcPtr + 0 * pack); float16x8_t s1_f16 = vld1q_f16(srcPtr + 1 * pack); float16x8_t s2_f16 = vld1q_f16(srcPtr + 2 * pack); float16x8_t s3_f16 = vld1q_f16(srcPtr + 3 * pack); float32x4_t d00_f32 = vcvt_f32_f16(vget_low_f16(s0_f16)); float32x4_t d10_f32 = vcvt_f32_f16(vget_low_f16(s1_f16)); float32x4_t d20_f32 = vcvt_f32_f16(vget_low_f16(s2_f16)); float32x4_t d30_f32 = vcvt_f32_f16(vget_low_f16(s3_f16)); vst1q_f32(tmp0, d00_f32); vst1q_f32(tmp1, d10_f32); vst1q_f32(tmp2, d20_f32); vst1q_f32(tmp3, d30_f32); memcpy(dstPtr + 0 * dstStep, tmp0, sizeof(float) * remainDepth); memcpy(dstPtr + 1 * dstStep, tmp1, sizeof(float) * remainDepth); memcpy(dstPtr + 2 * dstStep, tmp2, sizeof(float) * remainDepth); memcpy(dstPtr + 3 * dstStep, tmp3, sizeof(float) * remainDepth); srcPtr += 4 * pack; dstPtr += 4 * dstStep; realsize -= 4; } while (realsize > 0) { auto s0_f16 = vld1q_f16(srcPtr); float32x4_t d00_f32 = vcvt_f32_f16(vget_low_f16(s0_f16)); vst1q_f32(tmp0, d00_f32); memcpy(dstPtr + 0 * dstStep, tmp0, sizeof(float) * remainDepth); srcPtr += pack; dstPtr += dstStep; realsize--; } } } static void MNNQuantAttentionKeyFP16(int8_t* dst, const float* source, float* sumKeyPtr, float* maxKeyPtr, int32_t* params) { int32_t kvNumHead = params[0]; int32_t seqLen = params[1]; int32_t headDim = params[2]; int32_t blockNum = params[3]; int32_t lP = params[5]; int32_t hP = params[6]; int32_t pastLength = params[7]; int32_t kvHeadIdx = params[8]; auto blockHeadDim = UP_DIV(headDim, blockNum); auto weightStride1 = ROUND_UP(blockHeadDim, lP) * hP; auto weightStride2 = lP * hP; auto packedWeightStride1 = weightStride1 + 2 * sizeof(float) * hP; auto sourceFp16 = (FLOAT16*)source; auto maxKeyFp16 = (FLOAT16*)maxKeyPtr; int8_t tempBuffer[8]; float32x4_t neg128Vec = vdupq_n_f32(-128.0f); // Get max: [1, headDim] if (seqLen > 1) { for (int s = 0; s < seqLen; ++s) { const FLOAT16* keySrc = sourceFp16 + s * kvNumHead * headDim + kvHeadIdx * headDim; int d = 0; for (; d <= headDim - 16; d += 16) { float16x8_t maxVec0 = vld1q_f16(maxKeyFp16 + d); float16x8_t maxVec1 = vld1q_f16(maxKeyFp16 + d + 8); float16x8_t srcVec0 = vld1q_f16(keySrc + d); float16x8_t srcVec1 = vld1q_f16(keySrc + d + 8); maxVec0 = vmaxq_f16(maxVec0, srcVec0); maxVec1 = vmaxq_f16(maxVec1, srcVec1); vst1q_f16(maxKeyFp16 + d, maxVec0); vst1q_f16(maxKeyFp16 + d + 8, maxVec1); } for (; d <= headDim - 8; d += 8) { float16x8_t maxVec = vld1q_f16(maxKeyFp16 + d); float16x8_t srcVec = vld1q_f16(keySrc + d); maxVec = vmaxq_f16(maxVec, srcVec); vst1q_f16(maxKeyFp16 + d, maxVec); } for (; d < headDim; ++d) { maxKeyFp16[d] = ALIMAX(maxKeyFp16[d], keySrc[d]); } } } // Quant fp16 for (int s = 0; s < seqLen; s++) { const FLOAT16* keySrc = sourceFp16 + s * kvNumHead * headDim + kvHeadIdx * headDim; float16x8_t minVec = vdupq_n_f16(keySrc[0]); float16x8_t maxVec = vdupq_n_f16(keySrc[0]); int d = 0; for (; d <= headDim - 8; d += 8) { float16x8_t srcVec = vld1q_f16(keySrc + d); float16x8_t maxKeyVec = vld1q_f16(maxKeyFp16 + d); float16x8_t keyDataF16 = vsubq_f16(srcVec, maxKeyVec); minVec = vminq_f16(minVec, keyDataF16); maxVec = vmaxq_f16(maxVec, keyDataF16); float32x4_t keyDataF32Low = vcvt_f32_f16(vget_low_f16(keyDataF16)); float32x4_t keyDataF32High = vcvt_f32_f16(vget_high_f16(keyDataF16)); } FLOAT16 minKey = vminvq_f16(minVec); FLOAT16 maxKey = vmaxvq_f16(maxVec); for (; d < headDim; ++d) { auto keydata = keySrc[d] - maxKeyFp16[d]; minKey = ALIMIN(minKey, keydata); maxKey = ALIMAX(maxKey, keydata); } int outIndex = (pastLength + s) / hP; int inIndex = (pastLength + s) % hP; float range = (float)maxKey - (float)minKey; float quantScaleVal = 0; float biasVal = minKey + 128.0f * range / 255.0; if (range <= 1e-6f) { quantScaleVal = 0.f; } else { quantScaleVal = 255.0f / range; } for (int k = 0; k < blockNum; ++k) { int8_t* weightDstBase = dst + outIndex * blockNum * packedWeightStride1 + k * packedWeightStride1; float* scaleDst = (float*)(weightDstBase + weightStride1); float* biasDst = scaleDst + hP; scaleDst[inIndex] = range / 255.f; biasDst[inIndex] = biasVal; float32x4_t scaleVecFp32 = vdupq_n_f32(quantScaleVal); float32x4_t negMinKeyVecF32 = vdupq_n_f32(-(float)minKey); const FLOAT16* currentKeyBlock = keySrc + k * blockHeadDim; const FLOAT16* currentMaxBlock = maxKeyFp16 + k * blockHeadDim; int32x4_t sumInt32_0 = vdupq_n_s32(0); int32x4_t sumInt32_1 = vdupq_n_s32(0); int headDimIdx = 0; for (; headDimIdx <= blockHeadDim - 8; headDimIdx += 8) { float16x8_t srcVecFp16 = vld1q_f16(currentKeyBlock + headDimIdx); float16x8_t maxVecFp16 = vld1q_f16(currentMaxBlock + headDimIdx); float16x8_t keyDataF16 = vsubq_f16(srcVecFp16, maxVecFp16); float32x4_t keyDataLowFp32 = vcvt_f32_f16(vget_low_f16(keyDataF16)); float32x4_t keyDataHighFp32 = vcvt_f32_f16(vget_high_f16(keyDataF16)); keyDataLowFp32 = vaddq_f32(keyDataLowFp32, negMinKeyVecF32); keyDataHighFp32 = vaddq_f32(keyDataHighFp32, negMinKeyVecF32); keyDataLowFp32 = vmulq_f32(keyDataLowFp32, scaleVecFp32); keyDataHighFp32 = vmulq_f32(keyDataHighFp32, scaleVecFp32); keyDataLowFp32 = vaddq_f32(keyDataLowFp32, neg128Vec); keyDataHighFp32 = vaddq_f32(keyDataHighFp32, neg128Vec); int32x4_t keyDataLowInt32 = vcvtaq_s32_f32(keyDataLowFp32); int32x4_t keyDataHighInt32 = vcvtaq_s32_f32(keyDataHighFp32); int16x4_t s16Low = vmovn_s32(keyDataLowInt32); int16x4_t s16High = vmovn_s32(keyDataHighInt32); int16x8_t s16Combined = vcombine_s16(s16Low, s16High); // sum sumInt32_0 = vaddq_s32(sumInt32_0, keyDataLowInt32); sumInt32_1 = vaddq_s32(sumInt32_1, keyDataHighInt32); int8x8_t s8Vec = vqmovn_s16(s16Combined); if (lP == 8) { int i = headDimIdx / lP; int8_t* dstPtr = weightDstBase + i * weightStride2 + inIndex * lP; vst1_s8(dstPtr, s8Vec); } else if (lP == 4) { vst1_s8(tempBuffer, s8Vec); int iLow = headDimIdx / lP; int iHigh = (headDimIdx + 4) / lP; int8_t* dstPtrLow = weightDstBase + iLow * weightStride2 + inIndex * lP; int8_t* dstPtrHigh = weightDstBase + iHigh * weightStride2 + inIndex * lP; std::memcpy(dstPtrLow, tempBuffer, 4); std::memcpy(dstPtrHigh, tempBuffer + 4, 4); } else { vst1_s8(tempBuffer, s8Vec); for (int nk = 0; nk < 8; ++nk) { int headDimCurr = headDimIdx + nk; int i = headDimCurr / lP; int j = headDimCurr % lP; weightDstBase[i * weightStride2 + inIndex * lP + j] = tempBuffer[nk]; } } } int32_t sumInt32 = vaddvq_s32(sumInt32_0) + vaddvq_s32(sumInt32_1); for (; headDimIdx < blockHeadDim; ++headDimIdx) { int i = headDimIdx / lP; int j = headDimIdx % lP; float keyVal = (float)currentKeyBlock[headDimIdx] - (float)currentMaxBlock[headDimIdx]; float quantVal = (keyVal - minKey) * quantScaleVal - 128.0f; int32_t roundedVal = static_cast(roundf(quantVal)); int8_t finalVal = static_cast(std::max(-128, std::min(127, roundedVal))); weightDstBase[i * weightStride2 + inIndex * lP + j] = finalVal; sumInt32 += finalVal; } // store sum sumKeyPtr[outIndex * hP + inIndex] = sumInt32 * range / 255.f + (minKey * (float)(blockHeadDim) + 128.0f * range * (float)(blockHeadDim) / 255.0); } } } static void MNNQuantAttentionValueFP16(int8_t* dst, const float* source, float* valueSum, int32_t* params) { // float value src : [kvSeq,kvNumHead,headDim] // int8_t value dest: [updiv(maxLength,flashAttentionBlockKv), updiv(headDim,hp),updiv(flashAttentionBlockKv,lp),hp,lp] // float value sum: [updiv(maxLength,flashAttentionBlockKv), roundup(headDim,hp)] int32_t kvNumHead = params[0]; int32_t seqLen = params[1]; int32_t headDim = params[2]; int32_t blockNum = params[3]; int32_t maxLength = params[4]; int32_t lP = params[5]; int32_t hP = params[6]; int32_t pastLength = params[7]; int32_t kvHeadIdx = params[8]; int32_t flashAttentionBlockKv = params[9]; auto blockKvseq = UP_DIV(seqLen + pastLength, blockNum); auto weightStride2 = lP * hP; auto weightStride1 = UP_DIV(flashAttentionBlockKv, lP) * weightStride2; auto packedStride1 = (int)(weightStride1 + 2 * hP * sizeof(float)); auto packedStride0 = UP_DIV(headDim, hP) * packedStride1; auto srcStride0 = kvNumHead * headDim; auto sourceFp16 = (FLOAT16*)source; // quant scale & bias if (pastLength == 0) { for (int d = 0; d < headDim; ++d) { float* scalePtr = (float*)(dst + (d / hP) * packedStride1 + weightStride1) + (d % hP); float* biasPtr = scalePtr + hP; // find min,max float dMax = sourceFp16[d + kvHeadIdx * headDim]; float dMin = dMax; for (int s = 0; s < seqLen; ++s) { float data = sourceFp16[s * srcStride0 + d + kvHeadIdx * headDim]; dMax = ALIMAX(dMax, data); dMin = ALIMIN(dMin, data); } // scale & bias float range = dMax - dMin; if (range < 1e-6) { scalePtr[0] = 0.f; biasPtr[0] = dMax; } else { float scale = range / 255.f; float bias = range / 255.f * 128.f + dMin; scalePtr[0] = scale; biasPtr[0] = bias; } } } // copy the scale&bias to each blockKv // pastLength == 0: First time prefill // (seqLen + pastLength) % flashAttentionBlockKv == 0: Open a new blockKv if (pastLength == 0 || (pastLength % flashAttentionBlockKv) == 0) { int32_t d0 = UP_DIV(maxLength, flashAttentionBlockKv); int32_t d1 = UP_DIV(headDim, hP); for (int k = 0; k < d0; ++k) { for (int r = 0; r < d1; ++r) { float* scalePtr = (float*)(dst + k * packedStride0 + r * packedStride1 + weightStride1); float* biasPtr = scalePtr + hP; memcpy(scalePtr, dst + r * packedStride1 + weightStride1, hP * sizeof(float)); memcpy(biasPtr, dst + r * packedStride1 + weightStride1 + hP * sizeof(float), hP * sizeof(float)); } } } std::vector qScales(headDim); std::vector qBiases(headDim); std::vector deqScales(headDim); std::vector deqBiases(headDim); int8_t tmpQ[8]; for (int d = 0; d < headDim; ++d) { float* scaleBase = (float*)(dst + (d / hP) * packedStride1 + weightStride1) + (d % hP); float* biasBase = scaleBase + hP; float s_val = scaleBase[0]; float b_val = biasBase[0]; deqScales[d] = s_val; deqBiases[d] = b_val; bool is_small = s_val < 1e-6f; qScales[d] = is_small ? 0.0f : (1.0f / s_val); qBiases[d] = is_small ? 0.0f : (-b_val / s_val); } const __fp16* srcBasePtr = sourceFp16 + kvHeadIdx * headDim; const int32_t sumStride = ROUND_UP(headDim, hP); for (int s = 0; s < seqLen; ++s) { int kvSeqIndx = s + pastLength; int blkIdx = kvSeqIndx / flashAttentionBlockKv; int blkRem = kvSeqIndx % flashAttentionBlockKv; int idxInnerCommon = blkIdx * packedStride0 + (blkRem / lP) * weightStride2 + (blkRem % lP); float* curSumRow = valueSum + blkIdx * sumStride; const __fp16* srcRow = srcBasePtr + s * srcStride0; int d = 0; for (; d <= headDim - 8; d += 8) { // --- Load Source --- float16x8_t vSrc16 = vld1q_f16(srcRow + d); float32x4_t vSrc0 = vcvt_f32_f16(vget_low_f16(vSrc16)); float32x4_t vSrc1 = vcvt_high_f32_f16(vSrc16); // --- Load Quant Params --- float32x4_t vQs0 = vld1q_f32(&qScales[d]); float32x4_t vQb0 = vld1q_f32(&qBiases[d]); float32x4_t vQs1 = vld1q_f32(&qScales[d + 4]); float32x4_t vQb1 = vld1q_f32(&qBiases[d + 4]); // --- Quantize: x * qs + qb --- float32x4_t vRes0 = vaddq_f32(vmulq_f32(vSrc0, vQs0), vQb0); float32x4_t vRes1 = vaddq_f32(vmulq_f32(vSrc1, vQs1), vQb1); // --- Round & Saturate --- int32x4_t vInt32_0 = vcvtaq_s32_f32(vRes0); int32x4_t vInt32_1 = vcvtaq_s32_f32(vRes1); int16x8_t vInt16 = vcombine_s16(vqmovn_s32(vInt32_0), vqmovn_s32(vInt32_1)); int8x8_t vInt8 = vqmovn_s16(vInt16); // Clamp to [-128, 127] vst1_s8(tmpQ, vInt8); for (int k = 0; k < 8; ++k) { int cur_d = d + k; int dstOffset = (cur_d / hP) * packedStride1 + idxInnerCommon + (cur_d % hP) * lP; dst[dstOffset] = tmpQ[k]; } int16x8_t vXq16 = vmovl_s8(vInt8); float32x4_t vXqF0 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(vXq16))); float32x4_t vXqF1 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(vXq16))); float32x4_t vDs0 = vld1q_f32(&deqScales[d]); float32x4_t vDb0 = vld1q_f32(&deqBiases[d]); float32x4_t vDs1 = vld1q_f32(&deqScales[d + 4]); float32x4_t vDb1 = vld1q_f32(&deqBiases[d + 4]); // Dequant float32x4_t vDeq0 = vaddq_f32(vmulq_f32(vXqF0, vDs0), vDb0); float32x4_t vDeq1 = vaddq_f32(vmulq_f32(vXqF1, vDs1), vDb1); float* sumPtr = curSumRow + d; vst1q_f32(sumPtr, vaddq_f32(vld1q_f32(sumPtr), vDeq0)); vst1q_f32(sumPtr + 4, vaddq_f32(vld1q_f32(sumPtr + 4), vDeq1)); } for (; d < headDim; ++d) { float xf = (float)srcRow[d]; float val_f = xf * qScales[d] + qBiases[d]; int32_t val_i = (int32_t)roundf(val_f); if (val_i > 127) val_i = 127; if (val_i < -128) val_i = -128; int8_t xq = (int8_t)val_i; int dstOffset = (d / hP) * packedStride1 + idxInnerCommon + (d % hP) * lP; dst[dstOffset] = xq; curSumRow[d] += ((float)xq * deqScales[d] + deqBiases[d]); } } /* // Quant fp16 for (int d = 0; d < headDim; ++d) { // dst address int idxBase = (d / hP) * packedStride1 + (d % hP) * lP; int8_t* dstBase = dst + idxBase; float* scaleBase = (float*)(dst + (d / hP) * packedStride1 + weightStride1) + (d % hP); float* biasBase = scaleBase + hP; float* sumBase = valueSum + (d / hP) * hP + (d % hP); float qscale = scaleBase[0] < 1e-6 ? 0 : 1.0f / scaleBase[0]; float qbias = scaleBase[0] < 1e-6 ? 0 : (-biasBase[0] / scaleBase[0]); // quant for (int s = 0; s < seqLen; ++s) { int kvSeqIndx = s + pastLength; int idxInner = (kvSeqIndx / flashAttentionBlockKv) * packedStride0 + (kvSeqIndx % flashAttentionBlockKv) / lP * weightStride2 + (kvSeqIndx % flashAttentionBlockKv) % lP; float xf = sourceFp16[s * srcStride0 + d + kvHeadIdx * headDim]; int8_t xq = ALIMAX(ALIMIN(127, static_cast(roundf(xf * qscale + qbias))), -128); dstBase[idxInner] = xq; // sum int idxSum = (kvSeqIndx / flashAttentionBlockKv) * ROUND_UP(headDim, hP); sumBase[idxSum] += ((float)xq * scaleBase[0] + biasBase[0]); } } */ } #endif // MNN_SUPPORT_TRANSFORMER_FUSE #ifdef MNN_LOW_MEMORY void MNNAbsMaxFP16(const float* source, float* absmax, size_t src_depth_quad, size_t realSize, int pack) { if (pack == 4) { MNNAbsMaxFP16_Pack4(source, absmax, src_depth_quad, realSize, pack); return; } if (pack == 8) { MNNAbsMaxFP16_Pack8(source, absmax, src_depth_quad, realSize, pack); return; } // source: (src_depth_quad, realSize, pack) auto srcStep = pack * realSize; auto srcPtr = (FLOAT16*)source; auto dstPtr = (FLOAT16*)absmax; for (int i = 0; i < realSize; ++i) { FLOAT16 absmaxVal = 0; // absmaxVal>=0 for (int c = 0; c < src_depth_quad; ++c) { auto src = srcPtr + c * srcStep + i * pack; for (int k = 0; k < pack; ++k) { if (std::abs(src[k]) > absmaxVal) { absmaxVal = std::abs(src[k]); } } } dstPtr[i] = absmaxVal; } return; } static void MNNDynamicQuantFP16(const float* src, int8_t* dst, const float* scale, size_t src_depth_quad, size_t realSize, int pack, const float* bias = nullptr) { if (pack == 8) { MNNDynamicQuantFP16_Pack8(src, dst, scale, src_depth_quad,realSize, nullptr, pack); return; } if (pack == 4) { MNNDynamicQuantFP16_Pack4(src, dst, scale, src_depth_quad,realSize, nullptr, pack); return; } int8_t* dstPtr = dst; auto srcPtr = (FLOAT16*)src; for (int i = 0; i < realSize; ++i) { auto scaleVal = static_cast(scale[i]); for (int c = 0; c < src_depth_quad; ++c) { auto srcZ = srcPtr + c * pack * realSize + i * pack; auto dstZ = dstPtr + c * pack * realSize + i * pack; for (int k = 0; k < pack; ++k) { int val = (int)roundf(srcZ[k] * scaleVal); dstZ[k] = val; } } } return; } static void MNNAsyQuantFunc_Arm82(int8_t* dst, const float* src, float* qscale, float* qbias, const size_t* info) { // input shape: [kernelsize, blockNum, blockLU, EP, LP] // qscale&qbias [blockNum, EP] auto blockNum = info[0]; auto EP = info[1]; // real area for data auto LP = info[2]; // Innermost data layout, may come from backend's pack or gemmint8 units' SRC_UNIT auto DST_XUNIT = info[3]; // backend gemmint8 units auto SRC_UNIT = info[4]; auto kernelsize = info[5]; auto blockLU = info[6]; auto stride0 = blockNum * blockLU * EP * LP; auto stride1 = blockLU * EP * LP; auto srcPtr = (FLOAT16*)src; #ifdef __aarch64__ if (LP == 4 || LP == 8) { for (int k = 0; k < kernelsize; ++k) { for (int i = 0; i < blockNum; ++i) { if (LP == 4) { MNNDynamicQuantFP16_Pack4((float*)(srcPtr + k * stride0 + i * stride1), dst + k * stride0 + i * stride1, qscale + i * EP, blockLU, EP, qbias + i * EP, LP); } if (LP == 8) { MNNDynamicQuantFP16_Pack8((float*)(srcPtr + k * stride0 + i * stride1), dst + k * stride0 + i * stride1, qscale + i * EP, blockLU, EP, qbias + i * EP, LP); } } } return; } #endif for (int i = 0; i < EP; ++i) { for (int bk = 0; bk < blockNum; ++bk) { float quant_scale = qscale[i + bk * EP]; float quant_bias = qbias[i + bk * EP]; for (int n = 0; n < kernelsize; ++n) { for (int k = 0; k < blockLU; ++k) { for (int j = 0; j < LP; ++j) { int dataIndx = n * stride0 + bk * stride1 + k * EP * LP + i * LP + j; auto data_ = static_cast(srcPtr[dataIndx]); int qval = static_cast(roundf(data_ * quant_scale + quant_bias)); dst[dataIndx] = qval; } } } } } } static void MNNAsyQuantInfo_FP16(float* scale, float* bias, float* qscale, float* qbias, float* dstMin, float* dstMax, float* src, const size_t* info) { auto blockNum = info[0]; auto plane = info[1]; // real area for data auto innerSide = info[2]; // Innermost data layout, may come from backend's pack or gemmint8 units' SRC_UNIT auto DST_XUNIT = info[3]; auto kernelsize = info[5]; auto blockLU = info[6]; auto stride0 = blockNum * blockLU * plane * innerSide; auto stride1 = blockLU * plane * innerSide; auto srcPtr = (FLOAT16*)src; // input shape: [kernelsize,blocknum,blocklu,DST_XUNIT,SRC_UNIT] or [ic/core->pack, plane, core->pack] // dequant scale/bias : [EU, blockNum, step] // quant scale/bias: [blockNum, plane] if (info[7] == 1) { // scale&bias:[1] FLOAT16 maxval, minval; ARM82CountMinMaxValue(src, (float*)(&minval), (float*)(&maxval) , kernelsize * stride0); if (info[8] == 1 && (maxval - minval) > 1e-7) { if (minval > 0.f) { minval = 0.f; } else if (maxval < 0.f){ maxval = 0.f; } } auto range = maxval - minval; if (range <= 1e-7) { scale[0] = 1.f; qscale[0] = 1.f; qbias[0] = -maxval; bias[0] = maxval; } else { qscale[0] = 255.f / range; scale[0] = range / 255.f; qbias[0] = roundf(-minval * 255.f / range)- 128.f; bias[0] = -qbias[0] * scale[0]; } return; } #ifdef __aarch64__ if (DST_XUNIT == 12 || DST_XUNIT == 16) { // Arm82/SME2, fp16: core->pack=8, SRC_UNIT=4 // max,min shape: [blockNum, EP] if (innerSide == 4) { for (int i = 0; i < kernelsize; ++i) { MNNLocalMinMaxFP16_Pack4(dstMin, dstMax, (float*)(srcPtr + i * stride0), blockNum, blockLU, plane, innerSide, i); } } if (innerSide == 8) { for (int i = 0; i < kernelsize; ++i) { MNNLocalMinMaxFP16_Pack8(dstMin, dstMax, (float*)(srcPtr + i * stride0), blockNum, blockLU, plane, innerSide, i); } } // scale, bias if (DST_XUNIT == 12) { auto success = MNNAsyLocalQuantInfo_EP12_FP16(scale, bias, qscale, qbias, dstMin, dstMax, info); if (!success) { MNN_ERROR("Call error: MNNAsyLocalQuantInfo_EP12_FP16\n"); return; } return; } if (DST_XUNIT == 16) { auto success = MNNAsyLocalQuantInfo_EP16_FP16(scale, bias, qscale, qbias, dstMin, dstMax, info); if (!success) { MNN_ERROR("Call error: MNNAsyLocalQuantInfo_EP16_FP16\n"); return; } return; } } if (DST_XUNIT == 10 && innerSide == 8) { // Arm86, fp16: core->pack=8, SRC_UNIT=8 // max,min shape: [blockNum, plane] for (int i = 0; i < kernelsize; ++i) { MNNLocalMinMaxFP16_Pack8(dstMin, dstMax, (float*)(srcPtr + i * stride0), blockNum, blockLU, plane, innerSide, i); } // scale, bias auto success = MNNAsyLocalQuantInfo_EP10_FP16(scale, bias, qscale, qbias, dstMin, dstMax, info); if (!success) { MNN_ERROR("Call error: MNNAsyLocalQuantInfo_EP10_FP16\n"); return; } return; } #else // aarch32 // max,min shape: [blockNum, plane] auto minPtr = (FLOAT16*)dstMin; auto maxPtr = (FLOAT16*)dstMax; for (int i = 0; i < plane; ++i) { for (int bk = 0; bk < blockNum; ++bk) { auto idx0 = i * innerSide + bk * stride1; auto max_ = srcPtr[idx0]; auto min_ = max_; for (int n = 0; n < kernelsize; ++n) { for (int k = 0; k < blockLU; ++k) { for (int j = 0; j < innerSide; ++j) { auto dataIndx = idx0 + n * stride0 + k * (plane * innerSide) + j; auto data_ = srcPtr[dataIndx]; max_ = ALIMAX(max_, data_); min_ = ALIMIN(min_, data_); } } } auto sindx = i + bk * plane; minPtr[sindx] = min_; maxPtr[sindx] = max_; } } // scale, bias for (int i = 0; i < plane; ++i) { auto step = ALIMIN(DST_XUNIT, plane - (i / DST_XUNIT) * DST_XUNIT); auto sind0 = (i / DST_XUNIT) * DST_XUNIT * blockNum + (i % DST_XUNIT); for (int k = 0; k < blockNum; ++k) { auto sind = sind0 + k * step; auto qind = i + k * plane; auto max_ = (float)maxPtr[qind]; auto min_ = (float)minPtr[qind]; auto range = max_ - min_; if (fabs(range) < 1e-7) { qscale[qind] = 0.f; qbias[qind] = 0.f; scale[sind] = 0.f; bias[sind] = max_; } else { qscale[qind] = 255.f / range; qbias[qind] = -min_ * 255.f / range - 128.0f; scale[sind] = range / 255.f; bias[sind] = min_ + (128.f / 255.f) * range; } } } #endif } #endif // MNN_LOW_MEMORY #define EXP_APPROX_MIN_INPUT vdupq_n_f32(-88.0f) #define EXP_APPROX_MAX_INPUT vdupq_n_f32(88.0f) #define EXP_APPROX_LN2 vdupq_n_f32(0.69314718056f) // ln(2) #define EXP_APPROX_LN2_INV vdupq_n_f32(1.44269504089f) // 1/ln(2) // Fourth-order polynomial approximation coefficients of exp(r): // P(x) = c4*x^4 + c3*x^3 + c2*x^2 + c1*x + c0 #define EXP_APPROX_C4 vdupq_n_f32(0.0416624f) #define EXP_APPROX_C3 vdupq_n_f32(0.166665f) #define EXP_APPROX_C2 vdupq_n_f32(0.500000f) #define EXP_APPROX_C1 vdupq_n_f32(1.0f) #define EXP_APPROX_C0 vdupq_n_f32(1.0f) #ifndef __aarch64__ static inline float32x4_t vrndaq_f32_compat(float32x4_t x) { float32x4_t sign = vbslq_f32(vdupq_n_u32(0x80000000), x, vdupq_n_f32(0.0f)); return vcvtq_f32_s32(vcvtq_s32_f32(vaddq_f32(x, vbslq_f32(vcltq_f32(x, vdupq_n_f32(0.0f)), vdupq_n_f32(-0.5f), vdupq_n_f32(0.5f))))); } #endif static inline float32x4_t expApprox(float32x4_t x) { x = vminq_f32(vmaxq_f32(x, EXP_APPROX_MIN_INPUT), EXP_APPROX_MAX_INPUT); float32x4_t k_float; float32x4_t r; float32x4_t exp_r; #if defined(__aarch64__) k_float = vrndaq_f32(vmulq_f32(x, EXP_APPROX_LN2_INV)); // r = x - k * ln(2) r = vfmsq_f32(x, k_float, EXP_APPROX_LN2); // P(r) = (c0 + c2*r^2 + c4*r^4) + r*(c1 + c3*r^2) float32x4_t r2 = vmulq_f32(r, r); float32x4_t p_odd = vfmaq_f32(EXP_APPROX_C1, EXP_APPROX_C3, r2); float32x4_t p_even = vfmaq_f32(EXP_APPROX_C0, EXP_APPROX_C2, r2); p_even = vfmaq_f32(p_even, EXP_APPROX_C4, vmulq_f32(r2, r2)); exp_r = vfmaq_f32(p_even, p_odd, r); #else k_float = vrndaq_f32_compat(vmulq_f32(x, EXP_APPROX_LN2_INV)); r = vsubq_f32(x, vmulq_f32(k_float, EXP_APPROX_LN2)); // 2. c0 + r*(c1 + r*(c2 + r*(c3 + r*c4))) exp_r = vmlaq_f32(EXP_APPROX_C3, EXP_APPROX_C4, r); // c3 + c4*r exp_r = vmlaq_f32(EXP_APPROX_C2, exp_r, r); // c2 + r*(...) exp_r = vmlaq_f32(EXP_APPROX_C1, exp_r, r); // c1 + r*(...) exp_r = vmlaq_f32(EXP_APPROX_C0, exp_r, r); // c0 + r*(...) #endif int32x4_t k_int = vcvtq_s32_f32(k_float); int32x4_t k_shifted = vshlq_n_s32(k_int, 23); return vreinterpretq_f32_s32(vaddq_s32(vreinterpretq_s32_f32(exp_r), k_shifted)); } static void MNNSoftmaxFp16_Pack8(float* dest, const float* source, float* runningMax, float* runningSum, float* updateScale, int outside, int reduceSize, int kvSeqOffset, int validOffset, int pack, bool mask) { auto softmaxDst = (FLOAT16*)dest; auto softmaxSrc = (FLOAT16*)source; if (pack != 8) { MNN_ERROR("MNNSoftmaxFp16_Pack8 only support pack=8\n"); return; } const int packUnit = 8; int reduceSizeOuter = UP_DIV(reduceSize, packUnit); int stride0 = outside * packUnit; // Loop Tiling: Unroll K by 16 // 16 * 8 * 2 = 256 Bytes for (int k = 0; k < outside; k += 16) { int count = ALIMIN(16, outside - k); int validLens[16]; bool isRowValid[16]; for (int i = 0; i < count; ++i) { int currentK = k + i; if (mask && kvSeqOffset > currentK + validOffset) { isRowValid[i] = false; validLens[i] = 0; if (updateScale) updateScale[currentK] = 1.0f; } else { isRowValid[i] = true; validLens[i] = mask ? ALIMIN(reduceSize, currentK + (validOffset + 1) - kvSeqOffset) : reduceSize; } } float currentMax[16]; for (int i = 0; i < count; ++i) { currentMax[i] = runningMax ? runningMax[k + i] : -65504.0f; } for (int j = 0; j < reduceSizeOuter; ++j) { auto blockSrcBase = softmaxSrc + j * stride0 + k * packUnit; for (int i = 0; i < count; ++i) { if (!isRowValid[i]) continue; int len = validLens[i]; int blockStart = j * packUnit; if (blockStart >= len) continue; auto srcPtr = blockSrcBase + i * packUnit; int remain = len - blockStart; if (remain >= packUnit) { float16x8_t val = vld1q_f16(srcPtr); float maxInVec = vmaxvq_f16(val); currentMax[i] = ALIMAX(currentMax[i], maxInVec); } else { for (int p = 0; p < remain; ++p) { currentMax[i] = ALIMAX(currentMax[i], (float)srcPtr[p]); } } } } float currentSum[16] = {0.0f}; float32x4_t vecSum0[16]; // Low part accumulator float32x4_t vecSum1[16]; // High part accumulator float32x4_t finalMaxVec[16]; for (int i = 0; i < count; ++i) { vecSum0[i] = vdupq_n_f32(0.0f); vecSum1[i] = vdupq_n_f32(0.0f); finalMaxVec[i] = vdupq_n_f32(currentMax[i]); } for (int j = 0; j < reduceSizeOuter; ++j) { auto blockSrcBase = softmaxSrc + j * stride0 + k * packUnit; auto blockDstBase = softmaxDst + j * stride0 + k * packUnit; for (int i = 0; i < count; ++i) { if (!isRowValid[i]) { memset(blockDstBase + i * packUnit, 0, packUnit * sizeof(__fp16)); continue; } int len = validLens[i]; int blockStart = j * packUnit; if (blockStart >= len) { memset(blockDstBase + i * packUnit, 0, packUnit * sizeof(__fp16)); continue; } auto srcPtr = blockSrcBase + i * packUnit; auto dstPtr = blockDstBase + i * packUnit; int remain = len - blockStart; if (remain >= packUnit) { float16x8_t srcVal = vld1q_f16(srcPtr); // F16 -> F32 expansion float32x4_t low = vcvt_f32_f16(vget_low_f16(srcVal)); float32x4_t high = vcvt_f32_f16(vget_high_f16(srcVal)); // Subtract Max low = vsubq_f32(low, finalMaxVec[i]); high = vsubq_f32(high, finalMaxVec[i]); // Exp low = expApprox(low); high = expApprox(high); // Accumulate Sum vecSum0[i] = vaddq_f32(vecSum0[i], low); vecSum1[i] = vaddq_f32(vecSum1[i], high); // Store Exp result temporarily vst1q_f16(dstPtr, vcombine_f16(vcvt_f16_f32(low), vcvt_f16_f32(high))); } else { // Handle Tail for (int p = 0; p < remain; ++p) { float val = expf((float)srcPtr[p] - currentMax[i]); currentSum[i] += val; dstPtr[p] = (__fp16)val; } memset(dstPtr + remain, 0, (packUnit - remain) * sizeof(__fp16)); } } } // Horizontal reduction for sums for (int i = 0; i < count; ++i) { currentSum[i] += vaddvq_f32(vecSum0[i]) + vaddvq_f32(vecSum1[i]); } for (int i = 0; i < count; ++i) { int currentK = k + i; if (!isRowValid[i]) continue; float scale; if (runningMax && runningSum && updateScale) { // Incremental Softmax logic float oldMax = runningMax[currentK]; float scaleForSum = expf(oldMax - currentMax[i]); runningSum[currentK] = runningSum[currentK] * scaleForSum + currentSum[i]; runningMax[currentK] = currentMax[i]; updateScale[currentK] = scaleForSum; continue; } else { // Standard Softmax logic if (runningMax && runningSum) { currentSum[i] += runningSum[currentK] * expf(runningMax[currentK] - currentMax[i]); } scale = 1.0f / (currentSum[i] + 1e-20f); } float16x8_t scaleVec = vdupq_n_f16((__fp16)scale); // Normalize Pass for (int j = 0; j < reduceSizeOuter; ++j) { int len = validLens[i]; int blockStart = j * packUnit; if (blockStart >= len) break; auto dstPtr = softmaxDst + j * stride0 + k * packUnit + i * packUnit; if (len - blockStart >= packUnit) { float16x8_t val = vld1q_f16(dstPtr); val = vmulq_f16(val, scaleVec); vst1q_f16(dstPtr, val); } else { int remain = len - blockStart; for (int p = 0; p < remain; ++p) { dstPtr[p] = (__fp16)((float)dstPtr[p] * scale); } } } } } } static void MNNSoftmaxFp16_Pack1(float* dest, const float* source, float* runningMax, float* runningSum, float* updateScale, int outside, int reduceSize, int kvSeqOffset, int validOffset, bool mask) { auto softmaxDst = (FLOAT16*)dest; auto softmaxSrc = (FLOAT16*)source; for (int k = 0; k < outside; ++k) { int currentValidSize = reduceSize; bool isRowValid = true; if (mask) { if (kvSeqOffset > k + validOffset) { isRowValid = false; currentValidSize = 0; if (updateScale) updateScale[k] = 1.0f; } else { currentValidSize = ALIMIN(reduceSize, k + (validOffset + 1) - kvSeqOffset); } } if (!isRowValid || currentValidSize == 0) { memset(softmaxDst + k * reduceSize, 0, reduceSize * sizeof(__fp16)); continue; } auto srcRow = softmaxSrc + k * reduceSize; auto dstRow = softmaxDst + k * reduceSize; float oldMax = runningMax ? runningMax[k] : -65504.0f; float16x8_t maxVec = vdupq_n_f16(-65504.0f); // Unroll 4 (32 elements per loop) int i = 0; for (; i <= currentValidSize - 32; i += 32) { float16x8_t v0 = vld1q_f16(srcRow + i + 0); float16x8_t v1 = vld1q_f16(srcRow + i + 8); float16x8_t v2 = vld1q_f16(srcRow + i + 16); float16x8_t v3 = vld1q_f16(srcRow + i + 24); maxVec = vmaxq_f16(maxVec, v0); maxVec = vmaxq_f16(maxVec, v1); maxVec = vmaxq_f16(maxVec, v2); maxVec = vmaxq_f16(maxVec, v3); } // Handle remaining blocks of 8 for (; i <= currentValidSize - 8; i += 8) { maxVec = vmaxq_f16(maxVec, vld1q_f16(srcRow + i)); } // Horizontal Max reduction float newMax = vmaxvq_f16(maxVec); // Handle remaining scalars (Tail) for (; i < currentValidSize; ++i) { newMax = ALIMAX(newMax, (float)srcRow[i]); } float finalMax = ALIMAX(oldMax, newMax); float32x4_t finalMaxVec = vdupq_n_f32(finalMax); float sum = 0.0f; float32x4_t sumVec = vdupq_n_f32(0.0f); i = 0; // Unroll 2 (16 elements). Exp is heavy, unroll 4 might cause register spilling. for (; i <= currentValidSize - 16; i += 16) { float16x8_t v0 = vld1q_f16(srcRow + i); float16x8_t v1 = vld1q_f16(srcRow + i + 8); // Process v0 float32x4_t v0_lo = vcvt_f32_f16(vget_low_f16(v0)); float32x4_t v0_hi = vcvt_f32_f16(vget_high_f16(v0)); v0_lo = expApprox(vsubq_f32(v0_lo, finalMaxVec)); v0_hi = expApprox(vsubq_f32(v0_hi, finalMaxVec)); sumVec = vaddq_f32(sumVec, v0_lo); sumVec = vaddq_f32(sumVec, v0_hi); vst1q_f16(dstRow + i, vcombine_f16(vcvt_f16_f32(v0_lo), vcvt_f16_f32(v0_hi))); // Process v1 float32x4_t v1_lo = vcvt_f32_f16(vget_low_f16(v1)); float32x4_t v1_hi = vcvt_f32_f16(vget_high_f16(v1)); v1_lo = expApprox(vsubq_f32(v1_lo, finalMaxVec)); v1_hi = expApprox(vsubq_f32(v1_hi, finalMaxVec)); sumVec = vaddq_f32(sumVec, v1_lo); sumVec = vaddq_f32(sumVec, v1_hi); vst1q_f16(dstRow + i + 8, vcombine_f16(vcvt_f16_f32(v1_lo), vcvt_f16_f32(v1_hi))); } // Handle remaining blocks of 8 for (; i <= currentValidSize - 8; i += 8) { float16x8_t v = vld1q_f16(srcRow + i); float32x4_t v_lo = vcvt_f32_f16(vget_low_f16(v)); float32x4_t v_hi = vcvt_f32_f16(vget_high_f16(v)); v_lo = expApprox(vsubq_f32(v_lo, finalMaxVec)); v_hi = expApprox(vsubq_f32(v_hi, finalMaxVec)); sumVec = vaddq_f32(sumVec, v_lo); sumVec = vaddq_f32(sumVec, v_hi); vst1q_f16(dstRow + i, vcombine_f16(vcvt_f16_f32(v_lo), vcvt_f16_f32(v_hi))); } // Handle Tail scalars if (i < currentValidSize) { __fp16 tempDst[8]; int remain = currentValidSize - i; auto sPtr = srcRow + i; for (int p = 0; p < remain; ++p) { float val = expf((float)sPtr[p] - finalMax); sum += val; tempDst[p] = (__fp16)val; } memcpy(dstRow + i, tempDst, remain * sizeof(__fp16)); i += remain; // align i to currentValidSize } sum += vaddvq_f32(sumVec); // Fill remaining invalid part with 0 if (currentValidSize < reduceSize) { memset(dstRow + currentValidSize, 0, (reduceSize - currentValidSize) * sizeof(__fp16)); } if (runningMax && runningSum && updateScale) { float scaleForSum = expf(oldMax - finalMax); runningSum[k] = runningSum[k] * scaleForSum + sum; runningMax[k] = finalMax; updateScale[k] = scaleForSum; } else { if (runningMax && runningSum) { sum += runningSum[k] * expf(oldMax - finalMax); } float scale = 1.0f / (sum + 1e-20f); float16x8_t scaleVec = vdupq_n_f16((__fp16)scale); // Unroll 4 (32 elements) for throughput i = 0; for (; i <= currentValidSize - 32; i += 32) { float16x8_t v0 = vld1q_f16(dstRow + i); float16x8_t v1 = vld1q_f16(dstRow + i + 8); float16x8_t v2 = vld1q_f16(dstRow + i + 16); float16x8_t v3 = vld1q_f16(dstRow + i + 24); vst1q_f16(dstRow + i, vmulq_f16(v0, scaleVec)); vst1q_f16(dstRow + i + 8, vmulq_f16(v1, scaleVec)); vst1q_f16(dstRow + i + 16, vmulq_f16(v2, scaleVec)); vst1q_f16(dstRow + i + 24, vmulq_f16(v3, scaleVec)); } for (; i <= currentValidSize - 8; i += 8) { float16x8_t v = vld1q_f16(dstRow + i); vst1q_f16(dstRow + i, vmulq_f16(v, scaleVec)); } for (; i < currentValidSize; ++i) { dstRow[i] = (__fp16)((float)dstRow[i] * scale); } } } } static void MNNSoftmaxFp16(float* dest, const float* source, float* runningMax, float* runningSum, float* updateScale, int outside, int reduceSize, int kvSeqOffset, int validOffset, int pack, bool mask) { // source shape: [reduceSizeOuter, outside, reduceSizeInner] // for C4, [up_div(reduceSize,8), outside,8] => reduceSizeOuter=up_div(reduceSize,8), reduceSizeInner=8 // for C, [outside, reduceSize] => reduceSizeOuter=1, reduceSizeInner=reduceSize if (pack == 8) { MNNSoftmaxFp16_Pack8(dest, source, runningMax, runningSum, updateScale, outside, reduceSize, kvSeqOffset, validOffset, pack, mask); return; } if (pack == 1) { MNNSoftmaxFp16_Pack1(dest, source, runningMax, runningSum, updateScale, outside, reduceSize, kvSeqOffset, validOffset, mask); return; } MNN_ERROR("MNNSoftMaxFp16 not support pack!=8 and pack!=1\n"); return; } static CoreFunctions* gInstance = nullptr; static CoreInt8Functions* gArm82CoreInt8Functions = nullptr; bool Arm82Functions::init() { using Vec = MNN::Math::Vec; auto origin = MNNGetCoreFunctions(); #define FUNC_PTR_ASSIGN(dst, src) dst = (decltype(dst))(src) gInstance = new CoreFunctions; gArm82CoreInt8Functions = new CoreInt8Functions; *gArm82CoreInt8Functions = *MNNGetInt8CoreFunctions(); gInstance->int8MatmulRelatedFunctions = origin->int8MatmulRelatedFunctions; { if (origin->supportSDot) { gArm82CoreInt8Functions->MNNPackC4Int8ForMatMul_A = _Arm82MNNPackC4ForMatMul_A<12, 4>; gInstance->arm82MatmulRelatedFunctions = origin->arm82MatmulRelatedFunctions; gInstance->arm82MatmulRelatedFunctions.MNNPackC4Int8ForMatMul_A = _Arm82MNNPackC4ForMatMul_A<12, 4>; } if (origin->supportI8mm) { gArm82CoreInt8Functions->MNNPackC4Int8ForMatMul_A = _ArmBasicMNNPackC4ForMatMul_A_L8<10, 8>; gInstance->supportI8mm = true; } } FUNC_PTR_ASSIGN(gInstance->MNNFp32ToLowp, MNNQuantizeFP16); FUNC_PTR_ASSIGN(gInstance->MNNLowpToFp32, MNNDequantizeFP16); gInstance->bytes = 2; // Packed gInstance->pack = 8; FUNC_PTR_ASSIGN(gInstance->MNNPackCUnit, MNNPackC8FP16); FUNC_PTR_ASSIGN(gInstance->MNNUnpackCUnit, MNNUnPackC8FP16); FUNC_PTR_ASSIGN(gInstance->MNNPackCUnitTranspose, MNNPackTransposeInt16C8); FUNC_PTR_ASSIGN(gInstance->MNNUnpackCUnitTranspose, MNNUnpackTransposeInt16C8); FUNC_PTR_ASSIGN(gInstance->MNNConvRunForLineDepthwise, MNNConvRunForLineDepthwiseFP16); FUNC_PTR_ASSIGN(gInstance->MNNAxByClampBroadcastUnit, MNNAxByClampBroadcastC8FP16); FUNC_PTR_ASSIGN(gInstance->MNNMatrixSub, MNNMatrixSubFP16); FUNC_PTR_ASSIGN(gInstance->MNNMatrixAdd, MNNMatrixAddFP16); FUNC_PTR_ASSIGN(gInstance->MNNStrassenMergeCFunction, ARM82StrassenMerge); gInstance->MNNReorderWeightInt4 = origin->MNNReorderWeightInt4; gInstance->MNNSumWeightInt8 = origin->MNNSumWeightInt8; gInstance->MNNSumWeightInt8SmeHp128 = origin->MNNSumWeightInt8SmeHp128; gInstance->penalty = 2.0f; FUNC_PTR_ASSIGN(gInstance->MNNScaleAndAddBias, MNNScaleAndAddBiasFP16); FUNC_PTR_ASSIGN(gInstance->MNNGridSampleComputeCord, MNNGridSampleComputeCordFP16); FUNC_PTR_ASSIGN(gInstance->MNNGridSampleInterp, MNNGridSampleInterp); FUNC_PTR_ASSIGN(gInstance->MNNGridSampleInterpGrad, MNNGridSampleInterpGrad); FUNC_PTR_ASSIGN(gInstance->MNNGridSampleComputeCord3D, MNNGridSampleComputeCord3DFp16); FUNC_PTR_ASSIGN(gInstance->MNNGridSampleInterp3D, MNNGridSampleInterp3D); FUNC_PTR_ASSIGN(gInstance->MNNRoiPoolingMax, MNNRoiPoolingMaxFP16); FUNC_PTR_ASSIGN(gInstance->MNNRoiAlignMax, MNNRoiAlignMaxFP16); FUNC_PTR_ASSIGN(gInstance->MNNRoiAlignAvg, MNNRoiAlignAvgFP16); FUNC_PTR_ASSIGN(gInstance->MNNCopyC4WithStride, MNNCopyC8WithStrideFP16); FUNC_PTR_ASSIGN(gInstance->MNNAddC4WithStride, MNNAddC8WithStrideFP16); // MatMul FUNC_PTR_ASSIGN(gInstance->MNNGetMatMulPackMode, Arm82MNNGetMatMulPackMode); FUNC_PTR_ASSIGN(gInstance->MNNPackedMatMul, MNNPackedMatMulFP16); FUNC_PTR_ASSIGN(gInstance->MNNPackedMatMulRemain, MNNPackedMatMulRemainFP16); FUNC_PTR_ASSIGN(gInstance->MNNPackC4ForMatMul_A, Arm82MNNPackForMatMul_A); FUNC_PTR_ASSIGN(gInstance->MNNPackForMatMul_B, Arm82MNNPackForMatMul_B); FUNC_PTR_ASSIGN(gInstance->MNNSoftmax, MNNSoftmaxFp16); #if defined(__aarch64__) gInstance->supportFp16arith = origin->supportFp16arith; gInstance->supportSDot = origin->supportSDot; gInstance->supportI8mm = origin->supportI8mm; gInstance->supportSME2 = origin->supportSME2; gInstance->smeCoreNumber = origin->smeCoreNumber; #ifdef MNN_LOW_MEMORY // Dynamic Qaunt Helper Functions FUNC_PTR_ASSIGN(gInstance->MNNAbsMax, MNNAbsMaxFP16); FUNC_PTR_ASSIGN(gInstance->MNNQuantScale, MNNQuantScaleFP16); FUNC_PTR_ASSIGN(gInstance->MNNDynamicQuant, MNNDynamicQuantFP16); FUNC_PTR_ASSIGN(gInstance->MNNAsyQuantFunc, MNNAsyQuantFunc_Arm82); FUNC_PTR_ASSIGN(gInstance->MNNAsyQuantInfo, MNNAsyQuantInfo_FP16); // return 'plane' min&max FUNC_PTR_ASSIGN(gInstance->MNNDynamicUpdateConvBiasScale, origin->MNNDynamicUpdateConvBiasScale); if (origin->supportSDot) { FUNC_PTR_ASSIGN(gInstance->MNNGeneralIm2Col, MNNGeneralIm2col_Arm82); gInstance->arm82MatmulRelatedFunctions.MNNGeneralIm2Col = MNNGeneralIm2col_Arm82; } if (origin->supportI8mm) { FUNC_PTR_ASSIGN(gInstance->MNNGeneralIm2Col, MNNGeneralIm2col_Arm86); } #endif // MNN_LOW_MEMORY FUNC_PTR_ASSIGN(gInstance->MNNCountMaxMinValue, ARM82CountMinMaxValue); // return one min&max FUNC_PTR_ASSIGN(gInstance->MNNSumByAxisLForMatmul_A, origin->MNNSumByAxisLForMatmul_A); FUNC_PTR_ASSIGN(gInstance->MNNDepthwiseConvFastKernel, MNNDepthwiseConvFastKernelFP16); #endif // __aarch64__ #ifdef MNN_SUPPORT_TRANSFORMER_FUSE // Attention FUNC_PTR_ASSIGN(gInstance->MNNAttenPackAndScaleSingleHead, MNNAttenPackAndScaleSingleHead); FUNC_PTR_ASSIGN(gInstance->MNNFlashAttentionUpdateBlockOutput, MNNFlashAttentionUpdateBlockOutput); gInstance->MNNQuantAttentionKey = MNNQuantAttentionKeyFP16; gInstance->MNNQuantAttentionValue = MNNQuantAttentionValueFP16; // LinearAttention fp16 kernels FUNC_PTR_ASSIGN(gInstance->MNNRankOneUpdate, MNNRankOneUpdateFp16); FUNC_PTR_ASSIGN(gInstance->MNNDualMatVec, MNNDualMatVecFp16); FUNC_PTR_ASSIGN(gInstance->MNNDecayRankOneUpdate, MNNDecayRankOneUpdateFp16); #if defined(__aarch64__) && defined(MNN_USE_NEON) // Fused kernel uses NEON intrinsics directly (not extern asm), so the // assignment must follow the same guard as the function body above. FUNC_PTR_ASSIGN(gInstance->MNNFusedGatedDelta, MNNFusedGatedDeltaFp16); #endif #endif // MNN_SUPPORT_TRANSFORMER_FUSE gInstance->MNNComputeMatMulForH_1 = _MNNComputeMatMulForH_1_FP16; gInstance->MNNComputeMatMulForE_1 = _MNNComputeMatMulForE_1_FP16; FUNC_PTR_ASSIGN(gInstance->chooseWinoSourceTransformPack, Arm82WinogradFunction::chooseWinoSourceTransformPack); FUNC_PTR_ASSIGN(gInstance->chooseWinoSourceUnrollTransform, Arm82WinogradFunction::chooseSourceUnrollTransform); FUNC_PTR_ASSIGN(gInstance->chooseWinoDestUnrollTransform, Arm82WinogradFunction::chooseWinoDestUnrollTransform); gInstance->MNNDeconvRunForLineDepthwise = (decltype(gInstance->MNNDeconvRunForLineDepthwise))_MNNDeconvRunForLineDepthwise; gInstance->MNNDeconvRunForUnitDepthWise = (decltype(gInstance->MNNDeconvRunForUnitDepthWise))_MNNDeconvRunForUnitDepthWise; // Binary and Unary gInstance->MNNSelectBinaryFunctionForFloat = Arm82BinaryFloat::select; gInstance->MNNSelectUnaryFunctionForFloat = Arm82Unary::select; // Relu with slope gInstance->MNNReluWithSlopeChannel = Arm82Relu::reluWithSlopeChannel; gInstance->MNNPoolingMax = (decltype(gInstance->MNNPoolingMax))(poolingMax); gInstance->MNNPoolingAvg = (decltype(gInstance->MNNPoolingAvg))(poolingAvg); { gInstance->int8MatmulRelatedFunctions.MNNPackC4Int8ForMatMul_A = gArm82CoreInt8Functions->MNNPackC4Int8ForMatMul_A; gInstance->int8MatmulRelatedFunctions.MNNGeneralIm2Col = gInstance->MNNGeneralIm2Col; } #ifdef __aarch64__ #ifdef MNN_SME2 if (origin->supportSME2) { gArm82CoreInt8Functions->MNNPackC4Int8ForMatMul_A = _Arm82MNNPackC4ForMatMul_A<16, 4>; FUNC_PTR_ASSIGN(gInstance->MNNPackedMatMul, MNNPackedMatMulFP16_SME2); FUNC_PTR_ASSIGN(gInstance->MNNPackedMatMulRemain, MNNPackedMatMulRemainFP16_SME2); FUNC_PTR_ASSIGN(gInstance->MNNGetMatMulPackMode, Sme2MNNGetMatMulPackMode); FUNC_PTR_ASSIGN(gInstance->MNNPackC4ForMatMul_A, Sme2MNNPackC4ForMatMul_A_FP16); FUNC_PTR_ASSIGN(gInstance->MNNPackForMatMul_B, Sme2MNNPackForMatMul_B); #ifdef MNN_LOW_MEMORY FUNC_PTR_ASSIGN(gInstance->MNNGeneralIm2Col, MNNGeneralIm2col_Fp16Sme2); #endif } #endif // MNN_SME2 #endif // __aarch64__ // Update the function pointers in the int8MatmulRelatedFunctions struct. gInstance->int8MatmulRelatedFunctions.MNNPackC4Int8ForMatMul_A = gArm82CoreInt8Functions->MNNPackC4Int8ForMatMul_A; gInstance->int8MatmulRelatedFunctions.MNNGeneralIm2Col = gInstance->MNNGeneralIm2Col; return true; } CoreFunctions* Arm82Functions::get() { return gInstance; } CoreInt8Functions* Arm82Functions::getInt8() { return gArm82CoreInt8Functions; } }; #endif