Home AI Infra 面试题(九):工程实现与实验深挖
Post
Cancel

AI Infra 面试题(九):工程实现与实验深挖

本文是AI Infra 面试题系列的 Level 9,覆盖 Q91-Q115。重点是DDP/NCCL/MoE/checkpoint/Triton/vLLM 实验的实现与归因细节。

上一篇:系统设计 · 系列总索引 · 下一篇:运行时与底层契约


10. Level 9:工程实现与现场深挖

这一层不再考“知道术语”,而是检查候选人是否真的写过 runner、读过 runtime 日志、定义过 correctness,并能解释反常结果。回答应主动给出代码路径、同步边界、指标口径和证据限制。

Q91. bucket_cap_mb=1,为什么实际最大 DDP bucket 仍有 93.75 MiB?

高分回答要点

  • bucket_cap_mb 是 reducer 组 bucket 的目标容量,不是把每个 parameter tensor 任意切片的硬上限;单个大参数通常作为完整梯度进入一个 bucket。
  • 本实验 vocab 32,000、hidden 768,embedding/output 相关大矩阵形成约 93.75 MiB 参数,因此 cap 1/4/25 MiB 时最大 bucket 仍接近 94-95 MiB。
  • 不应从命令行 cap 推断 collective 数和 bytes;应读 _get_ddp_logging_data()bucket_sizes、重建后的 bucket 和 parameter ready order。
  • reducer 首轮可能使用初始布局,观察 steady state 要先 warmup,确认 has_rebuilt_buckets 和实际迭代阶段。

高压追问:如果大 embedding 破坏 overlap,能否手工拆 parameter?这会影响权重绑定、optimizer state、checkpoint 命名和 kernel,不应只为 bucket 数粗暴改模型;可先考虑分片 embedding、通信 hook 或参数注册/执行顺序。

实验锚点:cap 1/4/25/100 MiB 分别得到 50/37/13/4 个 bucket,最大 bucket 仍为 93.75/95.26/95.26/108.05 MiB。

Q92. 100 MiB 配置只有 4 个 NCCL kernel,为什么比 4 MiB 的 37 个更慢?

高分回答要点

  • collective 数减少只降低固定 launch/协议开销;更大的 bucket 要等更多梯度 ready,首个 AllReduce 更晚,compute/communication overlap 可能下降。
  • 本实验 100 MiB 的 NCCL union 约 104.02 ms,确实略短于 4 MiB 的 106.84 ms;但 overlap 从 20.44% 降到 15.05%,exposed NCCL 仍约 88.36 ms。
  • 端到端吞吐应由 max-rank step time 判定:100 MiB 约 30.1K tok/s,4 MiB 约 32.8K tok/s。
  • 25 MiB 结果方差较大,因此结论应是“数量不单调决定性能”,而不是宣布 4 MiB 永远最优。

追问:如何进一步验证首发时间?在 trace 中定位第一个 NCCL kernel 相对 backward 起点,并按 bucket 关联 gradient-ready event;更完整时用 Nsight Systems 做多 rank 对齐。

Q93. DDP 梯度累积中,no_sync() 应该包哪里?为什么?

高分回答要点

  • 对前 A-1 个 microstep,with model.no_sync(): 应覆盖 forward 和 backward;最后一个 microstep 正常 forward/backward 触发一次 reducer 同步。
  • DDP 文档和实现要求 forward 也在 context 内,因为 forward 会准备 reducer/unused-parameter 等迭代状态;只包 backward 容易与预期不同。
  • 每个 microstep 的 loss 要除以 accumulation,或在 optimizer step 前等价缩放 gradient;否则 effective learning rate 被放大 A 倍。
  • 实验输入应使用 accumulated batch 的不同 slice。重复同一 microbatch 虽能测性能,却不能作为等 effective batch 的数值语义对照。
  • 只有最后一次同步时,13 个 bucket 产生 13 个 NCCL kernel;每次都同步则是 8 x 13 = 104

实验锚点:m1a8 no-sync 为 86.4K tok/s,sync-every 为 33.3K tok/s,前者快 2.59x。

Q94. global batch 都是 64,为什么 m8a1 比 m1a8+no_sync() 快 2.52x?

