Skip to content

TBE 张量加速引擎

TBE 概述

TBE(Tensor Boost Engine)是 CANN 的算子开发框架,提供两种开发模式:

模式语言特点适用场景
DSL 模式Python声明式,自动调度规则算子,快速开发
TIK 模式Python命令式,手动控制复杂算子,性能优先
TBE TIK 模式已被 Ascend C 逐步替代。新项目推荐使用 Ascend C。
TBE DSL 模式仍然有效,适合快速开发规则算子。

TBE DSL 模式

DSL(Domain Specific Language)模式使用类似 NumPy 的声明式语法描述算子计算逻辑,TBE 自动完成调度优化。

核心 API

python
from te import tvm
from te.platform.fusion_manager import fusion_manager
import te.lang.cce as tbe
from topi import generic

示例:实现 ReLU 算子

python
from te import tvm
import te.lang.cce as tbe
from te.platform.fusion_manager import fusion_manager
from topi import generic
from topi.cce import util

@fusion_manager.register("relu")
def relu_compute(input_x, output_y, kernel_name="relu"):
    """
    ReLU 算子计算函数
    y = max(x, 0)
    """
    # 使用 TBE DSL 描述计算
    res = tbe.vrelu(input_x)
    return res

def relu(input_x, output_y, kernel_name="relu"):
    """
    ReLU 算子入口函数
    """
    # 获取输入形状和数据类型
    shape = input_x.get("shape")
    dtype = input_x.get("dtype").lower()
    
    # 输入校验
    util.check_shape_rule(shape)
    util.check_dtype_rule(dtype, ["float16", "float32"])
    
    # 创建 TVM 占位符
    data = tvm.placeholder(shape, name="data", dtype=dtype)
    
    # 调用计算函数
    res = relu_compute(data, output_y, kernel_name)
    
    # 自动调度
    with tvm.target.cce():
        schedule = generic.auto_schedule(res)
    
    # 编译配置
    config = {"name": kernel_name, "tensor_list": [data, res]}
    
    # 编译生成二进制
    tbe.cce_build_code(schedule, config)

示例:实现 Add 算子

python
from te import tvm
import te.lang.cce as tbe
from te.platform.fusion_manager import fusion_manager
from topi import generic

@fusion_manager.register("add")
def add_compute(input_x, input_y, output_z, kernel_name="add"):
    """
    逐元素加法:z = x + y
    """
    res = tbe.vadd(input_x, input_y)
    return res

def add(input_x, input_y, output_z, kernel_name="add"):
    shape_x = input_x.get("shape")
    shape_y = input_y.get("shape")
    dtype = input_x.get("dtype").lower()
    
    # 广播处理
    shape_x, shape_y, shape_max = util.produce_shapes(shape_x, shape_y)
    
    data_x = tvm.placeholder(shape_x, name="data_x", dtype=dtype)
    data_y = tvm.placeholder(shape_y, name="data_y", dtype=dtype)
    
    # 广播到相同形状
    data_x_broadcast = tbe.broadcast(data_x, shape_max)
    data_y_broadcast = tbe.broadcast(data_y, shape_max)
    
    res = add_compute(data_x_broadcast, data_y_broadcast, output_z, kernel_name)
    
    with tvm.target.cce():
        schedule = generic.auto_schedule(res)
    
    config = {"name": kernel_name, "tensor_list": [data_x, data_y, res]}
    tbe.cce_build_code(schedule, config)

TBE DSL 常用 API

python
# 向量运算
tbe.vadd(x, y)      # 逐元素加
tbe.vsub(x, y)      # 逐元素减
tbe.vmul(x, y)      # 逐元素乘
tbe.vdiv(x, y)      # 逐元素除
tbe.vrelu(x)        # ReLU
tbe.vexp(x)         # 指数
tbe.vlog(x)         # 对数
tbe.vsqrt(x)        # 平方根
tbe.vabs(x)         # 绝对值

# 归约运算
tbe.sum(x, axis)    # 求和
tbe.reduce_max(x, axis)  # 最大值
tbe.reduce_min(x, axis)  # 最小值

# 矩阵运算
tbe.matmul(x, y, trans_a=False, trans_b=False)  # 矩阵乘

# 广播
tbe.broadcast(x, shape)  # 广播到目标形状

