LCOV - code coverage report
Current view: top level - adump/dump_statistics/dump_stat_op - kfc_dump_base.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.1 % 152 146
Test Date: 2026-08-31 10:09:28 Functions: 100.0 % 92 92

            Line data    Source code
       1              : /**
       2              :  * Copyright (c) 2026 Huawei Technologies Co., Ltd.
       3              :  * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
       4              :  * CANN Open Software License Agreement Version 2.0 (the "License").
       5              :  * Please refer to the License for details. You may not use this file except in compliance with the License.
       6              :  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
       7              :  * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
       8              :  * See LICENSE in the root of the software repository for the full text of the License.
       9              :  */
      10              : 
      11              : #ifndef __KFC_DUMP_BASE_H__
      12              : #define __KFC_DUMP_BASE_H__
      13              : 
      14              : #include <type_traits>
      15              : #include "kernel_operator.h"
      16              : #include "kfc_dump_param.h"
      17              : 
      18              : // 是否支持 fp8 数据类型(hifloat8/fp8_e5m2/fp8_e4m3fn)
      19              : // 当前仅 David 系列支持,但语义上与架构指令差异独立
      20              : // KFC_DUMP_ARCH_DAVID 定义见 kfc_dump_param.h
      21              : #define KFC_DUMP_SUPPORT_FP8 KFC_DUMP_ARCH_DAVID
      22              : 
      23              : namespace KfcDumpStat {
      24              : using namespace AscendC;
      25              : 
      26              : constexpr float FP_INF = 3.4e40;                   // INF 标量
      27              : constexpr int64_t BUFFER_NUM = 2;                  // 使能 DOUBLE BUFFER
      28              : constexpr int64_t MAX_STAT_NUM = 64;               // 最大支持的统计项个数
      29              : constexpr int64_t MAX_OUTPUT_BYTE_SIZE = 8;        // 单个统计项结果所占的字节数
      30              : constexpr int64_t MAX_WORKSPACE_BYTE_SIZE = 32;    // 申请的 workspace 32B 字节对齐
      31              : constexpr int64_t MULTI_CORE_BYTES_NUM = 8 * 1024; // 单核模板多核模板分界字节数
      32              : constexpr int64_t BLOCK_SIZE = 32;                 // DataCopy 最小搬运单元 32B
      33              : constexpr int64_t COMMAND_SIZE = 256;              // Compare 命令需要 256B 对齐
      34              : #if KFC_DUMP_SUPPORT_FP8
      35              : constexpr int64_t MAX_MASK_NUM = 32; // 单核模板中,mask 需要最大的 UB 数量, david为处理fp8需扩大
      36              : #else
      37              : constexpr int64_t MAX_MASK_NUM = 16;        // 单核模板中,mask 需要最大的 UB 数量
      38              : #endif
      39              : 
      40              : constexpr int64_t DTYPE_BYTE_SIZE_b8 = 1;  // b8 字节数
      41              : constexpr int64_t DTYPE_BYTE_SIZE_b16 = 2; // b16 字节数
      42              : constexpr int64_t DTYPE_BYTE_SIZE_b32 = 4; // b32 字节数
      43              : 
      44              : #if KFC_DUMP_ARCH_DAVID
      45              : constexpr int64_t UB_BUF_CNT_b8 = 15;       // 1 origin value, 2 + 4 cast value, 4 worklocal, 4 cache
      46              : constexpr int64_t UB_BUF_CNT_b8_MULTI = 16; // 1 origin value, 2 + 4 cast value, 4 worklocal, 4 cache, 1 mask
      47              : #else
      48              : constexpr int64_t UB_BUF_CNT_b8 = 11;       // 1 origin value, 2 + 4 cast value, 4 worklocal
      49              : constexpr int64_t UB_BUF_CNT_b8_MULTI = 12; // 1 origin value, 2 + 4 cast value, 4 worklocal, 1 mask
      50              : #endif
      51              : constexpr int64_t UB_BUF_CNT_b16 = 8;       // 1 origin value, 1 + 2 cast value, 2 worklocal, 2 cache
      52              : constexpr int64_t UB_BUF_CNT_b32 = 5;       // 1 origin value, 1 cast value, 1 worklocal, 2 cache
      53              : constexpr int64_t UB_BUF_CNT_b16_MULTI = 9; // 1 origin value, 1 + 2 cast value, 2 worklocal, 2 cache, 1 mask
      54              : constexpr int64_t UB_BUF_CNT_b32_MULTI = 5; // 1 origin value, 1 cast value, 1 worklocal, 1 cache tensor, 1 mask
      55              : 
      56           43 : __aicore__ inline int64_t CalculateMaxProcCount(int64_t xDtypeSize, int64_t ubSize)
      57              : {
      58              :     // 满足 256B 对齐,可能存在 double buffer,保证每次最大搬运数量 256B 对齐
      59           43 :     if (xDtypeSize == DTYPE_BYTE_SIZE_b8) {
      60            8 :         int64_t divisor = UB_BUF_CNT_b8 * COMMAND_SIZE * BUFFER_NUM;
      61            8 :         if (divisor != 0 && xDtypeSize != 0) {
      62            8 :             return (((ubSize - MAX_MASK_NUM * BLOCK_SIZE - MAX_STAT_NUM * BLOCK_SIZE - MULTI_CORE_BYTES_NUM) /
      63              :                      divisor) *
      64            8 :                     COMMAND_SIZE * BUFFER_NUM) /
      65            8 :                    xDtypeSize;
      66              :         } else {
      67            0 :             return -1;
      68              :         }
      69           35 :     } else if (xDtypeSize == DTYPE_BYTE_SIZE_b16) {
      70            5 :         int64_t divisor = UB_BUF_CNT_b16 * COMMAND_SIZE * BUFFER_NUM;
      71            5 :         if (divisor != 0 && xDtypeSize != 0) {
      72            5 :             return (((ubSize - MAX_MASK_NUM * BLOCK_SIZE - MAX_STAT_NUM * BLOCK_SIZE - MULTI_CORE_BYTES_NUM) /
      73              :                      divisor) *
      74            5 :                     COMMAND_SIZE * BUFFER_NUM) /
      75            5 :                    xDtypeSize;
      76              :         } else {
      77            0 :             return -1;
      78              :         }
      79           30 :     } else if (xDtypeSize == DTYPE_BYTE_SIZE_b32) {
      80           29 :         int64_t divisor = UB_BUF_CNT_b32 * COMMAND_SIZE * BUFFER_NUM;
      81           29 :         if (divisor != 0 && xDtypeSize != 0) {
      82           29 :             return (((ubSize - MAX_MASK_NUM * BLOCK_SIZE - MAX_STAT_NUM * BLOCK_SIZE - MULTI_CORE_BYTES_NUM) /
      83              :                      divisor) *
      84           29 :                     COMMAND_SIZE * BUFFER_NUM) /
      85           29 :                    xDtypeSize;
      86              :         } else {
      87            0 :             return -1;
      88              :         }
      89              :     } else { // error data byte size
      90            1 :         return -1;
      91              :     }
      92              : }
      93              : 
      94          170 : __aicore__ inline int64_t CalculateMaxProcCountMulti(int64_t xDtypeSize, int64_t ubSize)
      95              : {
      96              :     // 满足 256B 对齐,可能存在 double buffer,保证每次最大搬运数量 256B 对齐
      97          170 :     if (xDtypeSize == DTYPE_BYTE_SIZE_b8) {
      98            6 :         int64_t divisor = UB_BUF_CNT_b8_MULTI * COMMAND_SIZE * BUFFER_NUM;
      99            6 :         if (divisor != 0 && xDtypeSize != 0) {
     100            6 :             return (((ubSize - MAX_STAT_NUM * BLOCK_SIZE - MULTI_CORE_BYTES_NUM) / divisor) * COMMAND_SIZE *
     101              :                     BUFFER_NUM) /
     102            6 :                    xDtypeSize;
     103              :         } else {
     104            0 :             return -1;
     105              :         }
     106          164 :     } else if (xDtypeSize == DTYPE_BYTE_SIZE_b16) {
     107            4 :         int64_t divisor = UB_BUF_CNT_b16_MULTI * COMMAND_SIZE * BUFFER_NUM;
     108            4 :         if (divisor != 0 && xDtypeSize != 0) {
     109            4 :             return (((ubSize - MAX_STAT_NUM * BLOCK_SIZE - MULTI_CORE_BYTES_NUM) / divisor) * COMMAND_SIZE *
     110              :                     BUFFER_NUM) /
     111            4 :                    xDtypeSize;
     112              :         } else {
     113            0 :             return -1;
     114              :         }
     115          160 :     } else if (xDtypeSize == DTYPE_BYTE_SIZE_b32) {
     116          159 :         int64_t divisor = UB_BUF_CNT_b32_MULTI * COMMAND_SIZE * BUFFER_NUM;
     117          159 :         if (divisor != 0 && xDtypeSize != 0) {
     118          159 :             return (((ubSize - MAX_STAT_NUM * BLOCK_SIZE - MULTI_CORE_BYTES_NUM) / divisor) * COMMAND_SIZE *
     119              :                     BUFFER_NUM) /
     120          159 :                    xDtypeSize;
     121              :         } else {
     122            0 :             return -1;
     123              :         }
     124              :     } else { // error data byte size
     125            1 :         return -1;
     126              :     }
     127              : }
     128              : 
     129              : template <typename T1, typename T2>
     130         1508 : __aicore__ inline T1 CeilDiv(T1 a, T2 b)
     131              : {
     132         1508 :     T1 bTemp(b);
     133         1508 :     return bTemp == 0 ? a : (a + bTemp - 1) / bTemp;
     134              : }
     135              : 
     136              : template <typename T1, typename T2>
     137         1314 : __aicore__ inline T1 CeilAlign(T1 a, T2 b)
     138              : {
     139         1314 :     T1 bTemp(b);
     140         1314 :     return bTemp == 0 ? a : CeilDiv(a, bTemp) * bTemp;
     141              : }
     142              : 
     143              : // 当前调用方式多核同步不支持硬同步,因此需要软同步
     144              : __aicore__ inline void SyncAllCore(
     145              :     TQue<QuePosition::VECOUT, BUFFER_NUM>& workQueue, uint64_t syncspace, uint64_t aiCoreNum)
     146              : {
     147              :     GlobalTensor<int32_t> syncGlobal;
     148              :     syncGlobal.SetGlobalBuffer((__gm__ int32_t*)syncspace, aiCoreNum * (BLOCK_SIZE / sizeof(int32_t)));
     149              : 
     150              :     LocalTensor<int32_t> workLocal = workQueue.AllocTensor<int32_t>();
     151              : 
     152              :     SyncAll<true>(syncGlobal, workLocal, aiCoreNum);
     153              :     workQueue.FreeTensor(workLocal);
     154              : }
     155              : 
     156              : template <typename T>
     157         1395 : __aicore__ inline void CopyInData(
     158              :     LocalTensor<T>& dstUB, GlobalTensor<T>& srcGM, int64_t dataCount, int64_t perBlockCount)
     159              : {
     160         1395 :     if (dataCount % perBlockCount) {
     161         1071 :         int64_t floorAlignCnt = dataCount / perBlockCount * perBlockCount;
     162         1071 :         if (floorAlignCnt > 0) {
     163         1057 :             DataCopy(dstUB, srcGM, floorAlignCnt);
     164         1057 :             pipe_barrier(PIPE_ALL);
     165              :         }
     166         7154 :         for (int64_t i = floorAlignCnt; i < dataCount; ++i) {
     167         6083 :             dstUB.SetValue(i, srcGM.GetValue(i));
     168              :         }
     169              :     } else {
     170          324 :         DataCopy(dstUB, srcGM, dataCount);
     171              :     }
     172         1395 :     pipe_barrier(PIPE_ALL);
     173         1395 : }
     174              : 
     175              : template <typename T>
     176         1395 : __aicore__ inline void CopyInX(
     177              :     TQue<QuePosition::VECIN, BUFFER_NUM>& xQue, GlobalTensor<T>& xGm, int64_t gmOffset, int64_t dataCount,
     178              :     int64_t perBlockCount)
     179              : {
     180         1395 :     LocalTensor<T> xIn = xQue.AllocTensor<T>();
     181         1395 :     GlobalTensor<T> xOut = xGm[gmOffset];
     182         1395 :     CopyInData(xIn, xOut, dataCount, perBlockCount);
     183         1395 :     xQue.EnQue(xIn);
     184         1395 : }
     185              : 
     186              : template <typename OutputT>
     187          359 : __aicore__ inline void UpdateCoreOutput(uint64_t addr, OutputT outputValue)
     188              : {
     189          359 :     GlobalTensor<OutputT> outputGm;
     190          359 :     outputGm.SetGlobalBuffer((__gm__ OutputT*)addr);
     191          359 :     outputGm.SetValue(0, outputValue);
     192              : 
     193          359 :     DataCacheCleanAndInvalid<OutputT, CacheLine::SINGLE_CACHE_LINE, DcciDst::CACHELINE_OUT>(outputGm);
     194          359 : }
     195              : 
     196              : template <typename OutputT>
     197         1374 : __aicore__ inline OutputT GetCoreOutput(uint64_t addr)
     198              : {
     199         1374 :     GlobalTensor<OutputT> outputGm;
     200         1374 :     outputGm.SetGlobalBuffer((__gm__ OutputT*)addr);
     201         1374 :     DataCacheCleanAndInvalid<OutputT, CacheLine::SINGLE_CACHE_LINE, DcciDst::CACHELINE_OUT>(outputGm);
     202         1374 :     return outputGm.GetValue(0);
     203              : }
     204              : 
     205         1318 : __aicore__ inline void CopyOutToWorkspace(
     206              :     LocalTensor<uint64_t>& localT, int64_t curCoreStart, uint64_t workspace, int64_t blockIdx, uint64_t statNum)
     207              : {
     208         1318 :     uint64_t curWorkSpaceAddr =
     209         1318 :         workspace + blockIdx * (statNum * MAX_WORKSPACE_BYTE_SIZE) + curCoreStart * MAX_WORKSPACE_BYTE_SIZE;
     210         1318 :     GlobalTensor<uint64_t> workspaceOut;
     211         1318 :     workspaceOut.SetGlobalBuffer((__gm__ uint64_t*)curWorkSpaceAddr, MAX_WORKSPACE_BYTE_SIZE / sizeof(uint64_t));
     212         1318 :     DataCopy(workspaceOut, localT, MAX_WORKSPACE_BYTE_SIZE / sizeof(uint64_t));
     213         1318 :     pipe_barrier(PIPE_ALL);
     214         1318 : }
     215              : 
     216           63 : __aicore__ inline void UpdateMsg(__gm__ KfcDumpStatMsg* sMsg, __gm__ KfcDumpStatMsg* rMsg, bool isSuccess)
     217              : {
     218           63 :     GlobalTensor<uint32_t> rcvMsgGM;
     219           63 :     rcvMsgGM.SetGlobalBuffer((__gm__ uint32_t*)rMsg);
     220              : 
     221           63 :     GlobalTensor<uint32_t> sendMsgGM;
     222           63 :     sendMsgGM.SetGlobalBuffer((__gm__ uint32_t*)sMsg);
     223              : 
     224           63 :     auto iterCount = sizeof(KfcDumpStatMsg) / sizeof(uint32_t);
     225         1071 :     for (int32_t i = 0; i < iterCount; ++i) {
     226         1008 :         sendMsgGM.SetValue(i, rcvMsgGM.GetValue(i));
     227              :     }
     228              : 
     229           63 :     sendMsgGM.SetValue(MSG_TYPE_INDEX, static_cast<uint32_t>(DumpStatMsgType::KFC_DUMP_MSG_RESPONSE));
     230              : 
     231           63 :     if (isSuccess) {
     232           56 :         sendMsgGM.SetValue(MSG_RESULT_INDEX, MSG_RESULT_SUCCESS);
     233              :     } else {
     234            7 :         sendMsgGM.SetValue(MSG_RESULT_INDEX, MSG_RESULT_FAILED);
     235              :     }
     236              : 
     237           63 :     sendMsgGM.SetValue(MSG_VALID_INDEX, DUMP_MSG_VALID_MASK);
     238              : 
     239           63 :     rcvMsgGM.SetValue(MSG_VALID_INDEX, ~DUMP_MSG_VALID_MASK);
     240              : 
     241           63 :     DataCacheCleanAndInvalid<uint32_t, CacheLine::SINGLE_CACHE_LINE, DcciDst::CACHELINE_OUT>(rcvMsgGM);
     242           63 :     DataCacheCleanAndInvalid<uint32_t, CacheLine::SINGLE_CACHE_LINE, DcciDst::CACHELINE_OUT>(sendMsgGM);
     243           63 : }
     244              : 
     245              : // fp8 类型仅在部分架构上存在,统一通过该trait判断,避免散落的架构宏
     246              : template <typename T>
     247              : struct IsFp8Type {
     248              : #if KFC_DUMP_SUPPORT_FP8
     249              :     static constexpr bool value =
     250              :         std::is_same_v<T, hifloat8_t> || std::is_same_v<T, fp8_e4m3fn_t> || std::is_same_v<T, fp8_e5m2_t>;
     251              : #else
     252              :     static constexpr bool value = false;
     253              : #endif
     254              : };
     255              : 
     256              : // 尾块对齐填充:将尾部 appendNum 个元素填充为 fillValue(fillValue 取首元素或 0)
     257              : template <typename T>
     258          915 : __aicore__ inline void AppendTailPadding(LocalTensor<T>& x, int64_t curProcessCount, int64_t appendNum)
     259              : {
     260          915 :     if (appendNum <= 0) {
     261          165 :         return;
     262              :     }
     263              : #if KFC_DUMP_SUPPORT_FP8
     264              :     if constexpr (IsFp8Type<T>::value) {
     265           15 :         return; // fp8 尾块在 cast 到 float32 后填充
     266              :     } else {
     267          735 :         T appendValue{0};
     268        16717 :         for (int64_t i = curProcessCount - appendNum; i < curProcessCount; ++i) {
     269        15982 :             x.SetValue(i, appendValue);
     270              :         }
     271              :     }
     272              : #else
     273              :     T appendValue{0};
     274              :     for (int64_t i = curProcessCount - appendNum; i < curProcessCount; ++i) {
     275              :         x.SetValue(i, appendValue);
     276              :     }
     277              : #endif
     278          735 :     pipe_barrier(PIPE_ALL);
     279              : }
     280              : 
     281              : // fp8 尾块在 cast 后以 float 填充(fp8 不支持 SetValue 填充自身类型)。
     282              : // 求和/计数类统计(mean/l2norm/nan/inf)补 0 不影响结果;
     283              : // max/min 需以首元素值填充,否则全负数据的 max / 全正数据的 min 会被错误统计为 0
     284          152 : __aicore__ inline void AppendTailPaddingFp32(
     285              :     LocalTensor<float>& x, int64_t curProcessCount, int64_t appendNum, float appendValue = 0.0f)
     286              : {
     287          152 :     if (appendNum <= 0) {
     288          121 :         return;
     289              :     }
     290          808 :     for (int64_t i = curProcessCount - appendNum; i < curProcessCount; ++i) {
     291          777 :         x.SetValue(i, appendValue);
     292              :     }
     293           31 :     pipe_barrier(PIPE_ALL);
     294              : }
     295              : 
     296              : // 将输入 x cast 到 float32(b8 走 half 中转),返回 float32 tensor;float 原样返回
     297              : template <typename T>
     298          483 : __aicore__ inline LocalTensor<float> CastXToFloat32(
     299              :     LocalTensor<T>& x, TBuf<TPosition::VECCALC>& castXBuf, int64_t curProcessCount)
     300              : {
     301              :     if constexpr (std::is_same_v<T, float>) {
     302          313 :         return x;
     303              :     } else if constexpr (std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>) {
     304           26 :         LocalTensor<half> float16X = castXBuf.Get<half>();
     305           26 :         Cast(float16X, x, RoundMode::CAST_NONE, curProcessCount);
     306           26 :         pipe_barrier(PIPE_ALL);
     307              : 
     308           26 :         LocalTensor<uint8_t> float32Addr = castXBuf.Get<uint8_t>()[curProcessCount * sizeof(half)];
     309           26 :         LocalTensor<float> float32X = float32Addr.ReinterpretCast<float>();
     310           26 :         Cast(float32X, float16X, RoundMode::CAST_NONE, curProcessCount);
     311           26 :         return float32X;
     312              :     } else {
     313              :         // int16/int32/half/bfloat16 及支持的 fp8 类型统一直接 cast 到 float32
     314          144 :         LocalTensor<float> float32X = castXBuf.Get<float>();
     315          144 :         Cast(float32X, x, RoundMode::CAST_NONE, curProcessCount);
     316          144 :         return float32X;
     317              :     }
     318              : }
     319              : 
     320              : // 不支持的类型(整型)统计项:结果置 0 并释放队列
     321              : template <typename T>
     322           81 : __aicore__ inline void ResetStatValueAndFree(
     323              :     TQue<QuePosition::VECIN, BUFFER_NUM>& xQue, LocalTensor<T>& x, LocalTensor<uint8_t>& calMidAddr)
     324              : {
     325           81 :     LocalTensor<int32_t> curValue = calMidAddr[sizeof(float)].ReinterpretCast<int32_t>();
     326           81 :     curValue.SetValue(0, 0);
     327           81 :     xQue.FreeTensor(x);
     328           81 : }
     329              : 
     330              : // 将 cur*Addr 处的统计结果搬到 calMidAddr 首地址,供 CopyOutToWorkspace 使用
     331              : template <typename OutputT>
     332          941 : __aicore__ inline void WriteStatOutput(LocalTensor<uint8_t>& calMidAddr, LocalTensor<uint8_t>& curValueAddr)
     333              : {
     334          941 :     LocalTensor<OutputT> targetValue = curValueAddr.ReinterpretCast<OutputT>();
     335          941 :     OutputT outputValue = targetValue.GetValue(0);
     336          941 :     LocalTensor<OutputT> localOutput = calMidAddr.ReinterpretCast<OutputT>();
     337          941 :     localOutput.SetValue(0, outputValue);
     338          941 :     pipe_barrier(PIPE_ALL);
     339          941 : }
     340              : 
     341              : // 整型是否为 nan/inf 统计不支持的类型
     342              : template <typename T>
     343              : struct IsIntType {
     344              :     static constexpr bool value = std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t> ||
     345              :                                   std::is_same_v<T, int16_t> || std::is_same_v<T, int32_t>;
     346              : };
     347              : 
     348              : // 浮点统计骨架:取数 -> 整型早退 -> 尾块填充 -> cast 到 float32 -> 回调 ComputeFunc -> 释放。
     349              : // nan/inf 等仅支持浮点输入的统计项复用该骨架,只需提供各自的 ComputeFunc
     350              : template <typename T, typename ComputeFunc>
     351          597 : __aicore__ inline void ComputeFloatStatSkeleton(
     352              :     TQue<QuePosition::VECIN, BUFFER_NUM>& xQue, TBuf<TPosition::VECCALC>& castXBuf,
     353              :     TQue<QuePosition::VECOUT, BUFFER_NUM>& workQueue, TBuf<TPosition::VECCALC>& maskBuf,
     354              :     TBuf<TPosition::VECCALC>& cacheBuf1, LocalTensor<uint8_t>& calMidAddr, int64_t curLoop, int64_t curProcessCount,
     355              :     int64_t appendNum, ComputeFunc computeFunc)
     356              : {
     357          597 :     LocalTensor<T> x = xQue.DeQue<T>();
     358              :     if constexpr (IsIntType<T>::value) {
     359              :         // 整型不支持该统计项,结果置 0
     360           81 :         ResetStatValueAndFree<T>(xQue, x, calMidAddr);
     361           81 :         return;
     362              :     }
     363              : 
     364          516 :     AppendTailPadding<T>(x, curProcessCount, appendNum);
     365              : 
     366              :     if constexpr (std::is_same_v<T, float>) {
     367          468 :         computeFunc(workQueue, maskBuf, cacheBuf1, x, calMidAddr, curLoop, curProcessCount);
     368              :     } else {
     369              :         // half/bfloat16 及支持的 fp8 类型统一 cast 到 float32 后统计
     370           48 :         LocalTensor<float> float32X = CastXToFloat32<T>(x, castXBuf, curProcessCount);
     371           48 :         AppendTailPaddingFp32(float32X, curProcessCount, appendNum);
     372           48 :         pipe_barrier(PIPE_ALL);
     373           48 :         computeFunc(workQueue, maskBuf, cacheBuf1, float32X, calMidAddr, curLoop, curProcessCount);
     374              :     }
     375              : 
     376          516 :     xQue.FreeTensor(x);
     377          516 : }
     378              : 
     379              : } // namespace KfcDumpStat
     380              : 
     381              : #endif // __KFC_DUMP_BASE_H__
        

Generated by: LCOV version 2.0-1