Skip to content

第一次在算子层写代码

2026 年 8 月。参加国产 AI 芯片的算子题目:二维卷积、激活函数、量化 GEMM,以及一个"消除末维上限"的实现。在此之前,我写的都是"调用"算子的代码。

和模型层完全不同的三件事

地址空间是显式的。 在模型层写 x.to(device) 就完事了,在算子层要区分这段内存属于哪一类(全局、共享、寄存器),并决定数据在哪一级之间搬运。第一版代码编译不过,原因就是地址空间标注错了。

分块决定了性能。 同一个卷积,按不同方式切分输入与输出,性能可以差出好几倍。原因不复杂:芯片的片上缓存有限,块切得不好就要反复从外部存储读同一份数据。把"数据复用"这件事想清楚,比堆优化技巧更有效。

类型是逐个分支处理的。 一个"简单"的激活函数,要分别处理 FP16、FP32、BF16 三种数据类型,还要覆盖 0–8 维的不同形状;量化 GEMM 则要额外处理整数与浮点的混合计算。写的时候必须把每种组合都想清楚,不能靠"跑一遍看看"。

做过的几道题

题目关键点
二维卷积(FP16 / FP32 / BF16)三种数据类型的分别实现与分块策略
逐元素激活(HardSwish,0–8 维)维度分支与形状对齐,避免小形状退化
INT8 量化 GEMM(小 M/N 大 K)量化参数处理与累加精度,K 维分块
消除末维 65535 上限 + 转置组合突破单次搬运的形状限制,处理转置后的访存模式

收获

最大的收获不是学会了某个具体写法,而是对"性能从哪来"有了具体的感知:不是靠更聪明的技巧,而是靠把数据搬运的次数降下来。这个视角反过头来影响了我做模型训练时的判断——例如在显存受限时如何设置批次与分块,本质上考虑的是同一件事。

由 VitePress 构建 · 部署于 Cloudflare Pages 与 GitHub Pages