高分回答要点

  • global/effective batch 只约束一次 optimizer update 消费的样本数,不约束执行图成本。
  • m8a1 只执行一次 forward/backward;m1a8 执行 8 次,重复支付 attention、LayerNorm、launch、autocast、Python 和框架调度开销。
  • m8 的 GEMM M 维更大,更容易提高 Tensor Core/SM 利用率;m1 的小矩阵效率低。
  • 两者都只有 13 个 NCCL kernel,但 m8 trace 的 NCCL overlap 为 43.76%,no-sync 约 22.69%,说明计算形状也改变 overlap。
  • 数值上也不保证逐 bit 相同,因为 GEMM/梯度累加顺序不同;应比较梯度、参数误差和训练质量。

失分点:只回答“m8 并行度更高”,却说不出 forward 次数、GEMM shape、通信次数和数值顺序。

Q95. 你是怎样从 Chrome trace 计算 exposed communication 的?

高分回答要点

  • 解析 CUDA kernel event,按时间戳形成 [start, end) 区间;分别识别 NCCL kernel 和所有非 NCCL GPU kernel。后者只是本解析器的 compute 分类,不等于纯 GEMM/FLOPs。
  • 先对每一类区间做 union,防止嵌套/并发事件重复计时;再求两个 union 的 intersection。
  • overlap fraction = intersection / NCCL unionexposed NCCL = NCCL union - intersection。不能把 key-averages 的 duration 直接相加。
  • rank 0 单步 trace 只近似本 rank 的事件暴露量,不是全 job critical path;绝对性能仍用无 profiler 的多次稳态 wall-time。
  • 更严谨分析还需处理多 stream、CPU launch、step 边界、跨 rank 时钟对齐和 profiler overhead。

上机追问:给一组重叠区间,要求现场写 union/intersection,并为相邻、包含、空列表和跨 step 事件写单测。

Q96. 为什么 NCCL 的最快算法/协议会随消息大小变化?

高分回答要点

  • 小消息主要受 latency、同步轮次和协议开销控制;Tree 深度较低,LL 针对低延迟。
  • 大消息更看稳态带宽、chunk/pipeline 和拓扑映射;Simple 常有更高 payload efficiency,Ring/Tree 的具体胜负由路径和 channel 决定。
  • LL128 处于延迟和带宽之间,但支持条件受硬件/拓扑影响,不能假设所有机器都可强制使用。
  • 本机 crossover 是校准数据,不是普遍阈值:1-16 KiB Tree+LL,64 KiB Ring+LL,256 KiB Ring+LL128,1-16 MiB Ring+Simple,64-256 MiB Tree+Simple 略胜。
  • 生产通常让 NCCL 自动选择;只有发现稳定回归时才用强制矩阵做诊断,并保留 correctness check。

Q97. 为什么把 NCCL channel 从 2 增加到 8 反而变慢?

高分回答要点

  • channel 增加并行 chunk/路径机会,也增加 GPU blocks、SM 占用、launch/调度和共享链路竞争;收益不是单调的。
  • 256 MiB Ring+Simple 本机 1/2/4/8 channel 为 6.92/6.98/6.15/6.40 GB/s,2 最快。
  • 训练中 NCCL 与 GEMM 争 SM 时,孤立 collective 的最优 channel 也未必是端到端最优;需要同时测 exposed time 和 step time。
  • 当前 NCCL 文档把强制调优变量定位为实验/调试手段,并在较新版本用 CTA 变量替代旧 channel 变量;版本兼容要从本机日志确认。

追问:如何防止一个环境变量污染全部作业?把它限定在 runner 进程环境,结果中持久化 env,生产通过 canary 和版本化配置发布,不写进全局 shell profile。

Q98. 四组跨 socket pair 并发为什么总带宽约减半,而四组 PIX pair 没有?

