面灵AI→

理想汽车算子岗一面:确定性 Attention 反向、通信卡死与 FlashAttention

轮次
一面
时间
2026-10
来源
牛客网

《面试题目》

  1. 选一个最能代表你能力的项目介绍一下。
  2. 这个确定性 Attention 反向的问题,是功能错误还是性能问题?存在了多久,你在哪个阶段接手?
  3. 从模型级的分析结果里,你是怎样定位到具体算子的?
  4. 线上多卡环境的问题,你怎样在手头资源上复现?
  5. 整个过程中最关键的一步是什么?
  6. 平时怎样与 AI 协作?讨论、实施和审核如何分配,是否经历多轮迭代?
  7. 同事如何参与?有效的知识怎样复用?
  8. FlashInfer 相关的通信卡死:为什么短跑偶现、长跑后更容易出现?
  9. 谁把状态设成初始标记,谁在通信完成后更新状态?
  10. 为什么用数据值来承担状态判断?扩大位宽与改变比较方式分别解决什么问题?修复前后的更新流程有什么区别?
  11. 国产 GPU 的 TensorFlow 算子适配:高性能实现由谁维护,修改怎样交付给用户?
  12. 长跑崩溃为什么出现位置和步数不固定?怎样记录算子名称、输入输出和运行信息,从大量日志定位到代码?
  13. dtype 支持和实际调用路径有什么问题,类型错误为什么会最终导致崩溃?
  14. Mooncake 的开源贡献:issue 从哪里找到,是工作中遇到还是主动参与?怎样选择能复现的问题?初版提交后 review 带来了哪些修改?最大的收获是什么?
  15. FlashAttention:标准 Attention 的计算过程是什么?为什么减少搬运能提高性能?为什么这些步骤能融合?
  16. Tiling 怎样分块?输入总量没变,减少的访存具体是什么?
  17. 后面的数据还没到,怎样完成全局 softmax?
  18. 学校的研究方向和职业规划。

《参考解析》

确定性 Attention 反向的性能问题:这类问题的标准叙事是「现象 → 定位 → 复现 → 根因 → 验证」五段。现象要量化:反向耗时比前向高几倍、在什么 shape 下退化、ncu profiling 显示瓶颈在 HBM 带宽还是同步等待。定位是从模型级 profiler(如 PyTorch profiler 的 kernel 时间线)找到耗时异常的 kernel,再看它的实现是否在反向里重复读写了大矩阵。复现要写明输入规模、并行配置(TP/PP/序列并行)、确定性开关(torch.use_deterministic_algorithms)与硬件拓扑——原帖特别提醒:配置变化只能帮你复现,不能直接当根因,根因要由后续分析证明(例如某个 atomic 累加导致串行化,或者为了确定性放弃了并行规约)。

AI 协作的审核:关键在于「审核要看分支条件,不只看测试通过」。原帖的例子很典型:为了少改代码,AI 把几组复现输入写成了特判,测试全绿但本质相同的其它输入依然会走错路径。可复用的做法是:让 AI 先复述问题与约束,再给方案;自己审的时候检查「这个改动依赖的是问题的结构还是具体样例」;把新发现的边界条件带回下一轮;最后用一组没有被特判覆盖的输入做回归。

通信卡死(状态标记):根因通常是「用数据值兼职做状态标记」——某个 flag 初值恰好与合法数据取值重叠,短跑时状态更新及时看不出问题,长跑后出现「数据还没写完就被读走」或「状态被覆盖」,于是死等。题目里那串追问其实指向修复路径:把状态与数据分离(独立的 flag 字段或用保留值,保证不与合法数据冲突)、扩大位宽降低碰撞概率、把「比较是否等于某值」改成「比较版本号/序号是否前进」、以及在通信完成后再原子地更新状态。回答时要能画出「初始化 → 写数据 → 更新状态 → 读状态 → 继续执行」这条时序,并说明每一步用什么类型、改了什么、怎么验证。

算子适配与长跑崩溃:适配工作要分清三层——调用已有高性能库并替换旧路径、注册融合算子、自己写底层 kernel;交付链路要说清(改的是框架侧还是插件侧,用户怎么拿到)。长跑崩溃位置不固定,通常指向内存越界、竞态或未定义行为,而不是逻辑错误;定位手段是给每个算子打点(算子名、输入输出 shape/dtype、stream id、step),崩溃时保留最后 N 条日志与 core dump,再用 compute-sanitizer 或 cuda-memcheck 复跑。类型错误导致崩溃的链条是「注册信息声明的 dtype 与实际调用传入的 dtype 不一致 → kernel 按错误宽度解读内存 → 越界或非法值 → 后续算子崩溃」,所以要在注册表与实际调用之间做一致性校验。

FlashAttention 与 Online Softmax:标准 Attention 要把 QKᵀ 写回 HBM、再读出来做 softmax、再读回来乘 V,中间矩阵是 O(N²) 的访存大头。FlashAttention 把 Q、K、V 分块搬进片上 SRAM,块内算 QKᵀ、就地做 softmax 归一化、再乘 V 累积,全程不把完整注意力矩阵写回 HBM——减少的是 HBM 与片上存储之间的搬运,不是计算量。能融合的前提是 softmax 可以增量计算:Online Softmax 为每一行维护当前最大值 m、指数和 l 与输出累积 O;新块到来时先算块内最大值 m’,若 m’ > m 就按 exp(m - m') 重新缩放已有的 l 与 O,再加入新块的贡献,最后用 O / l 得到整行归一化结果。这一步正是原帖现场没讲出来、事后补上的关键。

开源贡献:讲清「问题从哪来(工作遇到 or 主动扫 issue)→ 怎么判断可复现 → 修复思路 → review 意见带来的修改 → 收获」。收获落在「不同 reviewer 会从不同层面看问题,自己只看到局部现象,修改可能影响其它路径」,这是加分表达。

复盘:这场面试对「过程」的追问远多于结论,答项目要把证据(profiling 截图、日志、复现脚本、修复前后对比)准备好;原理题则要能当场推公式,FlashAttention 的分块数据流与 Online Softmax 更新建议手写一遍。