第四节:待测算子接口分析
做算子精度对齐,第一步不是写代码,而是看接口。
我见过太多人上来就对着算子一顿测,结果数据排布搞反了,精度对了一整天都对不上。说白了,你连输入输出长什么样都没搞清楚,怎么对齐?
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 知识体系总览
下面这张图,是我做算子接口分析时脑子里过的流程。你可以参考一下:
4.6 实操建议
最后,给你几个实操建议:
- 先读文档,再写代码:把接口参数一个个列出来,确认每个参数的含义和取值范围。
- 写个接口验证小脚本:用简单的输入(比如全1矩阵)跑一遍,看输出是否符合预期。
- 数据排布和类型要反复确认:我一般会在代码里加断言,比如
assert(layout == NHWC || layout == NCHW)。 - 保留接口分析记录:把每个参数的含义、测试结果记下来,方便后续排查。
嗯,接口分析这一步虽然枯燥,但做好了能省后面80%的调试时间。别嫌麻烦,慢慢来比较快。
想要更多资料,联系可可