高分回答要点

  • PIX pair 分布在各自较近的 PCIe 路径,四对可近似使用独立局部资源;单独与并发 aggregate 均约 47.15 GB/s。
  • SYS pair 跨 CPU/NUMA/host bridge,共享上行链路、内存/SHM transport 或 socket interconnect;并发 aggregate 从 27.45 降到 13.21 GB/s。
  • nvidia-smi topo -m 只给静态关系,不能表达多个 flow 同时争用;必须做 alone-vs-concurrent 对照。
  • NCCL_DEBUG 中 PIX 使用 P2P/direct,跨域路径可看到 SHM/direct 等 transport 选择,日志和性能现象相互印证。
  • 这仍不能证明多机 NIC/交换机行为;RDMA 需要单独测 rail、GDR、拥塞和路由。

系统设计追问:TP、DP、EP group 同时存在时,placement 目标函数应包含哪些并发边,而不只是最快 pair?

Q99. 一个 NCCL 性能回归,你会保存和比较哪些现场?

高分回答要点

  • 硬件/拓扑:GPU/NIC/CPU NUMA、PCIe link width/speed、P2P capability、affinity 和进程 placement。
  • 软件:driver、CUDA、NCCL、framework、container/插件版本和全部 NCCL_* 环境变量。
  • workload:collective、dtype、count/bytes、rank/world size、in-place、warmup/iterations、并发 group。
  • runtime 日志:algorithm/protocol/channel/CTA、ring/tree、transport、chunk size、GDR/P2P/SHM 决策。
  • 指标:algbw/busbw、p50/p95、correctness、GPU clocks/power、端到端 exposed time;与已知 good run 做同条件 A/B。

失分点:第一步就强制 NCCL_ALGO=Ring,没有先保存自动选择和版本现场。

Q100. variable-split all_to_all_single 的 split size 如何得到,combine 时为什么要反过来?

高分回答要点

  • router/发送端先得到发往每个 destination 的 send_splits,其和等于本 rank routed token 数。
  • 各 rank 先用一次小型 All-to-All 交换 count,得到“每个 source 将发给我多少”的 recv_splits;其和决定接收 buffer 大小。
  • dispatch 调用的 input splits 是 send_splits,output splits 是 recv_splits
  • combine 要把 expert output 发回原 source,因此本地 input splits 变成 recv_splits,output splits 变成原 send_splits
  • split、token permutation 和 inverse permutation 任一错位都可能 shape 合法但语义错误,所以实验用全局唯一 token id 做回环 oracle。

编码追问:某 rank 的某个 split 为 0 时是否正确?如何验证所有 rank 的 send sum 与对应 recv sum 全局守恒?

Q101. 为什么非热点 rank 的 combine 也可能很慢?

高分回答要点

  • collective 完成取决于所有 peer 进入并推进操作。非热点 rank 先完成少量 expert compute 后到达 combine,会等待仍在计算 24,576 tokens 的热点 rank。
  • profiler 将等待呈现在 collective/stream 上,不代表网络传 1,176 tokens 本身需要那么久。
  • 要做跨 rank timeline,比较 dispatch 结束、expert compute 结束和 combine launch/finish;collective 前的 arrival skew 是关键。
  • 可以插 barrier 做诊断以分离 arrival skew 和纯 collective,但 barrier 会改变真实关键路径,不能作为最终优化。
  • 优化可能在 router balance、capacity、expert replication/EPLB、token drop/reroute 或 compute/communication overlap,而不只是换 NCCL 参数。

实验锚点:hot case max compute 5.40 ms,max combine 9.70 ms;balanced 分别 1.26/1.79 ms。

Q102. 评估 MoE 负载均衡,为什么 max/mean 一个指标不够?

高分回答要点

  • 还要看每 expert token 的 p95/p99、方差、Gini/熵、跨 step 时间相关性和 source-destination 流量矩阵。
  • 相同 token 数下,不同序列/shape、padding 和 grouped GEMM packing 可能有不同 compute efficiency。
  • 系统指标包括 dispatch/combine bytes/time、arrival skew、expert compute、drop/reroute、capacity utilization、峰值显存和 step tail。
  • 模型指标包括 router auxiliary loss、z-loss、token drop 对主 loss/质量的影响;系统最均匀不一定是模型最优。
  • 应按 layer 观测,因为少数层热点即可决定 step critical path,模型级平均会掩盖它。

Q103. 如何把本项目的 MoE toy 扩展为更接近生产的实验?

