t1: Addcmul
t2: ClipByValue
t3: Lerp
做题与优化过程总结
1. 开发方式
在 tutorial 中发现有内置的 skills,因此我主要采用 Codex SSH 远程主机 的方式来实现代码。
2. 基础实现情况
整体来看,基础实现并不困难。
其中,第一题需要特别处理两个问题:
- Broadcast
- int8 类型及溢出模拟
我的做题顺序是:
T2 → T3 → T1
这也对应了我主观认为的题目复杂度顺序。
3. 初始优化思路
一开始,我的优化方向主要集中在:
- 针对不同数据范围决定开多少核;
- 选择合适的 tiling 大小;
- 研究如何进行数据切分。
我做了一天的矩阵实验,但始终没有明显优化效果。
后来我开始尝试套取实际数据范围,尤其是发现 T3 的所有测试点都小于 256KB,并且 6/7 个测试点小于 16KB。这和我原本预想的数据范围差距非常大,也改变了后续的优化方向。
4. 优化 Skill 的整理与尝试
另一个方向是参考网上已有经验,整理并 distill 出一个优化 skill。
4.1 Tiling 策略
由于大多数数据都不到 128MB,实际上每个 AI Core 中通常只需要做一次计算,因此复杂 tiling 的优化意义很小。
4.2 头尾开销优化
在头尾开销方面,比较值得优化的是:
- 设置合适的核数;
- 限制
TilingData结构大小。
除此之外,其他方向的收益似乎并不明显。
4.3 流水编排
考虑到 AI Core 大多数情况下只需要执行一次,double buffer 在大部分情况下反而是负优化。
只有在数据较大的时候,double buffer 才可能带来一定收益。但这个收益往往小于评测机本身的波动。因此,我只在部分题目中实现了 double buffer。
4.4 内存访问
在内存访问方面,我主要做了以下处理:
- 将 UB 使用量开到
ub_limit的 80% 左右; - GM 地址按照 512B 对齐;
- 尽量减少搬运次数和无效搬运。
由于数据规模较小,L2 Cache 切分没有必要。实际测试中,命中 L2 Cache 似乎也可能是负优化。
4.5 矢量计算
在矢量计算方面,Unified Buffer 内融合 是比较基础也比较必要的优化。
4.6 msProf 调试与优化
之后,我参考了一些优秀实践,用 msProf 进行实验,并 distill 了一个 msProf 调试 + 优化 skill。
5. 对优化方法的反思
上述 skill 在最后的优化中帮助并不大,尤其是考虑到评测机波动等因素。
我认为真正最有用的还是:
- 根据实际数据范围确定核数;
- 根据数据范围设计 tiling 方案;
- 挖掘测试数据的特殊性质。
6. Tiling 方面
Tiling 的核心思路是结合实际数据范围进行设计,而不是只根据理论上的大数据场景进行优化。
由于很多测试点数据量较小,过于复杂的 tiling 反而可能带来额外开销。因此,tiling 策略应尽量保持简单,并减少无效切分。
7. 核数选择
核数方面,我采用了:
- 本地矩阵实验;
- OJ 提交测试。
本地测试环境是 A3,和云端芯片并不完全一致,再加上一些其他因素,本地实验结果和云端结果很难完全对应。
因此,最终还是以 OJ 上的直接提交结果 更为权威。
8. 特殊数据性质挖掘
在 T1 中,我全面实现了 broadcast。随后通过分析特殊数据,发现了一些重要性质:
dimensions <= 5;- 大部分数据不会进入 broadcast 分支;
- 只有两个测试点需要实现 int8;
- 其中一个 int8 测试点还需要模拟 int8 溢出。
根据这些性质,我最终舍弃了其中一个测试点,但优化了 shape 判断和 tiling 结构,取得了比较高的收益。
9. 最后的调参方式
后期开始进行数据拟合,主要通过直觉调整核数。
一般来说,我会在本地实验结果的基础上,把核数往更大的方向调一些,然后多次提交 OJ,寻找评测效果较好的配置。
总结
整体来看,这次优化中最重要的经验是:
不要一开始就陷入通用优化模板,而是要先弄清楚真实数据范围和测试点特征。
在数据量较小、评测波动明显的情况下,复杂的流水、L2 Cache 切分、double buffer 等优化不一定有效,甚至可能带来负收益。
最终真正有效的优化主要来自:
- 数据范围分析;
- 核数选择;
- tiling 结构简化;
- 特殊测试点性质挖掘;
- 多次 OJ 提交拟合评测结果。
最后拿下 rk1,可能是参加的人比较少吧。