第四节:待测算子接口分析

做算子精度对齐,第一步不是写代码,而是看接口。

我见过太多人上来就对着算子一顿测,结果数据排布搞反了,精度对了一整天都对不上。说白了,你连输入输出长什么样都没搞清楚,怎么对齐?

4.1 接口文档,你的第一道防线

拿到一个自定义算子或者芯片算子,我个人的习惯是——先找接口定义。不管是头文件里的函数声明,还是芯片手册里的API说明,先把输入输出参数列出来。

举个例子,一个典型的卷积算子接口可能长这样:

// 自定义卷积算子接口示例
int conv2d(
    void* input,          // 输入张量
    void* weight,         // 权重张量
    void* bias,           // 偏置张量(可选)
    void* output,         // 输出张量
    int* input_shape,     // 输入形状 [N, H, W, C] 或 [N, C, H, W]
    int* weight_shape,    // 权重形状
    int* output_shape,    // 输出形状
    int data_type,        // 数据类型:0=FP32, 1=FP16, 2=INT8
    int layout            // 数据排布:0=NHWC, 1=NCHW
);

嗯,这里要注意——input_shape 的四个维度到底怎么排?是 NHWC 还是 NCHW?这个不搞清楚,后面全白干。

4.2 数据排布:NHWC vs NCHW

数据排布这件事,说白了就是多维数组在内存里怎么放的。我刚开始做算子开发时,在这上面栽过跟头。

NHWC(TensorFlow 默认):

  • 内存布局:先放 N,再放 H,再放 W,最后放 C
  • 特点:通道维在最后,对 CPU 更友好
  • 典型场景:TensorFlow、ONNX 部分模型

NCHW(PyTorch 默认):

  • 内存布局:先放 N,再放 C,再放 H,再放 W
  • 特点:通道维在最前,对 GPU 更友好
  • 典型场景:PyTorch、Caffe、大部分芯片算子库

我曾经在对接一个芯片算子时,文档里写的是 NHWC,但实际底层实现是 NCHW。结果我按 NHWC 传数据,精度对出来全是乱的。排查了两天才发现是排布问题。你想想看,这种低级错误多耽误事。

4.3 数据类型:FP32 / FP16 / INT8

数据类型决定了精度和性能的取舍。我一般会先看算子支持哪些类型,然后根据需求选。

数据类型 位宽 精度 典型场景
FP32 32位 高(约7位有效数字) 训练、精度验证基准
FP16 16位 中(约3位有效数字) 推理加速、移动端部署
INT8 8位 低(约2位有效数字) 量化推理、边缘设备

做精度对齐时,我建议先用 FP32 跑一遍,确保逻辑正确。然后再切到 FP16 或 INT8,看精度损失在不在可接受范围内。

小技巧:如果算子支持 FP16,但你的测试数据是 FP32,记得先做类型转换。我一般用 np.float32 转 np.float16,或者用芯片提供的 cast 接口。

4.4 接口参数解析实战

假设我们要分析一个芯片上的矩阵乘算子。接口定义如下:

// 芯片矩阵乘算子
int matmul(
    const void* A,        // 输入矩阵 A
    const void* B,        // 输入矩阵 B
    void* C,              // 输出矩阵 C
    int M, int N, int K,  // 矩阵维度:A[MxK], B[KxN], C[MxN]
    int dtype_A,          // A 的数据类型
    int dtype_B,          // B 的数据类型
    int dtype_C,          // C 的数据类型
    int transA,           // 是否转置 A:0=否,1=是
    int transB            // 是否转置 B:0=否,1=是
);

这里有几个关键点:

  • 维度顺序:M、N、K 的顺序不能搞反。我习惯在代码里加个注释:// A[MxK] * B[KxN] = C[MxN]
  • 数据类型组合:A 和 B 可以是 FP16,C 可以是 FP32。这种混合精度在芯片上很常见。
  • 转置标志:transA 和 transB 会影响数据读取方式。我曾经忘了设转置,结果矩阵乘法结果完全不对。

避坑指南:我曾经在对接一个芯片算子时,接口文档里写的是 transA=0 表示不转置,但实际芯片实现里 transA=0 表示转置。这种文档和实现不一致的情况,一定要通过实际测试验证。

4.5 知识体系总览

下面这张图,是我做算子接口分析时脑子里过的流程。你可以参考一下:

算子接口分析知识体系 算子接口分析 输入输出参数 数据排布 数据类型 形状 内存地址 NHWC NCHW FP32 FP16 INT8 接口分析 → 数据准备 → 精度对齐 → 验证通过

4.6 实操建议

最后,给你几个实操建议:

  1. 先读文档,再写代码:把接口参数一个个列出来,确认每个参数的含义和取值范围。
  2. 写个接口验证小脚本:用简单的输入(比如全1矩阵)跑一遍,看输出是否符合预期。
  3. 数据排布和类型要反复确认:我一般会在代码里加断言,比如 assert(layout == NHWC || layout == NCHW)。
  4. 保留接口分析记录:把每个参数的含义、测试结果记下来,方便后续排查。

嗯,接口分析这一步虽然枯燥,但做好了能省后面80%的调试时间。别嫌麻烦,慢慢来比较快。

想要更多资料,联系可可