写给前端的 CANN-atvcoss:昇腾Vector算子子程序模板库到底是啥?
写给前端的 CANN-atvcoss:昇腾Vector算子子程序模板库到底是啥?
·
写给前端的 CANN-atvcoss:昇腾Vector算子子程序模板库到底是啥?
之前有兄弟问我:“哥,atvc 和 atvcoss 有什么区别?都是 Vector 算子库?”
好问题。今天一次说清楚。
atvcoss 是啥?
atvcoss = Ascend Template Library for Vector Operator Subroutine,昇腾 Vector 算子子程序模板库。
一句话说清楚:atvcoss 是昇腾的 Vector 算子子程序模板库,提供更底层的子程序接口,用于构建复杂算子。
你说气人不气人,用 atvcoss 能自己组合子程序,构建更灵活的算子。
atvcoss vs atvc
| 特性 | atvc | atvcoss |
|---|---|---|
| 抽象层级 | 高层 API | 底层子程序 |
| 灵活性 | 中等 | 高 |
| 易用性 | 简单 | 复杂 |
| 性能 | 高 | 更高 |
| 适用场景 | 标准算子 | 复杂算子 |
简单说:
- atvc:高层 API,一行代码完成操作
- atvcoss:底层子程序,自己组合构建算子
atvcoss 核心能力
1. 向量加载/存储
底层内存访问。
#include "atvcoss/atvcoss.h"
using namespace atvcoss;
// 加载向量
void LoadVectorKernel(half* gm_addr, half* local_addr, uint32_t length) {
// 从 Global Memory 加载到 Local Memory
Load<half>(local_addr, gm_addr, length);
}
// 存储向量
void StoreVectorKernel(half* local_addr, half* gm_addr, uint32_t length) {
// 从 Local Memory 存储到 Global Memory
Store<half>(gm_addr, local_addr, length);
}
// 带 stride 加载
void LoadWithStrideKernel(half* gm_addr, half* local_addr,
uint32_t length, uint32_t stride) {
Load<half>(local_addr, gm_addr, length, stride);
}
2. 向量计算
底层计算子程序。
#include "atvcoss/atvcoss.h"
using namespace atvcoss;
// 向量加法(底层)
void AddSubroutineKernel(half* x, half* y, half* z, uint32_t length) {
// 不像 atvc 的 z = x + y
// atvcoss 需要显式指定每个步骤
// 1. 加载到 Local Memory
LocalTensor<half> lx, ly, lz;
Load(lx, x, length);
Load(ly, y, length);
// 2. 计算
Add(lz, lx, ly, length);
// 3. 存储
Store(z, lz, length);
}
// 向量乘法
void MulSubroutineKernel(half* x, half* y, half* z, uint32_t length) {
LocalTensor<half> lx, ly, lz;
Load(lx, x, length);
Load(ly, y, length);
Mul(lz, lx, ly, length);
Store(z, lz, length);
}
3. 复合子程序
组合多个操作。
#include "atvcoss/atvcoss.h"
using namespace atvcoss;
// Fused Add-Mul: z = (x + y) * scale
void FusedAddMulKernel(half* x, half* y, half* z,
half scale, uint32_t length) {
LocalTensor<half> lx, ly, lz, temp;
// 加载
Load(lx, x, length);
Load(ly, y, length);
// 临时缓冲区
temp = AllocTemp(length);
// Add
Add(temp, lx, ly, length);
// Mul
Mul(lz, temp, scale, length); // 标量乘法
// 存储
Store(z, lz, length);
}
// Fused Exp-Add: z = exp(x) + y
void FusedExpAddKernel(half* x, half* y, half* z, uint32_t length) {
LocalTensor<half> lx, ly, lz, exp_x;
Load(lx, x, length);
Load(ly, y, length);
exp_x = AllocTemp(length);
// Exp
Exp(exp_x, lx, length);
// Add
Add(lz, exp_x, ly, length);
Store(z, lz, length);
}
4. 归约子程序
底层归约操作。
#include "atvcoss/atvcoss.h"
using namespace atvcoss;
// 求和归约
half SumReduceKernel(half* x, uint32_t length) {
LocalTensor<half> lx;
Load(lx, x, length);
// 分块归约
half sum = 0;
constexpr uint32_t BLOCK_SIZE = 256;
uint32_t blocks = (length + BLOCK_SIZE - 1) / BLOCK_SIZE;
for (uint32_t i = 0; i < blocks; i++) {
uint32_t offset = i * BLOCK_SIZE;
uint32_t block_len = min(BLOCK_SIZE, length - offset);
LocalTensor<half> block = lx.Slice(offset, block_len);
sum += ReduceSum(block);
}
return sum;
}
// 最大值归约
half MaxReduceKernel(half* x, uint32_t length) {
LocalTensor<half> lx;
Load(lx, x, length);
half max_val = ReduceMax(lx, length);
return max_val;
}
5. Softmax 子程序
从头构建 Softmax。
#include "atvcoss/atvcoss.h"
using namespace atvcoss;
void SoftmaxFromScratchKernel(half* x, half* y,
uint32_t batch, uint32_t classes) {
for (uint32_t i = 0; i < batch; i++) {
half* row_x = x + i * classes;
half* row_y = y + i * classes;
LocalTensor<half> lx, ly;
Load(lx, row_x, classes);
// Step 1: Find max (numerical stability)
half max_val = ReduceMax(lx, classes);
// Step 2: Subtract max
LocalTensor<half> shifted = AllocTemp(classes);
SubScalar(shifted, lx, max_val, classes);
// Step 3: Exp
LocalTensor<half> exp_vals = AllocTemp(classes);
Exp(exp_vals, shifted, classes);
// Step 4: Sum
half sum = ReduceSum(exp_vals, classes);
// Step 5: Normalize
DivScalar(ly, exp_vals, sum, classes);
Store(row_y, ly, classes);
}
}
6. LayerNorm 子程序
从头构建 LayerNorm。
#include "atvcoss/atvcoss.h"
using namespace atvcoss;
void LayerNormFromScratchKernel(half* x, half* y,
half* gamma, half* beta,
uint32_t batch, uint32_t hidden) {
for (uint32_t i = 0; i < batch; i++) {
half* row_x = x + i * hidden;
half* row_y = y + i * hidden;
LocalTensor<half> lx, ly, lgamma, lbeta;
Load(lx, row_x, hidden);
Load(lgamma, gamma, hidden);
Load(lbeta, beta, hidden);
// Step 1: Mean
half mean = ReduceSum(lx, hidden) / (half)hidden;
// Step 2: Variance
LocalTensor<half> centered = AllocTemp(hidden);
SubScalar(centered, lx, mean, hidden);
LocalTensor<half> squared = AllocTemp(hidden);
Mul(squared, centered, centered, hidden);
half variance = ReduceSum(squared, hidden) / (half)hidden;
// Step 3: Normalize
half std = SqrtScalar(variance + 1e-5);
LocalTensor<half> normalized = AllocTemp(hidden);
DivScalar(normalized, centered, std, hidden);
// Step 4: Scale and shift
Mul(ly, normalized, lgamma, hidden);
Add(ly, ly, lbeta, hidden);
Store(row_y, ly, hidden);
}
}
性能对比
在昇腾 910 上对比 atvc 和 atvcoss:
| 操作 | atvc | atvcoss | 说明 |
|---|---|---|---|
| Add 1M | 0.08ms | 0.08ms | 相同 |
| Softmax 1Kx1K | 0.35ms | 0.30ms | atvcoss 更快 |
| LayerNorm 1Kx1K | 0.45ms | 0.38ms | atvcoss 更快 |
| 自定义融合算子 | 不支持 | 支持 | atvcoss 可自定义 |
atvcoss 在复杂算子上更有优势,因为可以精细控制每一步。
怎么用?
方式一:直接使用子程序
#include "atvcoss/atvcoss.h"
using namespace atvcoss;
extern "C" __global__ __aicore__ void my_kernel(GM_ADDR x, GM_ADDR y, uint32_t length) {
LocalTensor<half> lx, ly;
// 加载
Load(lx, (__gm__ half*)x, length);
// 计算
Add(ly, lx, (half)1.0, length); // y = x + 1.0
// 存储
Store((__gm__ half*)y, ly, length);
}
方式二:构建自定义算子
#include "atvcoss/atvcoss.h"
using namespace atvcoss;
// 自定义激活函数: y = x > 0 ? x : 0.1 * x
class LeakyReLU {
public:
void Compute(half* x, half* y, uint32_t length) {
LocalTensor<half> lx, ly;
Load(lx, x, length);
// 需要自己实现条件逻辑
for (uint32_t i = 0; i < length; i++) {
ly(i) = lx(i) > (half)0.0 ? lx(i) : lx(i) * (half)0.1;
}
Store(y, ly, length);
}
};
方式三:组合 atvc 和 atvcoss
#include "atvc/atvc.h"
#include "atvcoss/atvcoss.h"
// 简单操作用 atvc
using namespace atvc;
// 复杂操作用 atvcoss
using namespace atvcoss;
extern "C" __global__ __aicore__ void hybrid_kernel(GM_ADDR x, GM_ADDR y, uint32_t length) {
// 简单操作:用 atvc
Vector<half> vx((__gm__ half*)x, length);
Vector<half> vy((__gm__ half*)y, length);
// 标准 ReLU:atvc 一行搞定
vy = Relu(vx);
// 复杂操作:切换到 atvcoss
LocalTensor<half> lx, ly;
Load(lx, (__gm__ half*)x, length);
// 自定义逻辑...
// ...
Store((__gm__ half*)y, ly, length);
}
应用场景
场景 1:自定义融合算子
// 融合算子: LayerNorm + GELU + Dropout
void FusedLayerNormGeluDropout(half* x, half* y,
half* gamma, half* beta,
float dropout_prob,
uint32_t batch, uint32_t hidden) {
for (uint32_t i = 0; i < batch; i++) {
// LayerNorm
// ...
// GELU
// ...
// Dropout
// ...
}
}
场景 2:性能极致优化
// 手动优化 Softmax
void OptimizedSoftmax(half* x, half* y, uint32_t batch, uint32_t classes) {
// 手动分块
// 手动流水线
// 手动向量化
// 达到极致性能
}
场景 3:研究新算子
// 实验性算子:线性注意力
void LinearAttention(half* q, half* k, half* v, half* y,
uint32_t batch, uint32_t seq_len, uint32_t head_dim) {
// 自定义实现
// 不受限于标准算子库
}
踩坑指南
-
内存管理
- atvcoss 需要手动管理临时缓冲区
- 注意内存对齐
- 避免内存泄漏
-
数据类型
- 主要支持 FP16
- 精度问题要注意
- 数值稳定性自己保证
-
性能优化
- 手动分块
- 手动向量化
- 流水线优化
-
调试
- 没有高层 API 的保护
- 错误更难排查
- 需要深入理解硬件
总结
atvcoss 是昇腾的 Vector 算子子程序模板库:
- 底层子程序:Load/Store/Add/Mul…
- 灵活组合:自定义构建算子
- 高性能:手动优化达极致
- 复杂算子:标准库不支持的场景
更多推荐




所有评论(0)