Codex 自动研究实战:GPU Mode QR 分解竞赛 232 倍提速的三层方法

开源AIGPU

本文是对 sankalp 博客文章《Auto-research with codex: How I achieved a 232x Faster Kernel over baseline with Codex in GPU Mode's qr_v2 problem》的技术解读,原文于 2026 年 7 月 8 日发布。

GPU Mode 与 Core Automation 合办的 qr_v2 竞赛给出了一个看似传统的题目:为批量方形 FP32 CUDA 矩阵实现紧凑 Householder QR 分解,输出与 torch.geqrf 一致的 (H, tau) 紧凑表示。判定程序用 torch.linalg.householder_product 重建 Q,取 R = triu(H),验证 A ≈ QR、Q^T Q ≈ I、Q^T A ≈ R。183 名参赛者中排第 12 位的一位选手,用 14 天时间、1500+ 次提交,把全表几何平均耗时从基线 torch.geqrf/cuSolver 路径的约 419,000 µs 压到 1,805 µs,提速 232 倍。他把自己的完整方法写成了长文,核心是三层结构:数学层(blocked Householder + WY 更新)、执行层(Codex 驱动的反馈闭环)、策略层(beam of candidates 逃离局部最优)。

比赛为什么天然适合 agent 循环

赛题指定实现批量方形紧凑 Householder QR 分解,矩阵规模覆盖 n = 32 到 n = 4096,允许内部使用 FP16/FP8/NVFP4 低精度,但返回结果必须通过 FP32 级别的 QR 检查。组织方提供 popcorn CLI,agent 可以直接测试、基准测试、提交到排行榜,且判定程序会返回逐形状反馈和整体几何平均耗时。

这个设置的关键在于反馈回路的完整性。agent 每次提交都能拿到分形状的耗时数据,等于每一步都有可量化的验证信号。作者引用了一句概括:Agents yearn for tight feedback loops(agent 渴望紧凑的反馈循环)。在允许的提交节奏内,14 天里完成了超过 1500 次提交。

基线是 torch.geqrf 的约 419 ms。作者在实现 blocked Householder 路线后的第一天就在权重最高的 n = 512 形状上达到 5,000 µs,此后优化难度陡增,3,000 µs 之后每一步都需要人更深入地介入。

从串行依赖到 GEMM 形状:算法层的核心

Householder QR 的原生形态是串行的。第 j+1 个反射镜(reflector)必须基于第 j 个反射镜作用后的矩阵构建,无法重排也无法融合。串行的矩阵向量运算跑在 SM 的慢速向量通道上,而 tensor core 完全闲置。这构成第一层瓶颈。

解法是 blocked Householder 算法:取宽为 b(如 32 或 64)的窄面板列,所有串行工作被限制在面板内完成。随后,将面板的 b 个反射镜压缩成单次 rank-b 更新(WY 表示),用三次背靠背矩阵乘一次性作用到整个尾随块(trailing block)。串行工作被局限在廉价的面板内,其余全部变成 GEMM,恰好是 tensor core 最擅长的形状。

具体地,WY 表示把面板的 b 个反射镜堆成 V = [v₁, v₂, …, v_b],构建小的 b×b 上三角 T,得到 H₁H₂…H_b = I − V T V^T。尾随块更新拆成三步:W = V^T A_trail,Z = T^T W,A_trail ← A_trail − V Z。面板沿对角线推进,尾随块逐步缩小。

作者给出的十步优化阶梯完整记录了从 108.8k µs 到 1.80k µs 的每一步结构变化:

#结构变化类型几何平均
1全形状 torch.geqrf起点>108.8k µs
2n512 上 blocked WY QR算法/路由108.8k µs
3全形状 blocked 路线算法/路由10.2k µs
4Triton 面板 + grouped WY内核/运行时4.3k µs
5n4096 用 Cholesky-ORHR算法/路由4.0k µs
6CUDA graph 重放内核/运行时3.4k µs
10组合 superpanel + 自定义 Cholesky内核/运行时1.80k µs

(完整十步见原文,此处摘录关键节点。)

Codex 执行层:goal 循环与日志纪律

作者选择 Codex 的理由:ChatGPT Pro 订阅额度更大;从过往经验看 OpenAI 模型在 Triton 上更强;/compaction 在 Codex 里工作得很好。工具栈成本是 ChatGPT Pro 200 美元/月 + Claude Pro 20 美元/月,外加 Modal 每月免费 30 美元额度做 profiling。

harness 的搭建直接交给 Codex:problem_statement.md 记录题目、AGENTS.md 写提交规则和 popcorn CLI 用法、log.md 记录每次提交的接受/拒绝状态和分形状耗时。作者明确把这套 setup 与 Karpathy 的 autoresearch 项目中的 program.md 类比,这是对 program.md 的实践延续。

日志是想法的证据。3000 µs 之后作者加大了对日志的投入——未来的 agent 会话可以直接读取日志,快速判断某个想法是否已被尝试过。工作目录最终长成这样:

