5.Swish 激活函数算子

Swish 激活函数算子

大致代码参考官方教程即可快速入门-自定义算子开发-AscendC算子开发-CANN - 华为HarmonyOS开发者

一、创建工程

在~/mywork目录创建json文件:swish_custom.json

[ 
    { 
        "op": "SwishCustom", 
        "input_desc": [ 
            { 
                "name": "x", 
                "param_type": "required", 
                "format": [ 
                    "ND", 
                    "ND", 
                    "ND" 
                ], 
                "type": [ 
                    "fp16", 
                    "float", 
                    "int32" 
                ] 
            }
        ], 
        "output_desc": [ 
            { 
                "name": "z", 
                "param_type": "required", 
                "format": [ 
                    "ND", 
                    "ND", 
                    "ND" 
                ], 
                "type": [ 
                    "fp16", 
                    "float", 
                    "int32" 
                ] 
            } 
        ] 
    } 
]

使用msopgen命令创建工程:

msopgen gen -i ~/mywork/swish_custom.json -c ai_core-kirin9020 -out ~/mywork/SwishCustom

二、算子实现

代码同官方教程中的AddCustom算子基本一样,除了名字之外,只需要修改kernel侧代码(在官方教程的基础上修改变量、compute函数):

#include "kernel_operator.h"
constexpr int32_t BUFFER_NUM = 2; 
class KernelSwish { 
public: 
    __aicore__ inline KernelSwish() {} 
    // 初始化函数,完成内存初始化相关操作 
    __aicore__ inline void Init(GM_ADDR x, GM_ADDR z, uint32_t totalLength, uint32_t tileNum) 
    { 
        // 使用获取到的TilingData计算得到singleCoreSize(每个核上总计算数据大小)、tileNum(每个核上分块个数)、singleTileLength(每个分块大小)等变量 
        this->blockLength = totalLength / AscendC::GetBlockNum(); 
        this->tileNum = tileNum; 
        this->tileLength = this->blockLength / tileNum / BUFFER_NUM; 
         
        // 获取当前核的起始索引 
        xGm.SetGlobalBuffer((__gm__ DTYPE_X*)x + this->blockLength * AscendC::GetBlockIdx(), this->blockLength); 
        zGm.SetGlobalBuffer((__gm__ DTYPE_Z*)z + this->blockLength * AscendC::GetBlockIdx(), this->blockLength); 
        // 通过Pipe内存管理对象为输入输出Queue分配内存 
        pipe.InitBuffer(inQueueX, BUFFER_NUM, this->tileLength * sizeof(DTYPE_X)); 
        pipe.InitBuffer(outQueueZ, BUFFER_NUM, this->tileLength * sizeof(DTYPE_Z)); 
    } 
    // 核心处理函数,实现算子逻辑,调用私有成员函数CopyIn、Compute、CopyOut完成矢量算子的三级流水操作 
    __aicore__ inline void Process() 
    { 
        int32_t loopCount = this->tileNum * BUFFER_NUM; 
        for (int32_t i = 0; i < loopCount; i++) { 
            CopyIn(i); 
            Compute(i); 
            CopyOut(i); 
        } 
    } 
 
 
private: 
    // 搬入函数,完成CopyIn阶段的处理,被核心Process函数调用 
    __aicore__ inline void CopyIn(int32_t progress) 
    { 
        // 从Queue中分配输入Tensor 
        AscendC::LocalTensor<DTYPE_X> xLocal = inQueueX.AllocTensor<DTYPE_X>(); 
         // 将GlobalTensor数据拷贝到LocalTensor 
        AscendC::DataCopy(xLocal, xGm[progress * this->tileLength], this->tileLength); 
        // 将LocalTensor放入VECIN(代表矢量编程中搬入数据的逻辑存放位置)的Queue中 
        inQueueX.EnQue(xLocal); 
    } 
    // 计算函数,完成Compute阶段的处理,被核心Process函数调用 
    __aicore__ inline void Compute(int32_t progress) 
    { 
        // 将Tensor从队列中取出,用于后续计算 
        AscendC::LocalTensor<DTYPE_X> xLocal = inQueueX.DeQue<DTYPE_X>(); 
        // 从输出队列分配临时 Tensor
        AscendC::LocalTensor<DTYPE_Z> expNegX = outQueueZ.AllocTensor<DTYPE_Z>();

         // 1. -x
        AscendC::Muls(expNegX, xLocal, (DTYPE_X)-1.0, this->tileLength);
        // 2. exp(-x)
        AscendC::Exp(expNegX, expNegX, this->tileLength);
        // 3. 1 + exp(-x)  使用 Adds 直接加标量 1
        AscendC::Adds(expNegX, expNegX, (DTYPE_Z)1.0, this->tileLength);
        // 4. 倒数得到 sigmoid(x)
        AscendC::Reciprocal(expNegX, expNegX, this->tileLength);
        // 5. z = x * sigmoid(x)
        AscendC::LocalTensor<DTYPE_Z> zLocal = outQueueZ.AllocTensor<DTYPE_Z>();
        AscendC::Mul(zLocal, xLocal, expNegX, this->tileLength);

        outQueueZ.EnQue(zLocal);
        outQueueZ.FreeTensor(expNegX);
        inQueueX.FreeTensor(xLocal);
    } 
    // 搬出函数,完成CopyOut阶段的处理,被核心Process函数调用 
    __aicore__ inline void CopyOut(int32_t progress) 
    { 
        // 从VecOut的Queue中取出输出Tensor 
        AscendC::LocalTensor<DTYPE_Z> zLocal = outQueueZ.DeQue<DTYPE_Z>(); 
        // 将输出Tensor拷贝到GlobalTensor中 
        AscendC::DataCopy(zGm[progress * this->tileLength], zLocal, this->tileLength); 
        // 将不再使用的LocalTensor释放 
        outQueueZ.FreeTensor(zLocal); 
    } 
 
 
private: 
    // Pipe内存管理对象 
    AscendC::TPipe pipe; 
    // 输入数据Queue队列管理对象,QuePosition为VECIN 
    AscendC::TQue<AscendC::QuePosition::VECIN, BUFFER_NUM> inQueueX;  
    // 输出数据Queue队列管理对象,QuePosition为VECOUT 
    AscendC::TQue<AscendC::QuePosition::VECOUT, BUFFER_NUM> outQueueZ; 
    // 管理输入输出Global Memory内存地址的对象,其中xGm, 为输入,zGm为输出 
    AscendC::GlobalTensor<DTYPE_X> xGm; 
    AscendC::GlobalTensor<DTYPE_Z> zGm; 
    // 每个核上总计算数据大小 
    uint32_t blockLength; 
    // 每个核上总计算数据分块个数 
    uint32_t tileNum; 
    // 每个分块大小 
    uint32_t tileLength; 
};