高分回答要点

  • 实现 top-k router、stable token permutation/inverse permutation、capacity factor、drop/reroute 和 aux/z-loss。
  • 将多个 expert 放在每 rank,使用 grouped GEMM;比较 padding、group size 和 expert token shape。
  • 引入 TP/EP/DP process group,明确 expert 权重如何分片/复制以及 All-to-All 穿过哪些拓扑。
  • 尝试 dispatch/combine 与 dense/attention 或 expert compute overlap,使用跨 rank timeline 验证 critical path。
  • 加入 skew trace、动态 expert placement/replication、checkpoint/reshard 和数值对照。

边界表达:当前可证明“variable-split All-to-All 与热点等待”,不能证明真实 Megatron EP 的吞吐或收敛。

Q104. 为什么 checkpoint 的 manifest 必须最后发布?

高分回答要点

  • 多 rank 文件不是同时完成;如果目录存在就视为有效,恢复端可能读取半写版本。
  • 每个数据文件先写临时名并原子 rename;所有预期 shard 存在、长度和 checksum 确认后,最后原子发布 manifest 作为 commit point。
  • 恢复只枚举已提交 manifest,不根据“最新目录名”猜测;partial checkpoint 没有 manifest,因此不可见。
  • manifest 应包含格式版本、step、world/mesh metadata、tensor/shard identity、bytes/checksum;生产还要考虑对象存储没有 POSIX rename 的语义。
  • GC 必须先判断没有 reader/引用,再删未提交临时文件和过期版本,避免与慢 writer 竞态。

实验锚点:rank 3 跳过 rank-local 文件时,五组 partial case 均不发布 manifest。

Q105. 为了“可继续训练”,checkpoint 到底要保存哪些状态?

高分回答要点

  • model parameters/buffers、optimizer state、LR scheduler、GradScaler、global step/microstep 和精度/版本 metadata。
  • 每 rank CPU RNG、CUDA RNG,以及 dropout、采样或并行 RNG tracker;不能只保存一个全局 seed。
  • data sampler/generator、shuffle epoch、packed dataset cursor、已消费 token/sample 数,保证不重不漏。
  • 并行/shard metadata、tensor identity 和 checkpoint schema;跨 mesh 恢复需要 planner 重新分片。
  • 生产还可能需要 EMA、MoE router/expert state、FP8 scaling history、compile/runtime cache 的重建规则,而不是盲目序列化所有内部对象。

追问:DataLoader 有 prefetch 时,一个 cursor 是否足够?通常不够,需要定义 snapshot 边界,处理已取未消费 batch 和 worker RNG。

Q106. model、optimizer、RNG 和 data cursor 都精确加载,为什么恢复后 step 4 仍不同?

高分回答要点

  • “持久化状态相同”不保证 runtime 执行状态和浮点操作顺序相同;浮点加法不满足结合律。
  • 本实验首先验证保存点 model/optimizer SHA 精确,再用每步 gradient hash 定位第一个差异就在 step 4,而不是几步后 optimizer 才漂移。
  • fused 改 single-tensor、启用 deterministic algorithms 仍有约 3.725e-9 最大参数差,排除了两个常见单一原因。
  • DDP 连续 run 的 reducer 已按 gradient-ready order 重建 bucket;新进程直接 load 时 reducer 生命周期不同,归约分组/顺序可造成 ULP 级变化。
  • 生产验收应区分 bitwise、数值误差有界、loss continuity 和最终质量等层级。

失分点:直接说“GPU 本来就随机”,没有逐层排除 RNG、输入、optimizer 和 reducer。

Q107. reducer warm control 为什么是强证据,又为什么不能过度外推?

高分回答要点

  • 对照只在 resume 前增加 3 次 dummy backward,让 reducer 达到与连续 run 相同的 rebuilt bucket/parameter order;随后再加载 checkpoint,避免 dummy 更新污染模型和 optimizer。
  • 该组 gradient/model SHA、loss sequence 和最终参数全部 exact,最大差为 0;其他控制仍在 step 4 首次分叉。
  • 这比“机制上可能是 reducer”更强,因为它操纵了怀疑变量并消除了结果差异。
  • 仍不能推导所有 DDP 版本/模型的恢复差异都来自 reducer,也不能要求生产序列化私有 reducer 对象。
  • 可迁移结论是:位级回放测试必须控制 framework runtime lifecycle,并用 first-divergence fingerprint 定位。

