性能¶
单卡算子调优:先把执行过程看见,再逐个走过时间真正花掉的地方。
问题的形状¶
一个 PyPTO kernel 的墙上时间花在三台不同的机器上,而它们的失效方式各不相同:
host 编排 AICore
│ │ │
├─ 拷贝 ├─ 任务派发 ├─ kernel 本身
│ (06-host) │ (01, 02, 03) │ (04-incore)
│ │ │
└────────────── 内存:片上缓冲 + 运行时 ring (05-memory) ──────┘
第一次调优大多直奔第三列——kernel 里的算术——然后发现问题出在前两列。本章的顺序就是按它们通常咬人的顺序排的。
目录¶
| 页面 | 内容 |
|---|---|
| 读泳道图 | 采集并打开 L2 泳道图 —— 唯一能显示时间去哪了的视图 |
| 任务粒度 | 派发不是免费的;如何放大与合并 InCore 函数而不饿死核 |
| 运行时开销 | mix kernel、SPMD、allow_early_resolve、kernel 内 syncall |
| 管理任务依赖 | 运行时为何把本可并行的活串起来,以及怎么告诉它不用 |
| InCore 函数调优 | double buffer、算法切分、L0 指令级 trace、硬件粒度、外部 kernel |
| 内存 | 四层 scope-depth ring、scope 放置与 ring 尺寸 |
| Host | 让常驻的数据真的常驻 |
怎么用¶
下面每一项手段都用同样的四个字段来写,因为一个说不出代价的加速不算结果:
| 字段 | 回答什么 |
|---|---|
| 何时适用 | 什么症状说明该用这一招 |
| 怎么做 | 那一处代码改动 |
| 代价 | 它花掉了什么 —— 内存、通用性,或一份从此归你的正确性义务 |
| 怎么确认 | 哪个产物能证明它起作用了,以及其中什么该发生变化 |
本章不给加速数字。 它取决于你的形状、平台与工具链版本,而一个过期的数字比没有更糟——因为你没法察觉它过期了。确认那一步才是可迁移的部分:在你自己的 kernel 上跑一遍,你就得到属于你的数字。
动手之前¶
有两件更便宜的事排在这一切之前,而且都已经替你做好了:
- 构建输出里的
report/perf_hints.log。 编译器把它在编译期注意到的东西写在这里 —— 过小的搬运、它没能 tile 的 matmul、没放得下的流水深度。每次编译还会往 stderr 打一行摘要。 - host / device 的分野。
run()并不返回计时对象 —— 它的execution_time是整段墙上时间,含编译与 golden。要用pypto.runtime.benchmark,它的BenchmarkStats分开给出device_wall_us与host_wall_us。如果时间在 host 上,00–05 页里没有任何东西能动它 —— 直接去 Host。