// 支持ND2NZ格式转换 template <typename T> __aicore__ inline void DataCopy(const LocalTensor<T>& dstLocal, const GlobalTensor<T>& srcGlobal, const Nd2NzParams& intriParams);
该原型接口支持的数据通路和数据类型如下所示:
支持型号 |
数据通路 |
源操作数和目的操作数的数据类型 (两者保持一致) |
---|---|---|
Atlas推理系列产品AI Core |
GM -> VECIN |
int8_t / uint8_t / int16_t / uint16_t / int32_t / uint32_t / half / float |
Atlas推理系列产品AI Core |
GM -> A1 / B1 |
int8_t / uint8_t / int16_t / uint16_t / int32_t / uint32_t / half / float |
Atlas A2训练系列产品/Atlas 800I A2推理产品 |
GM -> VECIN |
int8_t / uint8_t / int16_t / uint16_t / int32_t / uint32_t / half / float |
Atlas A2训练系列产品/Atlas 800I A2推理产品 |
GM -> A1 / B1 |
int8_t / uint8_t / int16_t / uint16_t / int32_t / uint32_t / half / float |
注意:使用该接口时需要预留8K的Unified Buffer空间,作为接口的临时数据存放区
// 支持NZ2ND格式转换 template <typename T> __aicore__ inline void DataCopy(const GlobalTensor <T>& dstGlobal, const LocalTensor <T>& srcLocal, const Nz2NdParamsFull &intriParams);
该原型接口支持的数据通路和数据类型如下所示:
支持型号 |
数据通路 |
源操作数和目的操作数的数据类型 (两者保持一致) |
---|---|---|
Atlas推理系列产品AI Core |
VECOUT -> GM |
int16_t / uint16_t / int32_t / uint32_t / half / float |
Atlas推理系列产品AI Core |
CO2 -> GM |
int16_t / uint16_t / int32_t / uint32_t / half / float |
Atlas A2训练系列产品/Atlas 800I A2推理产品 |
VECOUT -> GM |
int16_t / uint16_t / int32_t / uint32_t / half / float |
// 可以通过参数控制使能NZ2ND功能,同时包含量化、relu等功能 template <typename T, typename U> __aicore__ inline void DataCopy(const GlobalTensor<T>& dstGlobal, const LocalTensor<U>& srcLocal, const DataCopyCO12DstParams& intriParams);
该原型接口支持的数据通路和数据类型如下所示:
支持型号 |
数据通路 |
源操作数的数据类型 |
目的操作数的数据类型 |
---|---|---|---|
Atlas A2训练系列产品/Atlas 800I A2推理产品 |
CO1 -> GM |
float |
uint8_t/int8_t/half/bfloat16_t/float |
Atlas A2训练系列产品/Atlas 800I A2推理产品 |
CO1 -> GM |
int32_t |
uint8_t/int8_t/half/int16_t/int32_t |
// 支持ND2NZ格式转换 template <typename T> __aicore__ inline void DataCopy(const LocalTensor<T> &dstLocal, const LocalTensor<T> &srcGlobal, const Nd2NzParams& intriParams);
该原型接口支持的数据通路和数据类型如下所示:
支持型号 |
数据通路 |
源操作数和目的操作数的数据类型 (两者保持一致) |
---|---|---|
Atlas A2训练系列产品/Atlas 800I A2推理产品 |
VECIN / VECCALC / VECOUT -> TSCM |
int8_t / uint8_t / int16_t / uint16_t / int32_t / uint32_t / half / float |
template <typename T, typename U> __aicore__ inline void DataCopy(const LocalTensor<T>& dstLocal, const LocalTensor<U>& srcLocal, const DataCopyCO12DstParams& intriParams);
该原型接口支持的数据通路和数据类型如下所示:
支持型号 |
数据通路 |
源操作数的数据类型 |
目的操作数的数据类型 |
---|---|---|---|
Atlas A2训练系列产品/Atlas 800I A2推理产品 |
CO1 -> A1 |
float |
uint8_t/int8_t/half/bfloat16_t |
Atlas A2训练系列产品/Atlas 800I A2推理产品 |
CO1 -> A1 |
int32_t |
uint8_t/int8_t/half/int16_t |
参数名称 |
输入/输出 |
含义 |
---|---|---|
dstLocal, dstGlobal |
输出 |
目的操作数,类型为LocalTensor或GlobalTensor。支持的数据类型为:half/int16_t/uint16_t/float/int32_t/uint32_t/int8_t/uint8_t。 |
srcLocal, srcGlobal |
输入 |
源操作数,类型为LocalTensor或GlobalTensor。支持的数据类型为:half/int16_t/uint16_t/float/int32_t/uint32_t/int8_t/uint8_t。 |
intriParams |
输入 |
搬运参数,类型为Nd2NzParams / Nz2NdParamsFull / DataCopyCO12DstParams。 |
参数名称 |
含义 |
---|---|
ndNum |
传输nd矩阵的数目,取值范围:ndNum∈[0, 4095]。 |
nValue |
nd矩阵的行数,取值范围:nValue∈[0, 16384]。 |
dValue |
nd矩阵的列数,取值范围:dValue∈[0, 65535]。 |
srcNdMatrixStride |
源操作数相邻nd矩阵起始地址间的偏移,取值范围:srcNdMatrixStride∈[0, 65535],单位:element。 |
srcDValue |
源操作数同一nd矩阵的相邻行起始地址间的偏移,取值范围:srcDValue∈[1, 65535],单位:element。 |
dstNzC0Stride |
ND转换到NZ格式后,源操作数中的一行会转换为目的操作数的多行。dstNzC0Stride表示,目的nz矩阵中,来自源操作数同一行的多行数据相邻行起始地址间的偏移,取值范围:dstNzC0Stride∈[1, 16384],单位:C0_SIZE(32B)。 |
dstNzNStride |
目的nz矩阵中,Z型矩阵相邻行起始地址之间的偏移。取值范围:dstNzNStride∈[1, 16384],单位:C0_SIZE(32B)。 |
dstNzMatrixStride |
目的nz矩阵中,相邻nz矩阵起始地址间的偏移,取值范围:dstNzMatrixStride∈[1, 65535],单位:element。 |
ND2NZ转换示意图如下,样例中参数设置值和解释说明如下:
参数名称 |
含义 |
---|---|
ndNum |
传输nz矩阵的数目,取值范围:ndNum∈[0, 4095]。 |
nValue |
nz矩阵的行数,取值范围:nValue∈[1, 8192]。 |
dValue |
nz矩阵的列数,取值范围:dValue∈[1, 8192]。dValue必须为16的倍数。 |
srcNdMatrixStride |
源相邻nz矩阵的偏移(头与头),取值范围:srcNdMatrixStride∈[1, 512],单位256 (16 * 16) 个元素。 |
srcNStride |
源同一nz矩阵的相邻z排布的偏移(头与头),取值范围:srcNStride∈[0, 4096],单位16个元素。 |
dstDStride |
目的nd矩阵的相邻行的偏移(头与头),取值范围:dstDStride∈[1, 65535],单位:element。 |
dstNdMatrixStride |
目的nd矩阵中,来自源相邻nz矩阵的偏移(头与头),取值范围:dstNdMatrixStride∈[1, 65535],单位:element。 |
以half数据类型为例,NZ2ND转换示意图如下,样例中参数设置值和解释说明如下:
以float数据类型为例,NZ2ND转换示意图如下,样例中参数设置值和解释说明如下:
下文中的C0取值:一般情况下,C0=16;channelSplit(channel切分)使能时,C0=8。
参数名称 |
含义 |
---|---|
nSize |
srcLocal横向方向的size大小。
|
mSize |
srcLocal纵向方向的size大小。 |
dstStride |
|
srcStride |
|
quantPre |
类型为QuantMode_t,默认值为QuantMode_t::NoQuant,即不使能量化功能。 QuantMode_t是一个枚举类型,用以控制量化模式,具体定义为:
enum QuantMode_t { NoQuant, // 不使能量化功能 F322F16, // float量化成half F322BF16, // float量化成bfloat16_t DEQF16, // int32_t量化成half, scalar量化 VDEQF16, // int32_t量化成half,tensor量化 QF322B8_PRE, // float量化成int8_t/uint8_t,scalar量化 VQF322B8_PRE, // float量化成int8_t/uint8_t,tensor量化 REQ8, // int32_t量化成int8_t/uint8_t,tensor量化 VREQ8, // int32_t量化成int8_t/uint8_t,tensor量化 }; |
reluPre |
类型为uint8_t,配置relu操作的模式。
|
channelSplit |
类型为bool,配置是否使能channel切分,对于float类型的dstLocal生效。
|
nz2ndEn |
类型为bool,配置是否使能NZ2ND的格式转换,仅在L0C->GM通路生效。 如果要使能NZ2ND的功能需要同步调用SetFixpipeNz2ndFlag来设置格式转换的相关配置信息。
|
Atlas推理系列产品AI Core
Atlas A2训练系列产品/Atlas 800I A2推理产品
#include "kernel_operator.h" namespace AscendC { class KernelDataCopyUb2GmNz2Nd { public: __aicore__ inline KernelDataCopyUb2GmNz2Nd() {} __aicore__ inline void Init(__gm__ uint8_t* dstGm, __gm__ uint8_t* srcGm) { Nz2NdParamsFull intriParamsIn{1, 32, 32, 1, 32, 32, 1}; intriParams = intriParamsIn; srcGlobal.SetGlobalBuffer((__gm__ half *)srcGm); dstGlobal.SetGlobalBuffer((__gm__ half *)dstGm); pipe.InitBuffer(inQueueSrcVecIn, 1, intriParams.nValue * intriParams.dValue * sizeof(half)); pipe.InitBuffer(inQueueSrcVecOut, 1, intriParams.nValue * intriParams.dValue * sizeof(half)); } __aicore__ inline void Process() { CopyIn(); Compute(); CopyOut(); } private: __aicore__ inline void CopyIn() { LocalTensor<half> srcLocal = inQueueSrcVecIn.AllocTensor<half>(); DataCopy(srcLocal, srcGlobal, intriParams.nValue * intriParams.dValue); inQueueSrcVecIn.EnQue(srcLocal); } __aicore__ inline void Compute() { LocalTensor<half> dstLocal = inQueueSrcVecIn.DeQue<half>(); LocalTensor<half> srcOutLocal = inQueueSrcVecOut.AllocTensor<half>(); DataCopy(srcOutLocal, dstLocal, intriParams.nValue * intriParams.dValue); inQueueSrcVecOut.EnQue(srcOutLocal); inQueueSrcVecIn.FreeTensor(dstLocal); } __aicore__ inline void CopyOut() { LocalTensor<half> srcOutLocalDe = inQueueSrcVecOut.DeQue<half>(); DataCopy(dstGlobal, srcOutLocalDe, intriParams); inQueueSrcVecOut.FreeTensor(srcOutLocalDe); } private: TPipe pipe; TQue<QuePosition::VECIN, 1> inQueueSrcVecIn; TQue<QuePosition::VECOUT, 1> inQueueSrcVecOut; GlobalTensor<half> srcGlobal; GlobalTensor<half> dstGlobal; Nz2NdParamsFull intriParams; }; } // namespace AscendC extern "C" __global__ __aicore__ void kernel_data_copy_nz2nd_ub2out(__gm__ uint8_t* src_gm, __gm__ uint8_t* dst_gm) { AscendC::KernelDataCopyUb2GmNz2Nd op; op.Init(dst_gm, src_gm); op.Process(); }
结果示例:
输入数据(srcGlobal): [1 2 3 ... 1024] 输出数据(dstGlobal):[1 2 ... 15 16 513 514 ... 527 528 17 18 ... 31 32 529 530 ... 543 544 ...497 498 ... 511 512 1009 1010... 1023 1024]
#ifdef ASCENDC_CPU_DEBUG #include "tikicpulib.h" #endif #include "kernel_operator.h" #include "../../instrs/common_utils/register_utils.h" using namespace AscendC; SET_G_CORE_TYPE_IS_AIC namespace AscendC { template <typename dst_T, typename fmap_T, typename weight_T, typename dstCO1_T> class KernelCubeDataCopy{ public: __aicore__ inline KernelCubeDataCopy(uint16_t CoutIn, uint8_t dilationHIn, uint8_t dilationWIn, QuantMode_t deqModeIn) { // ceiling of 16 Cout = CoutIn; dilationH = dilationHIn; dilationW = dilationWIn; C0 = 32 / sizeof(fmap_T); C1 = channelSize / C0; coutBlocks = (Cout + 16 - 1) / 16; ho = H - dilationH * (Kh - 1); wo = W - dilationW * (Kw - 1); howo = ho * wo; howoRound = ((howo + 16 - 1) / 16) * 16; featureMapA1Size = C1 * H * W * C0; // shape: [C1, H, W, C0] weightA1Size = C1 * Kh * Kw * Cout * C0; // shape: [C1, Kh, Kw, Cout, C0] featureMapA2Size = howoRound * (C1 * Kh * Kw * C0); weightB2Size = (C1 * Kh * Kw * C0) * coutBlocks * 16; m = howo; k = C1 * Kh * Kw * C0; n = Cout; biasSize = Cout; // shape: [Cout] dstSize = coutBlocks * howo * 16; // shape: [coutBlocks, howo, 16] dstCO1Size = coutBlocks * howoRound * 16; fmRepeat = featureMapA2Size / (16 * C0); weRepeat = weightB2Size / (16 * C0); deqMode = deqModeIn; } __aicore__ inline void Init(__gm__ uint8_t* fmGm, __gm__ uint8_t* weGm, __gm__ uint8_t* biasGm, __gm__ uint8_t* deqGm, __gm__ uint8_t* dstGm) { fmGlobal.SetGlobalBuffer((__gm__ fmap_T*)fmGm); weGlobal.SetGlobalBuffer((__gm__ weight_T*)weGm); biasGlobal.SetGlobalBuffer((__gm__ dstCO1_T*)biasGm); deqGlobal.SetGlobalBuffer((__gm__ uint64_t*)deqGm); dstGlobal.SetGlobalBuffer((__gm__ dst_T*)dstGm); pipe.InitBuffer(inQueueFmA1, 1, featureMapA1Size * sizeof(fmap_T)); pipe.InitBuffer(inQueueFmA2, 1, featureMapA2Size * sizeof(fmap_T)); pipe.InitBuffer(inQueueWeB1, 1, weightA1Size * sizeof(weight_T)); pipe.InitBuffer(inQueueWeB2, 1, weightB2Size * sizeof(weight_T)); pipe.InitBuffer(inQueueBiasA1, 1, biasSize * sizeof(dstCO1_T)); pipe.InitBuffer(inQueueDeqA1, 1, dstCO1Size * sizeof(uint64_t)); pipe.InitBuffer(inQueueDeqFB, 1, dstCO1Size * sizeof(uint64_t)); pipe.InitBuffer(outQueueCO1, 1, dstCO1Size * sizeof(dstCO1_T)); pipe.InitBuffer(outQueueA1, 1, dstCO1Size * sizeof(dst_T)); } __aicore__ inline void Process() { CopyIn(); Split(); Compute(); CopyOut(); } private: __aicore__ inline void CopyIn() { LocalTensor<fmap_T> featureMapA1 = inQueueFmA1.AllocTensor<fmap_T>(); LocalTensor<weight_T> weightB1 = inQueueWeB1.AllocTensor<weight_T>(); LocalTensor<dstCO1_T> biasA1 = inQueueBiasA1.AllocTensor<dstCO1_T>(); DataCopy(featureMapA1, fmGlobal, { 1, static_cast<uint16_t>(featureMapA1Size * sizeof(fmap_T) / 32), 0, 0 }); DataCopy(weightB1, weGlobal, { 1, static_cast<uint16_t>(weightA1Size * sizeof(weight_T) / 32), 0, 0 }); DataCopy(biasA1, biasGlobal, { 1, static_cast<uint16_t>(biasSize * sizeof(dstCO1_T) / 32), 0, 0 }); inQueueFmA1.EnQue(featureMapA1); inQueueWeB1.EnQue(weightB1); inQueueBiasA1.EnQue(biasA1); } __aicore__ inline void Split() { LocalTensor<fmap_T> featureMapA1 = inQueueFmA1.DeQue<fmap_T>(); LocalTensor<weight_T> weightB1 = inQueueWeB1.DeQue<weight_T>(); LocalTensor<fmap_T> featureMapA2 = inQueueFmA2.AllocTensor<fmap_T>(); LocalTensor<weight_T> weightB2 = inQueueWeB2.AllocTensor<weight_T>(); uint8_t padList[kPadSize] = {0, 0, 0, 0}; // load3dv2 LoadData(featureMapA2, featureMapA1, { padList, H, W, channelSize, k, howoRound, 0, 0, 1, 1, Kw, Kh, dilationW, dilationH, false, false, 0 }); // load2d LoadData(weightB2, weightB1, { 0, weRepeat, 1, 0, 0, false, 0 }); inQueueFmA2.EnQue<fmap_T>(featureMapA2); inQueueWeB2.EnQue<weight_T>(weightB2); inQueueFmA1.FreeTensor(featureMapA1); inQueueWeB1.FreeTensor(weightB1); } __aicore__ inline void Compute() { LocalTensor<fmap_T> featureMapA2 = inQueueFmA2.DeQue<fmap_T>(); LocalTensor<weight_T> weightB2 = inQueueWeB2.DeQue<weight_T>(); LocalTensor<dstCO1_T> dstCO1 = outQueueCO1.AllocTensor<dstCO1_T>(); LocalTensor<dstCO1_T> biasA1 = inQueueBiasA1.DeQue<dstCO1_T>(); // C = A * B + bias // m: 左矩阵Height, k: 左矩阵Width, n: 右矩阵Width Mmad(dstCO1, featureMapA2, weightB2, biasA1, { m, n, k, true, 0, false, false, false }); outQueueCO1.EnQue<dstCO1_T>(dstCO1); inQueueFmA2.FreeTensor(featureMapA2); inQueueWeB2.FreeTensor(weightB2); } __aicore__ inline void CopyOut() { LocalTensor<dstCO1_T> dstCO1 = outQueueCO1.DeQue<dstCO1_T>(); LocalTensor<dst_T> dstA1 = outQueueA1.DeQue<dst_T>(); // 使能DEQF16量化,量化参数设置为0.5 float tmp = (float)0.5; // 将float的tmp转换成uint64_t的deqScalar uint64_t deqScalar = static_cast<uint64_t>(*reinterpret_cast<int32_t*>(&tmp)); bool nz2ndEn = false; // nz2nd不使能时,nSize必须为16的倍数 uint16_t nSize = coutBlocks * 16; uint16_t mSize = m; // srcStride必须为16的倍数 uint16_t srcStride = (m + 16 - 1) / 16 * 16; // nz2nd不使能时,dstStride为burst头到头的距离,且为32B对齐 uint32_t dstStride = m * sizeof(dst_T) * 16 / 32; if (nz2ndEn) { // nd矩阵的数量为1,src_nd_stride与dst_nd_stride填0 SetFixpipeNz2ndFlag(1, 0, 0); // nz2nd使能时,nSize可以不为16的倍数,与Mmad的n保持一致 nSize = n; // nz2nd使能时,dstStride表示同一nd矩阵的相邻连续行的间隔,与n保持一致 dstStride = nSize; }; // 不使能relu与channelSplit DataCopyCO12DstParams intriParams(nSize, mSize, dstStride, srcStride, deqMode, 0, false, nz2ndEn); // mov l0c to gm, deq scalar quant SetFixpipePreQuantFlag(deqScalar); // 设置量化参数 PipeBarrier<PIPE_FIX>(); DataCopy(dstGlobal, dstCO1, intriParams); // // mov l0c to gm, deq tensor quant // // 需要额外申请deq tensor的gm空间,将值搬运到workA1 // LocalTensor<uint64_t> workA1 = inQueueDeqA1.DeQue<uint64_t>(); // // deq tensor的size // uint16_t deqSize = 128; // DataCopy(workA1, deqGlobal, deqSize); // // deq tensor在fix上的地址 // LocalTensor<uint64_t> deqFB = inQueueDeqFB.AllocTensor<uint64_t>(); // // l1->fix, burst_len unit is 128Bytes // uint16_t fbufBurstLen = deqSize / 128; // DataCopyParams dataCopyParams(1, fbufBurstLen, 0, 0); // DataCopy(deqFB, workA1, dataCopyParams); // // 设置量化tensor // SetFixPipeConfig(deqFB); // PipeBarrier<PIPE_FIX>(); // DataCopy(dstGlobal, dstCO1, intriParams); // inQueueDeqA1.FreeTensor(workA1); // inQueueDeqFB.FreeTensor(deqFB); // // mov l0c to l1, deq scalar quant, and then mov l1 to gm // SetFixpipePreQuantFlag(deqScalar); // 设置量化参数 // PipeBarrier<PIPE_FIX>(); // DataCopy(dstA1, dstCO1, intriParams); // DataCopy(dstGlobal, dstA1, dstCO1Size); // // mov l0c to l1, deq tensor quant, and then mov l1 to gm // LocalTensor<uint64_t> workA1 = inQueueDeqA1.DeQue<uint64_t>(); // uint16_t deqSize = 128; // DataCopy(workA1, deqGlobal, deqSize); // LocalTensor<uint64_t> deqFB = inQueueDeqFB.AllocTensor<uint64_t>(); // uint16_t fbufBurstLen = deqSize / 128; // DataCopyParams dataCopyParams(1, fbufBurstLen, 0, 0); // DataCopy(deqFB, workA1, dataCopyParams); // // 设置量化tensor // SetFixPipeConfig(deqFB); // PipeBarrier<PIPE_FIX>(); // DataCopy(dstA1, dstCO1, intriParams); // DataCopy(dstGlobal, dstA1, dstCO1Size); // inQueueDeqA1.FreeTensor(workA1); // inQueueDeqFB.FreeTensor(deqFB); // outQueueCO1.FreeTensor(dstCO1); // outQueueA1.FreeTensor(dstA1); } private: TPipe pipe; // feature map queue TQue<QuePosition::A1, 1> inQueueFmA1; TQue<QuePosition::A2, 1> inQueueFmA2; // weight queue TQue<QuePosition::B1, 1> inQueueWeB1; TQue<QuePosition::B2, 1> inQueueWeB2; // bias queue TQue<QuePosition::A1, 1> inQueueBiasA1; // deq tensor queue TQue<QuePosition::A1, 1> inQueueDeqA1; // fb dst of deq tensor TQue<QuePosition::C2PIPE2GM, 1> inQueueDeqFB; // dst queue TQue<QuePosition::CO1, 1> outQueueCO1; TQue<QuePosition::A1, 1> outQueueA1; GlobalTensor<fmap_T> fmGlobal; GlobalTensor<weight_T> weGlobal; GlobalTensor<dst_T> dstGlobal; GlobalTensor<uint64_t> deqGlobal; GlobalTensor<dstCO1_T> biasGlobal; uint16_t channelSize = 32; uint16_t H = 4, W = 4; uint8_t Kh = 2, Kw = 2; uint16_t Cout; uint16_t C0, C1; uint8_t dilationH, dilationW; uint16_t coutBlocks, ho, wo, howo, howoRound; uint32_t featureMapA1Size, weightA1Size, featureMapA2Size, weightB2Size, biasSize, dstSize, dstCO1Size; uint16_t m, k, n; uint8_t fmRepeat, weRepeat; uint8_t kPadSize = 4; QuantMode_t deqMode = QuantMode_t::NoQuant; }; } // namespace AscendC #define KERNEL_CUBE_DATACOPY(dst_type, fmap_type, weight_type, dstCO1_type, CoutIn, dilationHIn, dilationWIn, deqModeIn) \ extern "C" __global__ __aicore__ void cube_datacopy_kernel_##fmap_type(__gm__ uint8_t* fmGm, __gm__ uint8_t* weGm, \ __gm__ uint8_t* biasGm, __gm__ uint8_t* deqGm, __gm__ uint8_t* dstGm) \ { \ if (g_coreType == AIV) { \ return; \ } \ AscendC::KernelCubeDataCopy<dst_type, fmap_type, weight_type, dstCO1_type> op(CoutIn, dilationHIn, dilationWIn, \ deqModeIn); \ op.Init(fmGm, weGm, biasGm, deqGm, dstGm); \ op.Process(); \ } KERNEL_CUBE_DATACOPY(half, int8_t, int8_t, int32_t, 128, 1, 1, QuantMode_t::DEQF16);