Q108. rank exit 和 straggler timeout 的检测路径有何不同?

高分回答要点

  • rank os._exit(17) 会被 elastic launcher 通过子进程退出状态发现,最终 ChildFailedError 指出 rank 和 exit code。
  • straggler 仍存活但不进入 collective,launcher 看不到进程退出;其他 rank 的 ProcessGroupNCCL watchdog 在超时后报告 collective sequence、op 和 numel。
  • 本实验 timeout 6 s,日志定位 SeqNum=3ALLREDUCE、1,048,576 elements;外层 torchrun 总退出约 15.56 s,还包含传播和清理时间。
  • TORCH_NCCL_ASYNC_ERROR_HANDLING 等设置会影响错误传播/abort 行为,必须与框架版本一起记录。
  • runner 最后用 pgrep 检查无子进程残留;生产还要清理 rendezvous、共享内存、临时 checkpoint 和 scheduler allocation。

追问:为什么 timeout 不能设得极短?正常大 collective、编译、checkpoint 或系统抖动会造成误杀;要结合 heartbeat、progress 和分层超时。

Q109. 这个 Triton RMSNorm kernel 是怎样映射到 GPU 的?

高分回答要点

  • 一个 Triton program 处理一行,grid 大小等于 rows;hidden 维用 next_power_of_2 block 和 mask 加载。
  • FP16 input 转 FP32,做平方和 reduction、mean、rsqrt,再乘 FP16 weight 并写 FP16 output。
  • 融合避免显式 composite 的中间 tensor 和多次 HBM round trip;每行独立,无跨 program 同步。
  • hidden 很大时 power-of-two block、register/shared memory 和 occupancy 可能成为限制,不能无限沿用单行单 program。
  • wrapper 中输出分配也会影响端到端 API 时间;benchmark 要说明计时是否包含 allocation。

正确性追问:为什么 reference 用 FP32?避免只与另一个 FP16 实现互相验证;同时报告 max abs/relative error 和极端输入。

Q110. RMSNorm 的“805 GB/s 有效带宽”是怎么计算的,为什么不能当 HBM 实测?

高分回答要点

  • 本实验最小字节模型为 2 * rows * hidden * element_size + hidden * weight_element_size,即读一次 input、写一次 output、读一次 weight。
  • 用该 bytes 除以 kernel time 得约 805 GB/s,是算法字节口径,方便 shape 间比较。
  • cache line、transaction、weight 是否每个 program 重读、ECC、写分配和内部临时流量都未计入;因此不是 DRAM bytes counter。
  • 要声称 HBM 利用率,应使用 Nsight Compute 的 DRAM throughput/bytes,并核对 L2 hit、load efficiency 和时钟。
  • 不能把“有效带宽高于某个直觉值”立即判为错误,先确认分母是否是最小 bytes 且 weight 命中 cache。

Q111. 为什么多个小 shape 的 Triton 时间都卡在约 0.05 ms?如何验证?

高分回答要点

  • 工作量不足时,launch、Python/driver dispatch、事件计时精度和固定调度开销主导,计算/内存 bytes 增长尚未反映到 wall-time。
  • 本实验 rows=1,024 时 hidden 768/4096/8192 均约 0.05 ms,而 rows=8,192、hidden 4096/8192 才随 bytes 增到 0.169/0.333 ms。
  • 用 CUDA Graph 或批量重复 kernel 减少 launch 影响;用 profiler 看 kernel 本体和 CPU gap;扩大 rows/hidden 检查线性区间。
  • benchmark 要 warmup JIT,并把首次 compile 与 steady state 分离;使用 CUDA event/同步,不能只用未同步的 CPU timer。
  • 小 shape 的 speedup 可能主要来自减少 launch 数,而不是单 kernel 带宽提高,两种价值都可以但要分开表述。

Q112. 把这个 RMSNorm 变成可生产使用的 kernel,还缺什么?

