参考实现
设计思路
本节以BatchNorm算子为例,介绍通过TIK方式进行算子开发的设计思路。
算子分析
BatchNorm算子主要包括以下两部分:
- 对输入进行归一化处理,xnorm = (x − μ) / σ,其中, μ和 σ是计算的均值和方差。
- 归一化后进行缩放和平移,得到输出y = γ * xnorm + β。
本样例重点关注的是算子动态shape场景,因此仅实现了归一化处理部分。
算子规格
参数 |
规格 |
---|---|
支持框架 |
Caffe |
格式 |
单算子调用场景设计,采用NCHW格式。 |
输入数据类型 |
用户自行设计。当前样例为float16。 |
N |
用户自行设计。当前样例支持单Batch,即N=1。 |
C |
用户自行设计。当前样例为0~1024通道。 |
H |
用户自行设计。当前样例为0~1024像素。 |
W |
用户自行设计。当前样例为0~1024像素。 |
shape |
支持任意shape。 |
Tiling设计
场景划分 |
策略 |
---|---|
C通道并行度占优,C > H*W |
Tiling_1:按照C通道方向并行逐像素点计算,通道设计需考虑32Byte对齐,采用Pingpong以及多核优化技术。 |
单通道HW并行度占优,C < H*W |
Tiling_2:单featuremap的H*W*C(C=2)>112KB时,则一次最多能搬运1个通道,按照单通道方向切分featmap来计算BN,考虑Global Memory与Unified Buffer间PingPong操作。 说明:112KB是结合pingpong流水线考虑后的Size值,为Unified Buffer空间的一半并保留一定大小的Buffer缓存空间,下文不再赘述。 |
Tiling_3:单featuremap的H*W*C(C>1) <= 112KB时,一次可搬运多个通道,Global Memory与Unified Buffer间pingpong操作,不同通道间可考虑多核优化。 |
下面分别介绍各种场景的详细设计思路:
- C通道并行场景设计思路。
对于C > HW场景,例如NCHW为1x1024x8x6时,C通道并行度占优,需要充分利用C通道并行逐像素计算每个点BatchNorm值。性能提升方面考虑pingpong流水线操作与多核并行技术,提高Unified Buffer利用率。
- Size(N*C*H*W) < 112KB, 只需1次Global Memory->Unified Buffer->Global Memory搬运,不需要考虑HW平面上的Tiling策略。
- Size(N*C*H*W) > 112KB, 需多次Global Memory->Unified Buffer->Global Memory搬运,需要考虑HW平面上的Tiling策略。当H*W或C不满足32Byte对齐时,需要补成32Byte对齐。
注:图中的M=16倍数,是针对当前例子的float16类型数据进行32Byte对齐。
- 单通道HW并行场景设计思路。
- 当Size (H*W) < 112KB且Size (H*W*2) > 112KB时,仅考虑在单featuremap上逐像素计算每个点BatchNorm值。
- 当Size(H*W) > 112KB时,需考虑featuremap上Tiling策略。
设计时考虑Global Memory->Unified Buffer->Global Memory的PingPong操作,提高单位时间内Vector利用率,减少Vector等待时间。
- 多通道HW并行场景设计思路。
考虑Unified Buffer空间大小, 当Size (H*W*C) (C > 1) < 112KB时,可一次搬运多个通道的数据,C通道方向可以考虑多核并行计算。
设计时考虑Global Memory->Unified Buffer->Global Memory的PingPong操作,提高单位时间内Vector利用率,减少Vector等待时间。
算子实现
本节介绍如何针对每一种Tiling策略分别进行算子实现。BatchNorm算子的完整实现代码请参考样例使用获取。
代码结构
不同Tiling策略下,代码结构类似,如下所示:
#导入依赖的Python模块 from te import tik import numpy as np #常量定义,注意不同场景下MAX_WIDTH和MAX_HEIGHT的值有差异 #size of 310 ai core ub buffer UB_SIZE = 240 * 1024 #batch for N MAX_BATCH = 1 #channel for C MAX_CHANNEL = 1024 #width for W MAX_WIDTH = 32 #height for H MAX_HEIGHT = 32 class BatchNorm(): #初始化函数 def __init__(self, input0, gamma0, beta0, output0, kernel_name = "BatchNorm"): """ 初始化函数实现 """ #算子计算逻辑实现和编译 def batchnorm_compute(self): # 不同Tiling策略下计算逻辑不同 self.batchnorm_compute_tiling() # 算子编译 self.tik_instance.BuildCCE(kernel_name=self.kernel_name, inputs=[self.input_gm, self.gamma_gm, self.beta_gm], outputs=[self.output_gm], flowtable=[self.input_n, self.input_c, self.input_h, self.input_w, self.inputtype, self.output_n, self.output_c, self.output_h, self.output_w, self.outputtype, self.gamma_c, self.gammatype, self.beta_c, self.betatype, self.param1, self.param2, self.param3, self.param4, self.param5, self.param6, self.param7, self.param8, self.param9, self.param10], enable_l2=True) return self.tik_instance #算子定义函数 def batch_norm(input0, gamma0, beta0, output0, kernel_name = "BatchNorm"): obj = BatchNorm(input0, gamma0, beta0, output0, kernel_name) obj.batchnorm_compute()
如下几点需要关注:
- 不同Tiling策略下,代码结构类似,主要区别点在于不同Tiling策略下计算逻辑不同,具体请参考C通道并行、单通道HW并行、多通道HW并行。
- 通过BuildCCE接口编译TIK算子时,可以传入flowtable参数。该参数为算子选择器计算好的Tiling参数,相当于在output容器后增加一段用户缓存Tiling参数的地址空间,flowtable列表中的Tiling参数个数和inputs的参数个数之和小于等于64,每个Tiling参数为TIK InputScalar类型,Tiling参数不在TIK中计算,将重复的Scalar运算全部转移到Host CPU上的算子选择器计算,减少AI Core上Scalar运算量,有利于流水线并行。
#C通道并行计算 def batchnorm_compute_tiling_c(self): with self.tik_instance.if_scope(total_use_ub <= UB_SIZE): # Size(N*C*H*W) < 112KB,只需1次搬运 #准备Unified Buffer空间数据 ...... #CHW->HWC->CHW, 将数据从CHW转换为HWC,利用C通道连续数据进行计算,计算完毕后从 #HWC->CHW,搬运到Global Memory空间。 with self.tik_instance.for_range(0, align_wh // 16, thread_num = 2) as k: #transfer (chn_num, 16) to (16, chn_num) #[align_c, 16] with self.tik_instance.for_range(0, align_wh // 16, thread_num = 2) as k: #transfer (chn_num, 16) to (16, chn_num) #[align_c, 16] src_list = [data_ub[0*align_wh + k*16], ..., data_ub[15*align_wh + k*16]] #[align_wh, chn_num] dst_list = [trans_ub[(k*16 + 0)*align_c],trans_ub[(k*16 + 15)*align_c]] #CHW->HWC self.tik_instance.vec_trans_scatter(True, True, dst_list, src_list, align_c // 16, 1, align_wh) #calc batchnorm (x - mean)/var with self.tik_instance.for_range (0, align_wh, thread_num = 2) as m: self.tik_instance.vec_add(16, tmp_ub, trans_ub[m*align_c], gamma_ub, align_c // 16, 1, 1, 1) self.tik_instance.vec_mul(16, trans_ub[m*align_c], tmp_ub, beta_ub, align_c // 16, 1, 1, 1) with self.tik_instance.for_range(0, align_wh // 16, thread_num = 2) as k: #[align_wh, chn_num] src_list = [trans_ub[(k*16 + 0)*align_c], ... trans_ub[(k*16 + 15)*align_c]] #[chn_num, align_wh] dst_list = [data_ub[0*align_wh + k*16], ... data_ub[15*align_wh + k*16]] #HWC->CHW self.tik_instance.vec_trans_scatter(True, True, dst_list, src_list, align_c // 16, align_wh, 1) with self.tik_instance.else_scope(): # Size(N*C*H*W) > 112KB,需多次搬运 #准备Unified Buffer空间数据 ...... #Cx16->16xC->Cx16, 将数据从CHW转换为HWC,利用C通道连续数据进行计算,H*W方向上每次只搬运16个 #FP16数据,计算完毕后转换成HWC->CHW,搬运到Global Memory空间, 注H*W必须满足32Byte对齐。 src_list = [ping_ub[0, 0], ping_ub[1, 0], ping_ub[2, 0], ping_ub[3, 0], ping_ub[4, 0], ping_ub[5, 0], ping_ub[6, 0], ping_ub[7, 0], ping_ub[8, 0], ping_ub[9, 0], ping_ub[10, 0], ping_ub[11, 0], ping_ub[12, 0], ping_ub[13, 0], ping_ub[14, 0], ping_ub[15, 0]] #[16, align_c] dst_list = [trans_ub[0, 0], trans_ub[1, 0], trans_ub[2, 0], trans_ub[3, 0], trans_ub[4, 0], trans_ub[5, 0], trans_ub[6, 0], trans_ub[7, 0], trans_ub[8, 0], trans_ub[9, 0], trans_ub[10, 0], trans_ub[11, 0], trans_ub[12, 0], trans_ub[13, 0], trans_ub[14, 0],trans_ub[15, 0]] # transpose Cx16 to 16 x C self.tik_instance.vec_trans_scatter(True, True, dst_list, src_list, repeat_alignc, 1, 16) #计算均值与方差: (x - mean)/var with self.tik_instance.for_range (0, 16) as m: self.tik_instance.vec_add(16, tmp_ub, trans_ub[m, 0], gamma_ub, repeat_alignc, 1, 1, 1) self.tik_instance.vec_mul(16, trans_ub[m, 0], tmp_ub, beta_ub, repeat_alignc, 1, 1, 1) #[16, align_c] src_list = [trans_ub[0, 0], trans_ub[1, 0], trans_ub[2, 0], trans_ub[3, 0], trans_ub[4, 0], trans_ub[5, 0], trans_ub[6, 0], trans_ub[7, 0], trans_ub[8, 0], trans_ub[9, 0], trans_ub[10, 0], trans_ub[11, 0], trans_ub[12, 0], trans_ub[13, 0], trans_ub[14, 0], trans_ub[15, 0]] #[align_c, 16] dst_list = [vconv_ub[0, 0], vconv_ub[1, 0], vconv_ub[2, 0], vconv_ub[3, 0], vconv_ub[4, 0], vconv_ub[5, 0], vconv_ub[6, 0], vconv_ub[7, 0], vconv_ub[8, 0], vconv_ub[9, 0], vconv_ub[10, 0], vconv_ub[11, 0], vconv_ub[12, 0], vconv_ub[13, 0], vconv_ub[14, 0],vconv_ub[15, 0]] # transpose 16xC -> Cx16 self.tik_instance.vec_trans_scatter(True, True, dst_list, src_list, repeat_alignc, 16, 1)
单通道HW并行
#单通道HW并行计算 def batchnorm_compute_tiling_wh_single_c(self): with self.tik_instance.if_scope(iter_ceil < 2): # Size(H*W) < 112KB且Size(H*W*2) > 112KB时,可一次搬完一个通道 with self.tik_instance.for_range(0, iter_num_double, thread_num = 2) as i: #double buffer ...... #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub[0], ping_ub[0], self.sclar_gamma, maxiternum, 8, 8) self.tik_instance.vec_muls(128, temp_ub[0], temp_ub[0], self.sclar_beta, maxiternum, 8, 8) self.tik_instance.data_move(output_gm[i*input_wh], temp_ub, 0, 1, align_wh // 16, 0, 0) with self.tik_instance.for_range(iter_num_double, self.input_c) as i: #处理尾块 ...... #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub[0], ping_ub[0], self.sclar_gamma, maxiternum, 8, 8) self.tik_instance.vec_muls(128, temp_ub[0], temp_ub[0], self.sclar_beta, maxiternum, 8, 8) self.tik_instance.data_move(output_gm[i*input_wh], temp_ub, 0, 1, align_wh // 16, 0, 0) with self.tik_instance.else_scope(): # Size(H*W) > 112KB时,一次搬不完一个通道,需要切块多次搬。同时针对是否存在尾块的情况进行分别处理 with self.tik_instance.if_scope(res_h == 0): #对H进行块划分,情况1,能够划分完毕,没有尾块 with self.tik_instance.for_range(0, self.input_c) as i: #遍历C通道 self.sclar_gamma.set_as(gamma_ub[i]) self.sclar_beta.set_as(beta_ub[i]) idx = i*input_wh iter_num_double = floor_div_mul(iter_ceil, 2) with self.tik_instance.for_range(0, iter_num_double, thread_num = 2) as j: ... #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub, ping_ub, self.sclar_gamma, repeat_mask128 // 128, 8, 8) self.tik_instance.vec_muls(128, temp_ub, temp_ub, self.sclar_beta, repeat_mask128 // 128, 8, 8) with self.tik_instance.for_range(iter_num_double, iter_ceil) as j: 处理剩余奇数块 ...... #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub, ping_ub, self.sclar_gamma, repeat_mask128 // 128, 8, 8) self.tik_instance.vec_muls(128, temp_ub, temp_ub, self.sclar_beta, repeat_mask128 // 128, 8, 8) ...... with self.tik_instance.else_scope(): #对H进行块划分,情况2,不能够划分完毕,存在尾块,需要处理 with self.tik_instance.for_range(0, self.input_c) as i: #遍历C通道 ...... with self.tik_instance.for_range(0, iter_num_double, thread_num = 2) as j:#double buffer ...... #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub, ping_ub, self.sclar_gamma, repeat_mask128 // 128, 8, 8) self.tik_instance.vec_muls(128, temp_ub, temp_ub, self.sclar_beta, repeat_mask128 // 128, 8, 8) with self.tik_instance.for_range(iter_num_double, iter_ceil - 1) as j: #奇数尾块处理 self.tik_instance.vec_adds(128, temp_ub, ping_ub, self.sclar_gamma, repeat_mask128 // 128, 8, 8) self.tik_instance.vec_muls(128, temp_ub, temp_ub, self.sclar_beta, repeat_mask128 // 128, 8, 8) #H切分剩余尾块处理 with self.tik_instance.if_scope(iter_res_align16 > 0): ... self.tik_instance.vec_adds(128, temp_ub, pong_ub, self.sclar_gamma, repeat_res_mask128 // 128, 8, 8) self.tik_instance.vec_muls(128, temp_ub, temp_ub, self.sclar_beta, repeat_res_mask128 // 128, 8, 8)
多通道HW并行
#多通道HW并行计算 def batchnorm_compute_tiling_wh_single_c(self): with self.tik_instance.if_scope(align_wh == input_wh): # Size (H*W*C) (C > 1) < 112KB,且满足32Byte对齐时,直接搬多个通道 with self.tik_instance.for_range(0, iter_num_double, thread_num = 2) as i: #thread_num=2 double buffer加速 ...... #一次搬运多个通道,逐单通道进行BN with self.tik_instance.for_range(0, single_chnum) as j: #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub[idx1], ping_ub[idx1], self.sclar_gamma, repeat_mask + 1, 8, 8) self.tik_instance.vec_muls(128, temp_ub[idx1], temp_ub[idx1], self.sclar_beta, repeat_mask + 1, 8, 8) self.tik_instance.data_move(output_gm[index], temp_ub, 0, 1, repeat_length // 16, 0, 0) with self.tik_instance.for_range(iter_num_double, iter_cnum) as i: #处理无法进行double buffer的剩余奇数尾块 ... #逐单通道进行BN with self.tik_instance.for_range(0, single_chnum) as j: #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub[idx1], ping_ub[idx1], self.sclar_gamma, repeat_mask + 1, 8, 8) self.tik_instance.vec_muls(128, temp_ub[idx1], temp_ub[idx1], self.sclar_beta, repeat_mask + 1, 8, 8) self.tik_instance.data_move(output_gm[index], temp_ub, 0, 1, repeat_length // 16, 0, 0) with self.tik_instance.if_scope(res_ch_num > 0): #处理剩余通道数据 ...... with self.tik_instance.for_range(0, res_ch_num) as j: ...... #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub[j*input_wh], ping_ub[j*input_wh], self.sclar_gamma, repeat_mask + 1, 8, 8) self.tik_instance.vec_muls(128, temp_ub[j*input_wh], temp_ub[j*input_wh], self.sclar_beta, repeat_mask + 1, 8, 8) self.tik_instance.data_move(output_gm[index2], temp_ub, 0, 1, res_repeat_length // 16, 0, 0) with self.tik_instance.else_scope(): # Size (H*W*C) (C > 1) < 112KB,但Featuremap不满足32Byte对齐时,需要对每个通道依次搬运,否则每次都要处理尾块,影响计算效率 #一次处理单个通道,采用double buffer技术,2个通道可以并行 with self.tik_instance.for_range(0, iter_num_double, thread_num = 2) as i: ...... #计算均值与方差 self.tik_instance.vec_adds(128, temp_ub, ping_ub, self.sclar_gamma, repeat_mask128 + 1, 8, 8) self.tik_instance.vec_muls(128, temp_ub, temp_ub, self.sclar_beta, repeat_mask128 + 1, 8, 8) with self.tik_instance.for_range(iter_num_double, self.input_c) as i: #处理不能double buffer的剩余通道,剩余通道为1 ...... self.tik_instance.vec_adds(128, temp_ub, ping_ub, self.sclar_gamma, repeat_mask128 + 1, 8, 8) self.tik_instance.vec_muls(128, temp_ub, temp_ub, self.sclar_beta, repeat_mask128 + 1, 8, 8) self.tik_instance.data_move(output_gm[i * input_wh], temp_ub, 0, 1, align_wh // 16, 0, 0)
算子选择器
用户通过自定义算子选择器,根据shape的不同选择对应的Tiling策略。完整代码请参考样例使用获取。
.h实现
.h文件中定义了存储Tiling参数的结构体(例如BatchNormParam)和算子选择器实现函数(例如:SelectAclopBatchNorm)。
#ifndef TVM_TOPK_FLOWTABLE_H #define TVM_TOPK_FLOWTABLE_H #include "acl/acl.h" //align with 64 structBatchNormParam { int32_t input_n; // input format = nchw int32_t input_c; int32_t input_h; int32_t input_w; int32_t in_datatype; //input data type = fp16 int32_t output_n; // output format = nchw int32_t output_c; int32_t output_h; int32_t output_w; int32_t out_datatype; //output_data_type = fp16 int32_t gamma_c; //mean tensor channel = input channel int32_t gamma_datatype; int32_t beta_c; //var tensor channel = input channel int32_t beta_datatype; int32_t param1; //tiling parameters, difference tiling mode has difference tiling parameters int32_t param2; //gived 10 parameters int32_t param3; int32_t param4; int32_t param5; int32_t param6; int32_t param7; int32_t param8; int32_t param9; int32_t param10; }; /** * select acl batchnorm * @input: * numInputs, input tensor number * inputDesc, input tensor description * numOutputs, output tensor number * outputDesc, output tensor description * opAttr, op attribute * aclopKernelDesc, acl op kernel discription * @output: * @return success: ACL_ERROR_NONE failure: ACL_ERROR_FAILURE */ extern "C" aclError SelectAclopBatchNorm(int numInputs, const aclTensorDesc *const inputDesc[], int numOutputs, const aclTensorDesc *const outputDesc[], const aclopAttr *opAttr, aclopKernelDesc *aclopKernelDesc); #endif
.cpp实现
算子选择器关键实现逻辑如下:
- 根据输入的shape信息,判断使用哪种Tiling策略。示例代码:
/** * batchnorm tiling to * @limit: 1. n,c,h,w is the format of NCHW * 2. inDtype, outDtype is the multiple of 16, and the dtype is float16 * 3. mini, core num is 2 * 4. only support to select one batchnorm schedule * 5. not set kernel workspace */ void BatchNormTiling(uint64_t n, uint64_t c, uint64_t h, uint64_t w, aclDataType inDtype, TilingMode &mode) { uint64_t lenwh = w*h; uint64_t bytesize = 0; if(inDtype == ACL_FLOAT16){ bytesize = 2; } else{ cout << "[ERROR] not fp16 datatype not support yet." << endl; return; } if (c > lenwh && lenwh == CeilDivMul(lenwh, 16) && (CeilDivMul(c, 16) * lenwh * bytesize) <= TILING_0_UB_SIZE) { mode = TILING_MODE_1; } else{ if(lenwh*bytesize > TILING_2_UB_SIZE){ mode = TILING_MODE_2; } else if((lenwh*bytesize > TILING_2_UB_SIZE/2) && (lenwh*bytesize < TILING_2_UB_SIZE)){ mode = TILING_MODE_2; } else{ mode = TILING_MODE_3; } } //cout << "[INFO] select tiling mode is:" << mode << endl; return; }
- 针对不同的Tiling策略,计算具体Tiling参数。
对比固定shape场景下Tiling参数的计算,动态shape场景下Tiling参数不再在TIK算子实现函数中计算,将重复的Scalar运算全部转移到Host CPU上,减少AI Core上Scalar运算量,有利于流水线并行。
if(tilingmode == TILING_MODE_1){ scheduleFlag = "tiling_mode_1__kernel0"; //tiling mode 1 生成的二进制名字 int32_t chn_num = MAX_C_SIZE; int32_t align_16 = CeilDivMul(in_w*in_h, 16); int32_t total_use_ub = chn_num*align_16*2*2 + chn_num*2; if(total_use_ub <= UB_SIZE){ //数据可一次搬入Unified Buffer空间 bnparam.param1 = in_h*in_w; //input_wh bnparam.param2 = CeilDivMul(bnparam.param1, 16); //align_wh bnparam.param3 = CeilDivMul(in_c, 16); //align_c } else{//数据不可一次搬入Unified Buffer空间 bnparam.param1 = in_h*in_w; //input_wh bnparam.param2 = CeilDiv(bnparam.param1, 16); //iterwh_align16 bnparam.param3 = CeilDiv(in_c, 16); //repeat_alignc bnparam.param4 = CeilDivMul(bnparam.param1, 16); //align_wh bnparam.param5 = CeilDivMul(in_c, 16); //align_c bnparam.param6 = CeilDiv(bnparam.param4, 16) - 1; } } else if(tilingmode == TILING_MODE_2){ scheduleFlag = "tiling_mode_2__kernel0"; //tiling mode 2 生成的二进制名字 bnparam.param1 = in_h*in_w; //input_wh bnparam.param2 = CeilDivMul(bnparam.param1, 16); //align_wh int32_t tiling_num = (TILING_1_UB_SIZE / 4); bnparam.param3 = CeilDiv(bnparam.param1, tiling_num); //iter_ceil if(bnparam.param3 < 2){ //W*H可一次搬入给定的Unified Buffer空间 bnparam.param4 = tiling_num / 128;//iter_mask128 bnparam.param5 = bnparam.param4/255; //repeat_mask128 bnparam.param6 = bnparam.param4 - bnparam.param5*255; //repeat_res_mask128 bnparam.param7 = tiling_num - bnparam.param4*128; //res_mask128 } else{//W*H需分多次搬入给定的Unified Buffer空间 bnparam.param4 = CeilDiv(in_h, bnparam.param3);//iter_h bnparam.param5 = CeilDivMul(bnparam.param4*in_w, 16);//iter_align16 bnparam.param6 = CeilDivMul(((in_h - bnparam.param4*(bnparam.param3 - 1))*in_w) , 16); //iter_res_align16 bnparam.param7 = CeilDivMul(bnparam.param4*in_w, 128);//repeat_mask128 bnparam.param8 = CeilDivMul(((in_h - bnparam.param4*(bnparam.param3 - 1))*in_w), 128);//repeat_res_mask128 bnparam.param9 = in_h - bnparam.param4*bnparam.param3; } } else if(tilingmode == TILING_MODE_3){//tiling mode 3 生成的二进制名字 bnparam.param1 = in_h*in_w; //input_wh bnparam.param2 = CeilDivMul(bnparam.param1, 16); //align_wh int32_t tiling_num = (TILING_2_UB_SIZE / 4); bnparam.param3 = tiling_num / bnparam.param2; //single_chnum if(bnparam.param3 > in_c){ bnparam.param3 = in_c; } bnparam.param4 = (int32_t)(in_c / bnparam.param3);//iter_cnum if(bnparam.param4 <= 0){ bnparam.param4 = 1; } if(bnparam.param1 == bnparam.param2){//单次搬入多个Featuremap, W*H 32Byte对齐 bnparam.param5 = bnparam.param1*bnparam.param3; //repeat_length bnparam.param6 = bnparam.param1 / 128; //repeat_mask bnparam.param7 = bnparam.param1 - bnparam.param6*128; //repeat_res_mask bnparam.param8 = 128*bnparam.param6; //res_mask bnparam.param9 = in_c - bnparam.param3*(bnparam.param4); //res_ch_num bnparam.param10 = bnparam.param9*bnparam.param1; //res_repeat_length } else{//W*H 非32对齐,单次仅搬入1个通道数据 bnparam.param5 = bnparam.param1 / 128; //repeat_mask128 bnparam.param6 = bnparam.param1 - bnparam.param5*128; //repeat_res_mask } scheduleFlag = "tiling_mode_3__kernel0"; }
- 通过如下两个接口设置aclopKernelDesc参数,用于算子执行。
- aclopSetKernelArgs:供算子选择器选择到合适Kernel后,进行参数设置。将计算得到的参数,即kernel id、block dim, tiling参数,设置到aclopKernelDesc中。
aclopSetKernelArgs(aclopKernelDesc, scheduleFlag.c_str(), 1, &bnparam, sizeof(BatchNormParam));
建议此处设置的block dim(AI Core核数)和TIK算子实现时的使用的AI Core核数保持一致。
- aclopSetKernelWorkspaceSizes:供算子选择器选择到合适kernel后,进行workspace设置,非必须,根据算子情况可选。
- aclopSetKernelArgs:供算子选择器选择到合适Kernel后,进行参数设置。将计算得到的参数,即kernel id、block dim, tiling参数,设置到aclopKernelDesc中。