text
qr/
├── submission.py                    # 活跃提交入口
├── submission_*.py            (560) # 命名提交变体
├── modal_b200_*.py            (119) # Modal B200 探测/对比脚本
├── AGENTS.md                        # 顶层笔记
├── attempts_log.md / claude_ideas.md / leaps.md / problem_statement.md
├── docs/                      (68)  # 每个实验的写档
├── code/                            # QR 内核源码树
└── archive/                         # 归档的旧提交与探测

Codex goal 循环连续运行超过 25 小时

/goal 是驱动长循环的核心指令。给一个具体、可量化、可达成的数字目标加判据,例如「只用 Triton 或 CUDA,击败当前最优的 n = 512 耗时,彻底移除 cuSolver,仅作回退用」。这样的 goal 一次能跑一天以上。作者每 2-3 小时给一次输入调整方向,部分夜晚完全无人值守运行。

/btw/side 允许在不暂停主循环的前提下向模型提问,开辟带主对话上下文的临时线程。作者用这种方式检查进度、理解当前算法、获取下一步建议,再回到主线程把想法倾倒进去。他的经验:信任 agent,让它工作,只在卡住时才介入。别频繁查看 agent,要让 agent 做事,这是他强调的纪律。

逃离局部最优:beam of candidates

3000 → 1800 µs 阶段暴露了纯 hill-climbing 的局限。模型陷入局部最优的表现是无限重复参数微调和同一想法的小变体。作者引入 beam of candidates:同时维护 3-5 个活跃的想法族,而非单一最优候选。理由是新结构想法首测大概率跑不过现任最优,但迭代几轮后可能反超,单一候选机制会过早杀死这些成分。

AGENTS.md 中写入了完整的 beam 纪律条款:每条 beam 记录父候选、假设、改动函数、当前最优与下一步 singleton/组合/淘汰决策;至少保持 3 条活跃 beam(exploit 近最优、near-miss 差一点、structural 高风险);不许在孤立单例失败后宣判想法族死刑;两个各自中性偏慢但命中独立开销的想法,先尝试组合再考虑退役;淘汰 beam 必须给出明确理由(固有正确性失败、合理重调后仍持续回归、singleton 与组合都输、profiling 显示目标开销已不构成瓶颈、实现成本阻塞更高价值 beam)。

作者引用 maja 的话作结:提出更好问题的人塑造了我们

配套策略还包括:human in the loop(作者自任卡壳时的转向器)、鼓励模型冒险尝试野心想法、用更强的 advisor 模型产生更多样的想法(在 AGENTS.md 里指示模型用 claude -p headless 调用拿想法)、频繁派 sub-agent 搜索博客论文找微优化、用 NCU 和 Modal 双重 profiling 交叉定位瓶颈、定期清理上下文与环境归档旧文件。作者判断 strong advisor 策略(如 GPT-5.6 Sol 或 Fable 这类前沿大模型)会成为 auto-research 流程的标准配置,Claude Code 已提供 /advisor 命令。

作者注意到一个代际变化:两年前的 agent 因为不够聪明或验证回路不够稳而陷入 doom loop,当时的研究方向是采样、温度和解码策略。模型变聪明之后,瓶颈转移到 idea generation(想法生成)与研究品味:给定验证反馈、既有证据和 profiling 数据,下一个最优实验是什么。他把 good idea generation 称作 the next great adventure(下一场大冒险)。

复盘:没做好的部分

作者复盘时对照了排行榜前 10 的提交和第 5 名 Mike 的 writeup。n = 512 和 n = 1024 案例包含 dense、clustered、rank-deficient、mixed、near-rank 多种输入分布,更快的内核写了数据检测器来利用分布特征(低秩案例有大量零元素)。前 10 的方案更激进地移除库函数,第 2 和第 5 名用自定义三角逆替代 PyTorch 的 triangular solve,而作者方案里存在大量 PyTorch 与 Triton 的往返。尾随矩阵可以常驻 fp16 避免表示间反复搬运,这属于作者自己未知的 unknown unknown。他也承认没能在 Blackwell 上用上 tcgen05(NVIDIA 第五代 tensor core 指令集)来进一步压榨 B200。beam of candidates 应该从第一天就启用。

写在最后

这篇文章记录的是一场个人参与的竞赛复盘,不是通用方法论论文。它的价值在于把 auto-research 的三层结构拆开给你看:数学层决定优化空间的上限,执行层决定反馈回路的转速,策略层决定搜索能否跳出局部最优。QR 分解恰好是个理想的示范载体,判定程序给数字,popcorn CLI 给通道,B200 给舞台。

第二场比赛(特征值分解)正在进行,作者在文末留了排行榜链接。对想上手的人,作者给出的顺序建议藏在字里行间:先学足够的领域知识把 unknown unknown 转成 known unknown,再搭 harness,再让 agent 跑,最后在卡住的地方出手。领域专长加速 harness 设计和 human-in-the-loop 转向,两者缺一,循环的效率都要打折。