# 数据类型转换
tbe.cast_to(x, dtype)    # 类型转换

TBE TIK 模式

TIK(Tensor Iterator Kernel)提供更底层的控制,开发者需要显式管理数据搬运和计算流程。

TIK 核心概念

python
from te import tik

# 创建 TIK 实例
tik_instance = tik.Tik()

# 声明张量(在不同内存层次)
# GM:全局内存(HBM)
input_gm = tik_instance.Tensor("float16", (1024,), name="input", scope=tik.scope_gm)
output_gm = tik_instance.Tensor("float16", (1024,), name="output", scope=tik.scope_gm)

# UB:统一缓冲区(AI Core 本地)
input_ub = tik_instance.Tensor("float16", (256,), name="input_ub", scope=tik.scope_ubuf)
output_ub = tik_instance.Tensor("float16", (256,), name="output_ub", scope=tik.scope_ubuf)

TIK 数据搬运

python
# GM → UB(数据搬入)
tik_instance.data_move(
    input_ub,    # 目标(UB)
    input_gm,    # 源(GM)
    0,           # sid(通常为0)
    1,           # nburst(搬运次数)
    256 // 16,   # burst_len(每次搬运的 block 数,1 block = 32 字节)
    0,           # src_stride(源步长)
    0            # dst_stride(目标步长)
)

# UB → GM(数据搬出)
tik_instance.data_move(
    output_gm,
    output_ub,
    0, 1, 256 // 16, 0, 0
)

TIK 向量计算

python
# 向量加法
tik_instance.vec_add(
    128,         # mask(每次处理的元素数)
    output_ub,   # 目标
    input_ub,    # 源1
    input_ub,    # 源2
    1,           # repeat_times(重复次数)
    8, 8, 8      # dst_stride, src0_stride, src1_stride
)

# 向量乘法
tik_instance.vec_mul(128, output_ub, input_ub, weight_ub, 1, 8, 8, 8)

# 向量 ReLU
tik_instance.vec_relu(128, output_ub, input_ub, 1, 8, 8)

TIK 矩阵计算(Cube)

python
# 矩阵乘法(需要数据在 L0A/L0B 中)
# 先将数据从 UB 搬到 L0
input_l0a = tik_instance.Tensor("float16", (16, 16), scope=tik.scope_l0a)
weight_l0b = tik_instance.Tensor("float16", (16, 16), scope=tik.scope_l0b)
output_l0c = tik_instance.Tensor("float32", (16, 16), scope=tik.scope_l0c)

# 执行矩阵乘
tik_instance.mmad(output_l0c, input_l0a, weight_l0b, 16, 16, 16, False)

算子信息定义文件

每个算子需要一个 op_info_cfg.py 文件描述其元信息:

python
# custom_relu_op_info_cfg.py
from op_test_frame.ut import OpInfo

op_info = OpInfo(
    op_type="CustomRelu",
    inputs=[
        {"name": "x", "dtype": ["float16", "float32"], "format": ["ND", "NCHW"]}
    ],
    outputs=[
        {"name": "y", "dtype": ["float16", "float32"], "format": ["ND", "NCHW"]}
    ],
    attr=[],
    kernel_name="custom_relu"
)

算子编译与测试

编译算子

bash
# 使用 ATC 编译单算子
atc --singleop=op_list.json \
    --soc_version=Ascend910B3 \
    --output=./output

# op_list.json 格式
[{
    "op": "CustomRelu",
    "input_desc": [{"format": ["ND"], "type": "float16", "shape": [1, 1024]}],
    "output_desc": [{"format": ["ND"], "type": "float16", "shape": [1, 1024]}]
}]

单元测试

python
# 使用 op_test_frame 进行单元测试
from op_test_frame.ut import OpUT

ut = OpUT("CustomRelu", "custom_relu", "custom_relu")

# 添加测试用例
ut.add_case(
    params=[
        {"shape": (1, 1024), "dtype": "float16", "format": "ND", "ori_shape": (1, 1024)},
        {"shape": (1, 1024), "dtype": "float16", "format": "ND", "ori_shape": (1, 1024)}
    ],
    case_name="test_relu_fp16"
)

# 运行测试
ut.run("test_relu_fp16")

基于昇腾CANN文档整理