高分回答要点

  • backward:input/weight gradients、FP32 accumulation、数值误差和额外 reduction;验证 autograd/gradcheck 与基线训练。
  • shape/dtype:BF16、FP32、非连续 stride、不同 hidden、batch/sequence layout、极端值和 epsilon。
  • performance:num warps/stages autotune、two-pass 大 hidden、vectorized/coalesced load、register spill、occupancy 和 graph capture。
  • integration:dispatcher/autograd wrapper、fallback、compile cache、版本/SM capability gate、stream 和错误处理。
  • regression:跨 GPU/driver/Triton 版本的 correctness/perf CI,端到端模型收益,而不只微基准。

边界表达:当前证明的是 V100/SM70 上 forward shape sweep,不是完整 fused training kernel。

Q113. 如何构造能证明 prefix cache 驱逐的 workload?

高分回答要点

  • 先用固定 anchor prefixes 跑 cold 和 warm,建立未命中/命中基线;随后用超过 cache 工作集的互异长 prefix 做 pollution。
  • 再访问完全相同的 anchor 做 revisit,最后立即重跑一次 rewarm;revisit 退化、rewarm 恢复可区分驱逐与永久服务回归。
  • 设置默认和受限 cache 容量对照,保持模型、prompt、并发、seed 和请求顺序不变。
  • 每阶段抓 cache query/hit token counter 差分,并看 TTFT/throughput;仅看客户端延迟不能唯一证明 cache state。
  • 本实验默认 225,200 tokens 能保留 anchor;1 GiB/18,720 tokens 在 48 个独立 1,024-token prefix 污染后,revisit mean/p95 TTFT 为 warm 的 2.01x/5.87x。

追问:为什么 cold 阶段也有 88.89% hit?同一阶段 48 请求只使用 4 个 prefix,前面的请求填 cache 后,后续请求发生批内复用;所以必须结合阶段顺序理解 counter。

Q114. 为什么进程生命周期累计 prefix hit rate 会掩盖 SLO 回归?

高分回答要点

  • counter 混合了 cold、warm、无共享 pollution、revisit、rewarm,业务关键窗口在总量中占比有限。
  • 本实验最终累计 hit rate 76.9% 与 74.9% 只差 2 点,但小 cache 的 revisit p95 TTFT 退化 5.87x。
  • 应按相邻 Prometheus snapshot 做 delta,并按模型、租户、route、prefix class 和时间窗口聚合。
  • 同时看 cache capacity/used blocks、eviction/recompute、TTFT tail、prefill tokens/s、SLO goodput 和每副本 queue depth。
  • cache-aware routing 会提高命中但可能制造副本热点,所以命中率必须和负载偏斜、排队及拒绝率联动。

服务工程追问:runner 如何避免残留 worker?独立 process group 启动,health check 后运行,退出对整个 group 先 TERM、超时 KILL、wait 回收,并最终检查 GPU/进程。

Q115. 让你现场证明这些数字不是手工写进报告的,你会怎样从原始产物重建结论?

高分回答要点

  • 先给实验命令和版本环境,指出 raw log/JSON/trace/metrics 的一一对应关系,不从最终 Markdown 反向抄数。
  • 运行 parse_engineering_deep_dive.py,说明每个 parser 的字段来源、单位转换、分组键、重复均值/方差和 derived ratio。
  • 抽查 correctness:NCCL wrong=0、MoE token-id roundtrip、checkpoint manifest SHA、Triton FP32 oracle、vLLM failed=0
  • 对一个结论手工复算,例如 13.21/27.45 的跨域 aggregate、321.58/159.81 的 revisit TTFT,或 trace interval union/intersection。
  • 运行 validate_results.py 检查期望文件数、JSON、有限值、失败状态和报告存在性;再检查没有残留进程/GPU 占用。
  • 最后主动说明短 run、单 rank trace、toy/dummy model 和单机 PCIe 边界。可复现不等于可无限外推。

4 分答案特征:能从任意一条 claim 一路追溯到命令、原始事件、parser 公式和 correctness,而不是只展示一张图。



上一篇:系统设计 · 系列总索引 · 下一篇:运行时与底层契约

This post is licensed under CC BY 4.0 by the author.

AI Infra 面试题(八):训练与推理系统设计

AI Infra 面试题(十):框架运行时与底层契约