TIK C++算子开发入门-笔记总结
TIK2简介
TIK2是一种使用C/C++作为前端语言的编程框架,开发者可以使用TIK2提供的API编写自定义算子,并通过CCEC编译器将自定义算子编译成为可运行在昇腾AI处理器上的应用程序。
TIK2继承了TIK数据操作灵活的优点,除此之外,更多优势如下:
C/C++原语编程
编程模型屏蔽硬件差异,编程范式提高开发效率
多层级API封装,从简单到灵活,兼顾易用与高效
调试方法简单
核函数(Kernel Function)介绍
核函数(Kernel Function)是TIK2算子的入口。TIK2允许用户使用核函数这种C/C++函数的语法扩展来管理设备端的运行代码,用户在核函数中进行算子类的声明和其成员函数的调用,由此实现该算子的所有功能。
核函数是直接在设备端执行的代码。在核函数中,需要为在一个核上执行的代码规定要进行的数据访问和计算操作,当核函数被调用时,多个核将并行执行同一个计算任务。核函数需要按照如下规则进行编写。
核函数调用
内核调用符仅可在NPU侧编译时调用,CPU侧编译无法识别该符号。
执行配置由3个参数决定:
blockDim,规定了核函数将会在几个核上执行,blockDim的大小不能超过当前设备上核的配置个数。每个执行该核函数的核会被分配一个逻辑ID,表现为内置变量block_idx,可以在核函数的实现中直接使用;
l2ctrl,保留参数,暂时设置为固定值nullptr,开发者无需关注;
stream,类型为aclrtStream,stream是一个任务队列,应用程序通过stream来管理任务的并行。
编程范式
编程范式描述了算子实现的固定流程,基于编程范式进行编程,可以快速搭建算子实现的代码框架。
TIK2编程范式把算子核内的处理程序,分成多个流水任务,任务之间通过队列(Queue)进行通信和同步,并通过统一的内存管理模块(Pipe)管理任务间通信内存。
流水任务
TIK2分别针对Vector、Cube编程设计了不同的流水任务。开发者只需要完成基本任务的代码实现即可,底层的指令同步和并行调度由TIK2框架实现,开发者无需关注。
Vector编程范式把算子的实现流程分为3个基本任务:CopyIn,Compute,CopyOut。CopyIn负责搬入操作,Compute负责矢量指令计算操作,CopyOut负责搬出操作。
矢量编程
矢量编程主要分为CopyIn、Compute、CopyOut三个任务。CopyIn任务中将输入数据从Global内存搬运至Local内存后,需要使用EnQue将LocalTensor放入VECIN的Queue中;Compute任务等待VECIN的Queue中LocalTensor出队之后才可以完成矢量计算,计算完成后使用EnQue将计算结果LocalTensor放入到VECOUT的Queue中;CopyOut任务等待VECOUT的Queue中LocalTensor出队,再将其拷贝到Global内存。这样 ,Queue队列就完成了三个任务间的数据通信和同步。
内存管理
任务间数据传递使用到的内存统一由内存管理模块Pipe进行管理。Pipe作为片上内存管理者,通过InitBuffer接口对外提供Queue内存初始化功能,开发者可以通过该接口为指定的Queue分配内存。
Queue队列内存初始化完成后,需要使用内存时,通过调用AllocTensor来为LocalTensor分配内存,当创建的LocalTensor完成相关计算无需再使用时,再调用FreeTensor来回收LocalTensor的内存。
TIK2方式实现矩阵算子
算子分析:分析算子的数学表达式、输入、输出以及计算逻辑的实现,明确需要调用的TIK2接口。
核函数定义:定义TIK2算子入口函数。
根据矩阵编程范式实现算子类:完成核函数的内部实现。
算子调试
TIK2提供孪生调试方法,即在cpu侧创建一个npu的模型并模拟它的计算行为,用来进行业务功能调试。相同的算子代码可以在cpu域调试精度,npu域调试性能。
cpu域
运行使能ASSERT,初步拦截算子指令或框架使用错误,如参数超限,指令数据地址重叠,该芯片不支持的指令等;
可使用gdb单步调试算子计算精度,也可以在代码中直接编写printf(...)来观察数值的输出;
可使用model仿真粗调算子性能,获取性能仿真数据;
通过指令录制回放,可获得算子运行调用指令序列,分析指令执行顺序以及同步死锁分析。
npu域
可在真实芯片上获取profiling数据,进行性能精细调优;
npu域调试手段较少,可在cpu域精度调试通过,且性能粗调通过后,在芯片上验收测试。