news 2026/5/9 16:38:31

CANN/ops-rand AI Core算子开发指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN/ops-rand AI Core算子开发指南

AI Core算子开发指南

【免费下载链接】ops-randops-rand是CANN (Compute Architecture for Neural Networks)算子库中提供的随机数生成库。项目地址: https://gitcode.com/cann/ops-rand

说明:

  1. 算子开发过程中涉及的基本概念如Tiling、Kernel、Ascend C接口等,详细介绍请参考《Ascend C算子开发》。
  2. AI Core算子是使用Ascend C语言开发,运行在AI Core硬件单元的算子。

本指南以ops-rand项目中的随机数算子为例,介绍新算子开发流程以及涉及的交付件。

  1. 工程创建:开发算子前,需完成环境部署并创建算子目录。
  2. 算子定义:算子功能说明与原型定义。
  3. Tiling实现:实现Host侧Tiling策略。
  4. Kernel实现:实现Device侧算子核函数。
  5. 编译部署:通过工程编译脚本完成自定义算子的编译和安装。
  6. 算子验证:通过常见算子调用方式,验证自定义算子功能。

工程创建

1. 环境部署

开发算子前,请先参考环境部署完成基础环境搭建。

2. 目录创建

src目录下创建新的算子目录,目录名为算子名的小写下划线形式。

# 创建算子目录 mkdir -p src/${op_name} mkdir -p src/${op_name}/arch35 mkdir -p src/${op_name}/tests

创建完成后,目录结构如下所示:

${op_name}/ # 算子名的小写下划线形式 ├── CMakeLists.txt # 算子编译配置文件 ├── ${op_name}.cpp # Kernel入口文件 ├── arch35/ # Ascend950特有实现 │ └── ${op_name}.h # Kernel实现头文件 └── tests/ # 测试用例目录 ├── CMakeLists.txt └── test_${op_name}_example.cpp # 算子测试用例

算子定义

开发算子前需要先确定目标算子的功能和计算逻辑。

stateless_random_uniform_v2算子为例:

  • 功能:生成无状态的均匀分布随机数
  • 输入:seed(随机种子)、offset(偏移量)、shape(输出形状)
  • 输出:填充了随机数的张量
  • 支持的数据类型:FP32、FP16、BF16

API接口定义

include/cann_ops_rand.h中定义算子的对外API接口:

#ifdef __cplusplus extern "C" { #endif /* 无状态均匀分布随机数生成 (内部API) */ aclError _aclrandStatelessRandomUniformV2( uint64_t seed, uint64_t offset, int32_t alg, void *output, uint64_t n, int32_t dtype ); #ifdef __cplusplus } #endif

说明

  • output参数类型为void*,实际数据类型由dtype参数决定
  • dtype参数:0=FP32, 1=FP16, 2=BF16

Tiling实现

Tiling简介

因NPU中AI Core内部存储空间有限,无法一次性将整个张量数据加载到计算单元中处理,因此需要将输入张量切分为多个小块(Tile),逐块进行计算,这一过程称为Tiling。

TilingData结构体定义

在Kernel实现头文件中定义TilingData结构体:

// stateless_random_uniform_v2.h struct StatelessRandomUniformV2TilingData { uint32_t tilingKey; // Tiling标识,用于区分不同场景 uint32_t blockNum; // 核数 uint32_t blockTilingSize; // 每个核处理的数据量 uint32_t tailBlockTilingSize; // 尾核处理的数据量 uint32_t ubTilingSize; // UB切分大小 uint32_t alg; // 算法类型 uint32_t key[2]; // Philox算法密钥(来自seed) uint32_t counter[4]; // Philox算法计数器(来自offset) };

说明tilingKey字段用于标识不同的 Tiling 场景,便于在 Kernel 中根据场景选择不同的处理逻辑。

Tiling策略计算

在Host侧(Kernel入口函数或API调用前)计算Tiling参数:

// 获取平台信息 auto ascendcPlatform = platform_ascendc::PlatformAscendCManager::GetInstance(); auto coreNum = ascendcPlatform->GetCoreNumAiv(); // 获取可用核数 // 计算Block切分参数 constexpr uint32_t CORE_ALIGN_SIZE = 512; // 核对齐大小 constexpr uint32_t MIN_TILING_SIZE = 256; // 最小切分大小 constexpr uint32_t BLOCK_SIZE_BYTES = 32; // 块大小(字节) auto outputDtypeSize = sizeof(float); auto outputSize = n * outputDtypeSize; auto coreAlignFactor = CORE_ALIGN_SIZE / outputDtypeSize; auto blockFactor = CeilDiv(outputSize, coreNum); auto blockAlignFactor = CeilDiv(blockFactor, coreAlignFactor) * coreAlignFactor; tilingData->blockTilingSize = std::max(blockAlignFactor, MIN_TILING_SIZE); tilingData->blockNum = CeilDiv(outputSize, tilingData->blockTilingSize); tilingData->tailBlockTilingSize = outputSize - tilingData->blockTilingSize * (tilingData->blockNum - 1); // 计算UB切分参数 uint64_t ubSize = 0; ascendcPlatform->GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize); auto quarterUbSize = ubSize / 4; // 使用1/4的UB空间 auto alignFactor = BLOCK_SIZE_BYTES / outputDtypeSize; // 块对齐因子 tilingData->ubTilingSize = CeilDiv(quarterUbSize / outputDtypeSize, alignFactor) * alignFactor; // 设置随机数种子参数 tilingData->key[0] = static_cast<uint32_t>(seed); tilingData->key[1] = static_cast<uint32_t>(seed >> 32); tilingData->counter[0] = static_cast<uint32_t>(offset); tilingData->counter[1] = static_cast<uint32_t>(offset >> 32);

Kernel实现

Kernel代码结构

ops-rand项目的Kernel实现主要包含以下文件:

src/${op_name}/ ├── ${op_name}.cpp # Kernel入口文件 + API实现 └── arch35/ └── ${op_name}.h # Kernel类实现

Kernel类定义

// stateless_random_uniform_v2.h template <typename T> class StatelessRandomUniformV2 { public: __aicore__ inline StatelessRandomUniformV2() {} __aicore__ inline void Init( GM_ADDR y, const StatelessRandomUniformV2TilingData* __restrict tilingData, TPipe* pipeIn ); __aicore__ inline void Process(); private: __aicore__ inline void ParseTilingData(const StatelessRandomUniformV2TilingData* tilingData); __aicore__ inline void Skip(const uint64_t count); // 跳过计数器 __aicore__ inline void DataTypeHandle(LocalTensor<T>& yOutput, const uint32_t calCount); __aicore__ inline void CopyOut(); private: TPipe* pipe; // 队列和缓冲区 TQue<QuePosition::VECOUT, BUFFER_NUM> outQueY_; // 输出队列 TBuf<QuePosition::VECCALC> philoxQueBuf_; // Philox结果缓冲区 GlobalTensor<T> outputGm_; // 全局输出张量 // Tiling参数 uint32_t blockNum_ = 0; uint32_t blockTilingSize_ = 0; uint32_t ubTilingSize_ = 0; // 随机数生成状态 uint32_t key_[2] = {0}; uint32_t counter_[4] = {0}; };

Init函数实现