extern "C" __global__ __aicore__ void swish_custom(GM_ADDR x, GM_ADDR z, GM_ADDR workspace, GM_ADDR tiling) {  
    GET_TILING_DATA(tiling_data, tiling);
    KernelSwish op;
    op.Init(x, z, tiling_data.totalLength, tiling_data.tileNum);
    op.Process();
}

然后编译

./build.sh

三、运行测试

生成测试数据:

import numpy as np

# 生成随机输入,例如形状 (256,),数据类型 float16
x = np.random.randn(256).astype(np.float16)
x.tofile('swish_x.bin')

# 计算 golden
def swish(x):
    return x / (1 + np.exp(-x))
golden = swish(x).astype(np.float16)
golden.tofile('swish_golden.bin')

print("数据已生成并保存到 swish_x.bin 和 swish_golden.bin")

创建json文件:swish_config.json

{
    "op_type": "SwishCustom",
    "gen_data": false,
    "inputs": [
        {
            "name": "x",
            "dtype": "float16",
            "format": "ND",
            "shape": [256],
            "param_type": "required",
            "data_file": "/home/dj/mywork/SwishCustom/swish_x.bin"
        }
    ],
    "outputs": [
        {
            "name": "z",
            "dtype": "float16",
            "format": "ND",
            "shape": [256],
            "param_type": "required",
            "data_file": "/home/dj/mywork/SwishCustom/swish_golden.bin"
        }
    ]
}

CPU测试命令:

ascendebug kernel \
    --backend cpu \
    --chip-version kirin9020 \
    --repo-type customize \
    --json-file ./swish_config.json \
    --core-type AiCore \
    --work-dir ./debug_workspace

仿真测试命令:

ascendebug kernel --backend simulator --repo-type customize --json-file ./swish_config.json --core-type AiCore --chip-version kirin9020 --work-dir ./debug_workspace --block-num 1 --timeout 1200 


posted @ 2026-09-13 22:47  Tikas  阅读(3)  评论(0)    收藏  举报