template <typename T> __aicore__ inline void StatelessRandomUniformV2<T>::Init( GM_ADDR y, const StatelessRandomUniformV2TilingData* tilingData, TPipe* pipeIn ) { // 1. 解析Tiling数据 ParseTilingData(tilingData); // 2. 计算当前核的偏移量 auto blockIdx = GetBlockIdx(); blockOffset_ = blockTilingSize_ * blockIdx; // 3. 处理尾核情况 if (blockIdx == blockNum_ - 1) { currBlockTilingSize_ = tailBlockTilingSize_; } else { currBlockTilingSize_ = blockTilingSize_; } // 4. 初始化全局张量和队列 outputGm_.SetGlobalBuffer((__gm__ T*)y); pipe = pipeIn; pipe->InitBuffer(outQueY_, BUFFER_NUM, ubTilingSize_ * sizeof(T)); pipe->InitBuffer(philoxQueBuf_, ubTilingSize_ * sizeof(uint32_t)); }

Process函数实现

template <typename T> __aicore__ inline void StatelessRandomUniformV2<T>::Process() { // 1. 计算并跳过偏移量 auto groupCnt = (blockOffset_ + RESULT_ELEMENT_CNT - 1) / RESULT_ELEMENT_CNT; Skip(groupCnt); // 2. 分UB循环处理 ubLoopCnt_ = (currBlockTilingSize_ + ubTilingSize_ - 1) / ubTilingSize_; for (auto idx = 0; idx < ubLoopCnt_; idx++) { // 处理尾块 currUbTilingSize_ = ubTilingSize_; if ((idx == ubLoopCnt_ - 1) && (currBlockTilingSize_ % ubTilingSize_ != 0)) { currUbTilingSize_ = currBlockTilingSize_ % ubTilingSize_; } currOffset_ = blockOffset_ + idx * ubTilingSize_; // 3. 生成随机数 LocalTensor<uint32_t> philoxRes = philoxQueBuf_.Get<uint32_t>(); LocalTensor<T> yOutput = outQueY_.AllocTensor<T>(); // 使用Philox算法生成随机数 PhiloxRandom<10>( philoxRes, {key_[0], key_[1]}, {counter_[0], counter_[1], counter_[2], counter_[3]}, currUbTilingSize_ ); // 4. 数据类型转换 DataTypeHandle(yOutput, currUbTilingSize_); outQueY_.EnQue(yOutput); // 5. 拷贝输出 CopyOut(); // 6. 更新计数器 groupCnt = (currUbTilingSize_ + RESULT_ELEMENT_CNT - 1) / RESULT_ELEMENT_CNT; Skip(groupCnt); } }

数据类型转换

template <typename T> __aicore__ inline void StatelessRandomUniformV2<T>::DataTypeHandle( LocalTensor<T>& yOutput, const uint32_t calCount ) { if constexpr (AscendC::IsSameType<T, half>::value) { // FP16: 取10位尾数,指数设为15 Uint16ToHalf(yOutput, calCount); } else if constexpr (AscendC::IsSameType<T, float>::value) { // FP32: 取23位尾数,指数设为127 Uint32ToFloat(yOutput, calCount); } else if constexpr (AscendC::IsSameType<T, bfloat16_t>::value) { // BF16: 取7位尾数,指数设为127 Uint16ToBfloat16(yOutput, calCount); } }

Kernel入口函数

// stateless_random_uniform_v2.cpp extern "C" __global__ __aicore__ void stateless_random_uniform_v2( GM_ADDR y, StatelessRandomUniformV2TilingData tilingData ) { TPipe pipe; StatelessRandomUniformV2<float> op; op.Init(y, &tilingData, &pipe); op.Process(); }

Host侧API实现

aclError _aclrandStatelessRandomUniformV2( uint64_t seed, uint64_t offset, int32_t alg, float *output, uint64_t n ) { // 1. 参数校验 if (output == nullptr || n == 0) { return ACL_ERROR; } // 2. 分配设备内存 void *devOut = nullptr; aclrtMalloc(&devOut, n * sizeof(float), ACL_MEM_MALLOC_HUGE_FIRST); // 3. 计算Tiling参数 auto ascendcPlatform = platform_ascendc::PlatformAscendCManager::GetInstance(); StatelessRandomUniformV2TilingData *tilingData = new StatelessRandomUniformV2TilingData(); // ... Tiling参数计算 ... // 4. 启动Kernel stateless_random_uniform_v2<<<tilingData->blockNum, nullptr, 0>>>( (__gm__ uint8_t *)devOut, *tilingData ); // 5. 拷贝结果到Host aclrtMemcpy(output, n * sizeof(float), devOut, n * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); // 6. 释放资源 aclrtFree(devOut); return ACL_SUCCESS; }

编译部署

算子开发完成后,需对算子工程进行编译,生成ops-rand安装包。

1. 准备工作

参考工程创建完成基础环境搭建,同时检查算子开发交付件是否完备。

2. 配置环境变量

# 默认路径安装 source /usr/local/Ascend/cann/set_env.sh # 指定路径安装 # source ${install_path}/cann/set_env.sh

3. 编译ops-rand包

进入项目根目录,执行编译命令:

# 编译所有算子 bash build.sh --pkg --soc=ascend950 # 编译指定算子 bash build.sh --pkg --soc=ascend950 --ops=${op_name}

若提示如下信息,说明编译成功:

Self-extractable archive "cann-950-ops-rand_9.0.0_linux-x86_64.run" successfully created.

4. 安装ops-rand包

./build_out/cann-${soc_name}-ops-rand_${cann_version}_linux-${arch}.run --full --install-path=${install_path}
  • ${soc_name}:表示NPU型号名称,如950
  • ${cann_version}:表示CANN版本号,如9.0.0
  • ${arch}:表示CPU架构,如aarch64x86_64
  • ${install_path}:表示指定安装路径,需要与toolkit包安装在相同路径,默认安装在/usr/local/Ascend目录。

ops-rand安装在${install_path}/cann路径中。

算子验证

# 编译并运行测试 bash build.sh --ops=${op_name} --run

随机数生成注意事项

Philox算法

ops-rand使用Philox算法生成随机数,该算法具有以下特点:

  1. 确定性:相同的seed和offset产生相同的随机序列
  2. 并行友好:支持多核并行生成,通过counter偏移实现
  3. 高质量:通过多轮加密混淆保证随机性

数据类型转换公式

数据类型尾数位数指数值转换公式
FP3223127(exp << 23) | mantissa- 1.0f
FP161015(exp << 10) | mantissa- 1.0h
BF167127(exp << 7) | mantissa- 1.0bf

【免费下载链接】ops-randops-rand是CANN (Compute Architecture for Neural Networks)算子库中提供的随机数生成库。项目地址: https://gitcode.com/cann/ops-rand

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 16:33:54

CANN驱动AI Core查询API

dcmi_get_device_aicore_info 【免费下载链接】driver 本项目是CANN提供的驱动模块&#xff0c;实现基础驱动和资源管理及调度等功能&#xff0c;使能昇腾芯片。 项目地址: https://gitcode.com/cann/driver 函数原型 int dcmi_get_device_aicore_info(int card_id, in…

作者头像 李华
网站建设 2026/5/9 16:31:53

CHB-MIT数据集在Kaggle竞赛与学术研究中的价值:给AI+医疗新手的入门指南

CHB-MIT数据集在Kaggle竞赛与学术研究中的价值&#xff1a;给AI医疗新手的入门指南 在医疗AI领域&#xff0c;数据是推动研究进展的核心燃料。对于刚踏入这个交叉领域的研究者而言&#xff0c;如何选择一个既具备学术价值又适合技术实践的公开数据集&#xff0c;往往成为项目启…

作者头像 李华
网站建设 2026/5/9 16:31:03

CANN/hccl HCCL集合通信算法简介

算法简介 【免费下载链接】hccl 集合通信库&#xff08;Huawei Collective Communication Library&#xff0c;简称HCCL&#xff09;是基于昇腾AI处理器的高性能集合通信库&#xff0c;为计算集群提供高性能、高可靠的通信方案 项目地址: https://gitcode.com/cann/hccl …

作者头像 李华
网站建设 2026/5/9 16:30:48

五分钟接入OpenAI兼容API为网站添加智能对话

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 五分钟接入OpenAI兼容API为网站添加智能对话 基础教程类&#xff0c;面向需要为网站集成AI功能的开发者&#xff0c;介绍如何通过c…

作者头像 李华
网站建设 2026/5/9 16:25:45

网路原理(各层协议)

一. 应用层具体如何自定义协议自定义协议分成两个阶段1.根据需求明确传输哪些信息2.约定好信息组织的格式约定信息的组织格式有很多种方法1.行文本的方式一个响应有多行构成2.通过xml格式来约束请求和响应数据xml用来网络传输&#xff0c;和浏览器怎么显示无关&#xff0c;html…

作者头像 李华