这份试卷流传得挺广。它的定位很明确:不考概念背诵,而是把你直接扔进万卡集群的线上事故现场。题目质量整体在线——很多题干描述的现象(GPU 满载 100% 却无输出、单卡 OOM 引发连锁雪崩、Xid 静默故障)确实是生产里真实会遇到的东西。

但做下来发现一个问题:有 12 处题干前提站不住。有些是命名错误(DualPipe-V2 不存在),有些是职责错配(把节点健康探测的活儿算到调度器头上),还有些是版本演进导致的自然过时——最典型的是题目按 DeepSeek-V2/V3 的 MLA 配置(潜变量 512 + 解耦 RoPE 64)给 DeepSeek-V4 出题,可 V4 已经不用 MLA 了。

所以本文分两部分:先逐题给答案与解析,再把 12 处勘误集中列出。答案都尽量追到一手来源(NVIDIA 官方文档、PyTorch / vLLM 源码、Linux 内核定义、模型官方 config.json),而不是靠印象作答。


一、题目勘误汇总(先看这个)

先集中列出题干本身的问题,避免读者照着错前提记结论。

# 位置 问题 正确表述
1 判断 2 称 NCCL_DEBUG=INFO 产生「标准错误输出开销」 NCCL 调试日志默认写 stdout(ncclDebugFile = stdout),要改道需设 NCCL_DEBUG_FILE。且逐算子日志属 COLL 子系统,而 NCCL_DEBUG_SUBSYS 默认只有 INIT,BOOTSTRAP,ENV——只设 NCCL_DEBUG=INFO 看不到集合通信级日志,需补 NCCL_DEBUG_SUBSYS=COLL
2 判断 8 「接受率过低」被当成绝对阈值;「反复校验失败重算」表述不准 严格条件是 α ≤ c(阈值与草稿模型的相对开销绑定,草稿越便宜阈值越低);每轮验证只有一次目标前向,不存在「反复重算」,真正代价是白付的草稿前向与几何衰减的接受 token 数
3 单选 3 按 V2/V3 的 MLA 配置(512+64)给 DeepSeek-V4 出题 DeepSeek-V4 已弃用 MLA,改为混合注意力(滑动窗口 + CSA 压缩率 4 + HCA 压缩率 128),骨干是共享 K=V 的 MQA(num_key_value_heads=1),官方 config.json 中 kv_lora_rank 字段已不存在
4 单选 7 选项 B 的同步时机写反 应为「拷贝后同步」而非「拷贝前」——异步拷贝提交后,需在 CPU 读取前完成同步
5 单选 8 称昇腾有「高精度累加模式」环境变量 昇腾不存在该名称的开关。真实可核查的开关是 matmul/conv 的 allow_hf32 默认值、ATC --allow_hf32、ENABLE_HF32_EXECUTION、cube_math_type、--precision_mode、HCCL_DETERMINISTIC
6 多选 1 A 项「全集群节点两两配对」 O(N²) 不可行;标准做法是在作业通信域内跑 all_reduce_perf,比对各 rank 的离群值
7 多选 5 D 项把探测/污点/驱逐算作调度器能力 职责错配:NVSentinel(fault-quarantine 打污点、node-drainer 驱逐)与作业控制器负责,调度器只消费污点
8 多选 10 DualPipe-V2 命名错误 DualPipe 出自 DeepSeek-V3 技术报告(arXiv 2412.19437),无官方 DualPipe-V2;相近的 DualPipeV 是 Sea AI Lab 的 V 形调度。机制本身成立——DeepSeek-V4 技术报告明写仍用调整后的 DualPipe 1F1B
9 简答 6 NCCL_NET_GDR_LEVEL=5 不是文档化取值 文档化取值只有 5 个字符串:LOC / PIX / PXB / PHB / SYS(没有 DIS)。旧式整数 0/1/2/3/4 依次对应这 5 档,>4 一律按 SYS 解释(源码 levelsOldToNew[] 中 5 亦映射为 SYS)。整数形式仅为向后兼容,官方明确不推荐(“the literal values can change over time”)——生产直接写助记符
10 简答 7 同 #3 同 #3,应基于 V2/V3 的 MLA 作答并明确注明版本
11 简答 9 称 vLLM 用「重算 + 换出」两种抢占策略 vLLM V1 引擎已移除 swap,只剩 recomputation 一条路径;V0 时代才是两者并存
12 简答 16 变量状态未更新 NCCL_ASYNC_ERROR_HANDLING 从来不是 libnccl 的变量,而是 PyTorch ProcessGroupNCCL 读的。PyTorch 2.1 及更早用旧名、默认 1(TearDown);2.2 起改名 TORCH_NCCL_ASYNC_ERROR_HANDLING、默认改 3(SkipCleanUp)。旧名在 2.2+ 源码中已不再被读取——写了也静默无效

#3 和 #10 是同一件事的两处体现,说明出题时参考的是 V3 时代的架构资料。这不是吹毛求疵——MLA 的 KV Cache 计算方式和混合注意力的完全不同,照错口径背公式,到了真实部署会算错显存。



二、判断题(10 题 · 10 分)

1. 推理循环中每生成一个 Token 就手动调用一次 torch.cuda.empty_cache(),是官方推荐的最佳显存优化手段,能彻底规避 CUDA OOM

答案:×

  • empty_cache() 释放的是 caching allocator 中未被占用的缓存块(还给驱动,能在 nvidia-smi 里看到下降),不会释放仍被张量持有的显存;官方原话是 “empty_cache() doesn’t increase the amount of GPU memory available for PyTorch”,其用途只有两个:把显存让给别的进程、以及在特定情况下缓解碎片。
  • 推理 OOM 的根因是活跃张量(权重 + KV Cache + 激活峰值)超过显存,逐 token 清缓存完全不改变这个峰值,因此"彻底规避 OOM"从原理上就不成立。
  • 反向代价明确:清空缓存后下一次分配要重新走 cudaMalloc(昂贵且同步),而 caching allocator 的设计目标正是 “fast memory deallocation without device synchronizations”;逐 token 清缓存等于把 allocator 的收益全部丢掉,单步延迟与 GPU 空转都会恶化。
  • 更隐蔽的风险:在 CUDA Graph 捕获/回放、多流并发的路径上频繁回收显存,会破坏图捕获期间"地址稳定"的假设,触发重捕获或直接报错。
  • 正确做法:按 max_num_seqs / max_model_len / 并发预算显存,用 expandable_segments:True 缓解碎片,用 memory_allocated()(真实张量占用)与 memory_reserved()(allocator 持有的总量)区分两者,只在确实要让位给其他进程或做 profiling 前清一次。
  • 真正有用的场景:同一张卡上多个进程共享、需要在进程间让出显存;或输入长度高度可变导致"缓存块尺寸碎片化"形成高峰时,在请求间隙偶发清一次;这与"每个 token 清一次"是完全不同的用法。

依据:PyTorch 官方 torch.cuda.empty_cache / torch.cuda.memory.empty_cache API 文档;PyTorch “CUDA semantics - Memory management”(caching allocator 设计目标、memory_allocated/reserved 语义)。

2. 线上集群无故通信挂起时,临时开 NCCL_DEBUG=INFO 会引入额外日志输出开销,但它是快速定位卡在哪个 Rank / 哪个通信算子的核心手段

答案:√

  • 机制上 NCCL 日志按"级别 × 子系统"双维度过滤:INFO 级别会打开初始化、bootstrap、网络选路、拓扑探测等大量输出,多 Rank 同时打到终端或文件,格式化与 I/O 开销真实存在;官方也建议生产默认不开、需要时用 NCCL_DEBUG_FILE 落盘并用 NCCL_DEBUG_SUBSYS 收窄。
  • Hang 定位原理:每个 Rank 在下发每个集合通信前会打印 "%s: opCount %lx sendbuff %p recvbuff %p count %zu datatype %d op %d root %d comm %p [nranks=%d] stream %p";把所有 Rank 的日志按时间对齐,最后一个成功打印的算子即卡住点(例如 7 个 Rank 停在 AllReduce、1 个停在 AllGather,就是典型的算子/调用顺序不匹配)。
  • 初始化阶段的 hang 另有专门组合:NCCL_DEBUG=INFO NCCL_DEBUG_SUBSYS=INIT,BOOTSTRAP,NET,官方文档给的就是这个用法,重点看 bootstrap 连接、网卡选择、Rank 同步三类问题。
  • 生产更优解是 PyTorch Flight Recorder:TORCH_NCCL_TRACE_BUFFER_SIZE=2000 + TORCH_NCCL_DUMP_ON_TIMEOUT=1,用内存环形缓冲记录每 Rank 的 last_enqueued / started / completed_collective 与 C++ 栈,超时后自动 dump、再用 fr_trace.py 分析,避免海量日志本身成为二次故障。

⚠️ 题目本身有问题:两处不严谨。(1) NCCL 调试日志默认写 stdout(源码 ncclDebugFile = stdout,只有设 NCCL_DEBUG_FILE=/dev/stderr 或文件才改道),说"标准错误输出开销"不准确;(2) 逐算子那一行属于 COLL 子系统,而 NCCL_DEBUG_SUBSYS 的默认值只有 INIT,BOOTSTRAP,ENV,因此只设 NCCL_DEBUG=INFO 看不到集合通信级日志,需补 NCCL_DEBUG_SUBSYS=COLL(或 ALL)。更准确的表述是 NCCL_DEBUG=INFO NCCL_DEBUG_SUBSYS=INIT,COLL,NET。

依据:NCCL User Guide “Logging” 与 env 页;NCCL 源码 src/enqueue/enqueue.cc(INFO(NCCL_COLL, "%s: opCount ..."))、src/param/param.cc(NCCL_DEBUG_SUBSYS 默认 INIT|BOOTSTRAP|ENV)、src/debug.cc(默认输出流为 stdout);PyTorch Flight Recorder 官方教程。

3. PyTorch 原生 torch.compile + Inductor 后端可直接在昇腾 910B/C 上无缝编译执行,无需任何专用 CANN 编译器或 Torch-NPU 插件

答案:×

  • torch.compile 是"前端 + 后端"结构:Dynamo 抓取 FX 图后必须交给某个后端做代码生成。PyTorch 官方树内的 Inductor codegen 只有 Triton(GPU)、C++/OpenMP(CPU)以及 xpu / rocm / mps / mtia 等目标,没有昇腾的代码生成路径,所以"原生无缝"从第一性原理上不成立。
  • 昇腾上跑图模式必须装 CANN + torch_npu(Ascend Extension for PyTorch),再用 TorchAir 提供 NPU 后端:torchair.get_npu_backend() 把 FX 图转换为 GE 计算图,交由 CANN 的 GE 图引擎做算子融合、编译优化后下发到 910B/C 执行。
  • 官方示例即 import torch_npu 之后 import torchair,再 model = torch.compile(model, backend=npu_backend);且 PyTorch / torch_npu / CANN 三者版本需严格匹配,属于典型 out-of-tree 扩展。
  • 昇腾侧其实有两条图模式路线:TorchAir 的 GE 图后端(图模式整图下发)与 torch_npu 体系内的 Inductor/AscendC 后端(算子级编译),两者都依赖 CANN 工具链,都不是"原生 Inductor"。
  • 落地检查项:确认 torch_npu 与 CANN 版本匹配(不匹配时典型表现是 torch.compile 直接报后端不支持或图模式下算子 fallback 到 eager),并在图模式出现精度/编译报错时先用 TorchAir 的调试开关做图与 eager 的精度比对。
  • 结论:昇腾支持恰恰依赖插件与 CANN 编译器,题干把必要条件说成了"无需"。

依据:Ascend/torchair 官方 README(torchair.get_npu_backend、“将 PyTorch 的 FX 图转换为 GE 计算图”、版本匹配表);昇腾社区《PyTorch 框架使能图模式》(CANN 8.1.RC1);PyTorch 仓库 torch/_inductor/codegen/ 目录清单(triton.py / cpp.py / xpu / rocm / mps / mtia,无 Ascend)。

4. 异步 Checkpoint 时,主训练线程只需把 GPU 显存中的权重拷到 Host Pinned Memory 即可立即恢复计算,无需等待磁盘/网络落盘

答案:√

  • 核心机制是把"GPU→Host 的拷贝"与"Host→存储的落盘"解耦:主线程先把 state_dict 反暂存到 CPU 侧 staging 缓冲(PyTorch DCP 默认 use_pinned_memory=True、use_async_staging=True),落盘在独立线程或进程中完成,训练线程不再等磁盘/对象存储 I/O。
  • DCP 的 async_save 返回 AsyncSaveResponse,含 staging_completion(本地拷贝完成)与 upload_completion(真正写完)两个 future;也可选 async_checkpointer_type 决定用线程还是独立进程落盘,避免 GIL 影响。
  • 收益量级:同步保存时训练要空转数秒到数十秒(取决于 state 大小与存储带宽),异步把 stall 压缩到 D2H 拷贝时间,典型大模型训练里能把 checkpoint 抖动从"分钟级停顿"降到"亚秒级抖动"。
  • 代价与边界:需要额外一份 pinned host 内存(量级约等于 state 大小,优化器状态另计),D2H 拷贝本身要走 PCIe/NVLink,与训练通信抢带宽;staging 完成前不能修改原始 state_dict(synchronize_staging 语义),实现上通常双缓冲。
  • 工程上最容易踩的坑:落盘线程/进程失败必须能被感知并让训练报错,否则会静默产出坏 checkpoint;另外 pinned memory 过多会挤压宿主内存,多机训练时要按 state 大小规划。
  • 大集群常用"多级异步":先异步写到本地 NVMe,再由后台任务上传到远端对象存储,把跨机带宽争抢与训练错峰,本质上仍是"主线程只等 D2H 拷贝"这一条。

依据:PyTorch 官方 torch.distributed.checkpoint 文档(async_save / AsyncSaveResponse / StagingOptions / BlockingAsyncStager / synchronize_staging)。

5. Kubernetes 原生调度器默认具备 GPU 拓扑感知能力,申请 4 卡时会自动挑同一颗 PCIe Switch 下的卡,无需任何拓扑感知插件

答案:×

  • 默认路径上 kube-scheduler 只看到节点状态里的整数扩展资源 nvidia.com/gpu(官方文档:“Extended resources are only supported as integer resources and cannot be overcommitted”),既不认识 PCIe Switch 也不认识 NVLink 域,只做数量匹配与打分,物理上根本没有拓扑信息可用。
  • 决定"分哪几张卡"的是 kubelet 的 device manager + device plugin:默认按 ListAndWatch 上报顺序 first-fit 分配;device-plugin API 确实有可选的 GetPreferredAllocation 提示接口,但 kubelet 只在插件声明 GetPreferredAllocationAvailable 时才调用,且接口注释明确"结果不保证被最终采用"。
  • 即便装了 NVIDIA 插件,其当前实现是按副本分布策略(distributed / packed / spread)比较物理 GPU 的已分配次数来挑卡,并非按 PCIe Switch 拓扑挑卡——所以"同一 PCIe Switch 自动选卡"在默认栈里不存在。
  • 集群级/整机拓扑(NVLink 域、rail 优化、超节点)需要额外组件:NFD/Topograph 打标签 + Volcano / KAI Scheduler / Kueue TAS 等拓扑感知调度器,或走 DRA(含多节点 NVLink 的 ComputeDomain 方案),且通常还要配合 Gang 调度保证整组任务原子落地。
  • NUMA 对齐属于另一条链路:插件上报 TopologyInfo{Nodes:[NUMANode]},由 kubelet Topology Manager 在开启非 none 策略时使用,粒度是 NUMA 节点,与"同一 PCIe Switch 选卡"不是一回事。
  • 结构上还有一层限制:调度发生在节点级(kube-scheduler 先绑定节点),设备分配发生在节点内(kubelet + 插件),而插件看不到"这个 Pod 是 4 卡 TP 还是 4 卡 DP、要和哪台机做 AllReduce",所以单机插件再怎么优化也做不出集群级拓扑最优解。

依据:Kubernetes 官方 Device Plugins 文档(整数扩展资源、GetPreferredAllocation 为可选提示、TopologyInfo 与 Topology Manager 集成);NVIDIA k8s-device-plugin 源码 internal/plugin/server.go、internal/rm/{rm,allocate}.go;NVIDIA Topograph 文档(“the default Kubernetes scheduler places one pod at a time”)。

6. RoCEv2 中交换机开启 PFC Deadlock Watchdog 后,若某队列持续收到 PFC 暂停帧超时,交换机会主动强制丢弃该队列报文以打破死锁环路

答案:√

  • PFC 死锁的本质是多跳缓冲依赖成环:环上每个端口都在等下游"腾空间",暂停帧沿环传递后谁都不发数据,队列永久停在 XOFF;拥塞控制失效(DCQCN 参数不当、微突发)往往是形成环的前置条件,因此 PFC watchdog 与 ECN/DCQCN 调优是配套手段。
  • Watchdog 的做法是在端口上按周期(如 100 ms)轮询 no-drop 队列状态:队列持续处于暂停状态超过 shutdown-multiplier × interval 即判定卡死,把它转入 shut 状态并丢弃该队列的所有出向报文(“all outgoing packets are dropped on interfaces that match the PFC queue that is not being drained”),从而打断暂停帧传播链。
  • 随后进入 auto-restore 观察期:若在该窗口内不再收到该队列的 PFC 帧,则恢复队列、流量继续;若仍有 PFC 帧到达则计时器重置、继续保持丢弃,必要时可手工执行 recover 命令。
  • 本质是"用丢包换解锁":牺牲无损性,靠 RoCE 重传补数据,代价是瞬时丢包、重传风暴与吞吐下探;timer 必须按业务调优,过于激进会在正常拥塞时误杀流量。
  • 注意两点前提:watchdog 通常需要显式开启(不是所有平台默认打开),且动作可配置——有的平台默认丢队列,有的默认 err-disable 端口(整口下线),故障定位时要先确认动作类型。
  • 观测手段:交换机侧看 watchdog / shutdown / auto-restore 事件计数与被丢弃报文计数,主机侧看 RoCE 端口的 PFC pause 帧计数与重传计数;两侧同时跳变即可确认"死锁被打断后重传恢复"这一路径。

依据:Cisco NX-OS 9000 QoS 配置指南 “Priority Flow Control Watchdog” 节(队列 shut + 丢包 + auto-restore 原话);Cisco IOS XR PFC watchdog(interval / shutdown-multiplier / auto-restore-multiplier)。

7. vLLM 配 --gpu-memory-utilization 0.9 意味着权重加 KV Cache 永不超过 90% 显存,因此绝不可能 OOM 崩溃

答案:×

  • 该参数的官方定义是 “The fraction of GPU memory to be used for the model executor”,且明确是 per-instance 限制(同一张卡两个实例各设 0.5 也合法),它是"给本实例的预算比例",不是硬件级硬上限。
  • 实现方式是启动时跑一次 profiling,测出权重、激活峰值与 CUDA Graph 开销后,用剩余额度反推 KV Cache 的 block 数(num_gpu_blocks);这是基于采样的估算,可被 --num-gpu-blocks-override 覆盖,也存在估算偏差。
  • 仍有大量 OOM 路径:权重本身放不下(要加 TP/PP 或换低精度)、KV Cache 需求超预算(要降 max_model_len)、碎片导致没有连续大块(要开 expandable_segments)、CUDA Graph 捕获/预热阶段吃掉预留之外的余量、同卡其他进程或实例占用、输入分布比 profiling 时更极端(长序列/多模态/大 batch)。
  • 预算的边界也值得注意:预留的那 10% 恰恰是留给 CUDA Graph、sampler warmup 与碎片的,所以"利用率调到 0.9"本身并不保证捕获阶段不 OOM,这也是小显存卡上最容易翻车的地方。
  • 排查方法:先看 OOM 发生在哪个阶段(加载权重 / 切 KV Cache / 图捕获 / 运行期),再对症下药;运行期还可观测 KV Cache 使用率指标,确认是容量不足还是碎片问题。
  • 默认值本身也在演进(历史版本 0.9,新版本引入可扩展 KV Cache 后语义与默认值有调整),所以"0.9 就是一个硬保证"这种理解在任何版本上都不成立。

依据:vLLM 官方 Engine Arguments 文档(--gpu-memory-utilization / --device-memory-utilization、per-instance 语义、profiling 与 num_gpu_blocks);NVIDIA NIM “Troubleshooting GPU Memory Out-of-Memory Errors”(默认 0.9,剩余额度留给 CUDA Graph/warmup/碎片,按阶段给出不同修法)。

8. 推测解码中若草稿模型接受率过低,端到端延迟不仅不降,反而会比直接跑大模型更慢

答案:√

  • 论文给出的期望加速比为 (1-α^(γ+1)) / ((1-α)(γc+1)),其中 α 为接受率、c 为"草稿一步耗时 / 目标一步耗时"、γ 为草稿长度;推论是只有 α > c 时才存在某个 γ 带来提升,即 α ≤ c 时任何草稿长度都无法加速。
  • 物理直觉:验证阶段目标模型只跑一次 forward(处理 γ+1 个 token),换来的是"被接受的那一小段前缀"。若大部分草稿被拒,这一轮只产出 1 个 token——与普通解码相同——却额外付了 γ 次草稿前向的开销;α 越低收益按几何级数衰减、成本线性增长,于是端到端更慢。
  • 参数敏感度:γ 越大,期望接受 token 数 (1-α^(γ+1))/(1-α) 的边际收益越小而成本线性上升,所以低接受率时应先缩 γ,再考虑换草稿模型(EAGLE/Medusa 等更对齐的草稿器提高 α)。
  • 低接受率的常见来源:草稿与目标分布不匹配(tokenizer 不一致、领域差异)、采样温度偏高;此外高并发下目标模型已算力饱和,推测解码更容易变成负收益——它面向的是低/中 QPS、内存带宽受限的时延场景。
  • 生产判据:用接受 token 数 / 草稿 token 数(如 spec_decode_num_accepted_tokens_total / draft_tokens_total)实测 α,再按上面的公式判断当前 γ 是否划算。
  • 实测经验与理论一致:单流低并发下常见 1.5x~2.5x 加速,但接受率随任务类型剧烈波动(代码/结构化输出高、开放文本低),并发升高后常出现整体吞吐反而下降的负收益。

⚠️ 题目本身有问题:"接受率过低"没有绝对阈值,严格条件是 α ≤ c(阈值与草稿模型的相对开销绑定,草稿越便宜阈值越低);另外"反复校验失败重算"表述不准——每轮验证只有一次目标前向,并不存在反复重算,真正的代价是白付的草稿前向与几何衰减的接受 token 数。

依据:Leviathan et al., arXiv:2211.17192 §3.3 与 Corollary 3.9(期望加速比公式、“If α>c, there exists γ for which we’ll get an improvement”);vLLM 官方推测解码文档(面向低/中 QPS、内存受限场景与可观测指标)。

9. 显存出现 Double-Bit Error(DBE)时,GPU 硬件会自动降频并跳过错误数据继续运算,不会导致运行中的 CUDA Kernel 崩溃

答案:×

  • ECC 的 SECDED 编码只能纠正 1 bit、检出 2 bit:DBE 发生时硬件明确知道"这份数据已损坏且无法纠正",不会静默跳过,而是立即中断上报(驱动侧即 Xid 48,语义为 GPU 上出现不可纠正错误)。
  • 后果是数据与上下文双失:受影响页内容不可信、CUDA 上下文被污染,正在执行的 kernel 会失败、进程必须重启;NVIDIA 的 Xid 目录对 Xid 48 明确要求 “A GPU reset or node reboot is needed to clear this error”,对包含型/非包含型错误(Xid 94 / 95)分别要求 restart app / reset GPU。
  • 硬件侧的"自愈"是事后动作:动态页退休(旧架构)或行重映射(Ampere 及以后,伴随 Xid 63/64)把坏行换掉,但内容已经丢失,救不回正在跑的任务;行重映射失败(Xid 64)只能复位或换卡。
  • 与可纠正错误(CE)的区别在于处置阈值:CE 通常按阈值累积后才触发页退休/行重映射,DBE 则是"一次即不可用",且往往直接触发节点下线与 RMA 流程。
  • 降频属于温度/功耗保护动作,与 ECC 错误没有因果关系。工程做法是订阅 Xid 的健康检查(DCGM / 节点健康探针)命中后立即把节点拉出调度池并复位或换机,训练侧靠 checkpoint 重启恢复。
  • 定位路径:内核日志里的 Xid 行 + DCGM 的 ECC/页退休/行重映射字段(含 DBE 与 CE 计数)是判断"该复位还是该换卡"的直接依据;训练框架侧看到的往往只是 NCCL 超时或 CUDA 报错,别把根因误判成通信问题。

依据:NVIDIA Xid Errors 文档 Xid Catalog(Xid 48 ROBUST_CHANNEL_GPU_ECC_DBE、“A GPU reset or node reboot is needed to clear this error”;Xid 63/64 页退休与行重映射;Xid 94 RESTART_APP / Xid 95 RESET_GPU)。

10. 超长分布式预训练中 FP16 比 BF16 更容易下溢或上溢(NaN),本质原因是 FP16 的指数位比 BF16 少 3 位

答案:√

  • 位宽布局是根因:FP16 为 1/5/10(5 位指数),bfloat16 为 1/8/7(8 位指数,与 FP32 同阶),指数位少 3 位意味着 FP16 的动态范围窄得多。
  • 数量级对比:FP16 正规数下限约 2^-14 ≈ 6.1e-5、上限 65504(次正规可到约 6e-8);BF16 约为 1.18e-38 ~ 3.39e38。同一个 loss / 激活 / 梯度值域在 FP16 下更容易下溢成 0(梯度被吃掉)或上溢成 inf。
  • 严格说上溢首先产生 inf,NaN 多由 inf−inf、0×inf 之类的二次运算派生;这不改变结论。FP16 训练必须配 loss scaling(静态或动态)把梯度抬进可表示区间,BF16 通常不需要,这也是长序列训练普遍选 BF16 的直接原因。
  • 反向的取舍是精度:FP16 尾数多 3 位(10 vs 7),同量级下相对误差更小,BF16 的舍入更粗——所以"BF16 更稳、FP16 更准"是这一对格式的常见表述。
  • 具体路径举例:反向传播中小梯度常落在 1e-6~1e-8 量级,FP16 下直接归零(梯度被吃掉),而 BF16 仍可表示;相反,激活/attention logits 超过 65504 时 FP16 直接变 inf,随后 softmax 出 NaN。
  • 工程上还有配套约束:无论用哪种 16 位格式,优化器主权重通常仍保留 FP32 master copy 以保证更新精度;到 FP8 时代 BF16 依然是主流的主计算格式。

依据:NVIDIA Transformer Engine 低精度训练文档(FP32 1/8/23、BF16 1/8/7、FP16 1/5/10;BF16 共享 FP32 指数范围,FP16 动态范围受限需 loss scaling);IEEE 754 binary16 定义。


三、单选题(10 题 · 20 分)

核实说明:本卷所有答案与解析均已联网核对官方文档 / 官方仓库源码 / 规范,来源在每题末尾标注。
标注 ⚠️ 题目本身有问题 的题目存在事实错误或表述歧义;标注「注」的为答案不受影响的细节修正。


1. 千卡训练卡死、GPU 利用率 100%,定位首个掉队的 Rank

题干:千卡分布式训练进程突然卡死不动,所有 GPU 利用率均维持在 100%(实际处于 CPU/GPU 忙轮询等待状态),排查定位具体是哪个 Rank 率先掉队卡死的最有效手段是:

  • A. 立即重启整机服务器并清空日志
  • B. 使用 py-spy 或 GDB 打印各节点的调用栈,排查哪台机器未停留在当前的 NCCL 集合通信函数上
  • C. 调小模型的全局 Batch Size
  • D. 查看交换机 CPU 利用率

答案:B

卡死的本质是某个 rank 没有进入集合通信:NCCL 的集合操作要求同一通信域内所有 rank 按同一顺序进入同一次 collective,只要有一个 rank 没进来,其余 rank 就会在 ncclKernel 里忙轮询空等——表现正是「GPU 利用率 100% 但毫无进展」。因此最有效的动作是在所有节点上同时抓调用栈,看谁的栈不在集合通信函数里,谁就是元凶(即二分法的一次采样)。A 直接销毁现场,最差解;C 改 batch size 与根因无关;D 交换机 CPU 利用率与 GPU 侧通信阻塞没有因果对应关系。
现代补充手段:PyTorch Flight Recorder(TORCH_NCCL_TRACE_BUFFER_SIZE / TORCH_NCCL_DUMP_ON_TIMEOUT / TORCH_NCCL_TRACE_CPP_STACK)会把每个 rank 的 collective 元数据写盘,官方示例的结论正是「某 rank 未加入某次 all_reduce」。
命令(示意):逐节点 py-spy dump --pid <rank_pid> > /tmp/rank_${RANK}.stack,再 diff 各栈;无 py-spy 时用 gdb -p <pid> -batch -ex 'py-bt'。抓完栈后再用 NCCL_DEBUG=INFO NCCL_DEBUG_SUBSYS=COLL 的 SeqNum / OpType 对齐各 rank 的最后一次 collective,确认是「某个 rank 完全没到」还是「到了但顺序不一致」——后者是代码 bug(不同 rank 调用了不同数量或不同顺序的 collective)。

依据:PyTorch《Flight Recorder》教程(“rank 1 did not join the ‘all_reduce’ collective”、TORCH_NCCL_TRACE_BUFFER_SIZE 默认 2000);Ray Train《Detect NCCL hangs》(py-spy 提供原生栈,判据为各 rank 的 collective 计数不一致);NVIDIA NCCL RAS 文档。


2. dmesg 中的 Xid 31 代表什么硬件问题

题干:在 Linux 系统内核日志(dmesg)中频繁发现 NVRM: Xid (PCI:0000:xx:00): 31, pid=xxxx, chid=xx 报错,该 Xid 31 错误通常代表硬件发生了什么问题?

  • A. GPU 温度过高触发保护性降频
  • B. GPU 显存单比特 ECC 错误被自动纠正
  • C. GPU 显存页面错误(GPU Memory Page Fault,通常由非法的显存越界访问引起)
  • D. 交换机端口光模块掉电

答案:C

NVIDIA 官方 Xid Catalog 中 Xid 31 的助记符是 ROBUST_CHANNEL_FIFO_ERROR_MMU_ERR_FLT,描述为 “GPU memory page fault”:GPU 的 MMU 上报了一次地址翻译失败,即某个 chip unit 访问了非法(未映射)的虚拟地址。官方给出的即时动作是 RESTART_APP,并明确说明它 “Typically these are application-level bugs, but can also be driver bugs or hardware bugs”,建议用 Compute Sanitizer / cuda-gdb 或 CUDA_DEVICE_WAITS_ON_EXCEPTION=1 抓现场。
A 温度触发的降频走 clock throttle 事件,不产生 Xid;B 可纠正的单比特 ECC 由 ECC 计数器与独立 Xid 类别上报,不可纠正的双比特 ECC 是 Xid 48,都不是 31;D 光模块掉电属于网络侧,与 GPU Xid 无关。
生产口径:反复出现在同一张卡→ 转硬件诊断(DCGM diag / FieldDiags)并摘卡;跨卡随机→ 优先怀疑应用侧越界访问。
日志形态(示意):NVRM: Xid (PCI:0000:xx:00): 31, pid=xxxx, chid=xx,其后常跟 MMU Fault: ENGINE ... faulted 与 Fault is of type FAULT_PDE ACCESS_TYPE_VIRT_READ——FAULT_PDE 表示页目录项级翻译失败,ACCESS_TYPE_VIRT_READ 表示触发的是读访问。看到这个格式就可以确定是地址翻译失败而非 ECC 或热问题。

依据:NVIDIA《XID Errors》Xid Catalog(Xid 31 = “GPU memory page fault”,Immediate Action = RESTART_APP;Xid 48 = “Double Bit ECC Error”)。


3. DeepSeek-V4 (Flash/Pro) 的 MLA 单 Token KV Cache 大小

题干:在当前主流的 DeepSeek-V4 (Flash/Pro) 架构中,延续并升级了 MLA(Multi-Head Latent Attention),将每 Token 的 KV 向量压缩为一个潜变量(维度 ▢)与解耦 RoPE 向量(维度 ▢)。在 BF16 精度(每个参数 2 字节)下,单层 Transformer 的单个 Token 占用的 KV Cache 物理显存理论大小仅为多少?
(原卷此处两个维度数字在排版中丢失,以 ▢ 表示)

  • A. 576 字节
  • B. 1,152 字节
  • C. 2,304 字节
  • D. 4,608 字节

答案:B(1,152 字节)

按题干自述的 MLA 口径计算(kv_lora_rank=512、qk_rope_head_dim=64):
每 token 每层需缓存的只有潜变量 c_KV(512 维)与解耦 RoPE key k_R(64 维),共 576 个元素;K/V 由 c_KV 上投影得到,不单独入缓存。
BF16 每元素 2 字节 → 576 × 2 = 1,152 字节。
干扰项口径:A = 576 是元素个数不是字节数;C = 2,304 是按 FP32(4 字节)算;D = 4,608 无对应口径。

⚠️ 题目本身有问题:DeepSeek-V4 真实存在(2026-04-24 预览并开源:V4-Pro 1.6T 总参 / 49B 激活,V4-Flash 284B / 13B 激活,1M 上下文为官方服务默认),但它并没有「延续并升级 MLA」。V4 把 V3 的 MLA 换成了混合注意力:滑动窗口注意力 + CSA(Compressed Sparse Attention,压缩率 4,带 Lightning Indexer 稀疏选择)+ HCA(Heavily Compressed Attention,压缩率 128),骨干是共享 K=V 的 MQA(num_key_value_heads=1);官方 config.json 里 kv_lora_rank 字段已不存在(head_dim=512、q_lora_rank=1024、qk_rope_head_dim=64 仍在,但 64 是 partial RoPE 的维度,不是 MLA 的解耦 RoPE key)。题干中的 512+64 实为 DeepSeek-V2/V3 的 MLA 配置(V3:kv_lora_rank=512、qk_nope_head_dim=128、qk_rope_head_dim=64、v_head_dim=128)。
V4 实际缓存的东西完全不同:滑动窗口分支的 K=V(单头,sliding_window=128)、CSA/HCA 压缩器按 4×/128× 合并后的聚合条目,以及 Lightning Indexer 的独立缓存(index_n_heads=64、index_head_dim=128、index_topk=512)——KV 占用由压缩率而非 kv_lora_rank 决定。
结论:按题干自述的 MLA 口径,答案仍取 B;但「V4 = 升级版 MLA」是事实错误,V4 的单 token KV 占用不能用 MLA 公式计算。

依据:Hugging Face Transformers v5.8.0 deepseek_v4 模型文档(“replaces DeepSeek-V3’s Multi-head Latent Attention (MLA) with a hybrid local + long-range design”);deepseek-ai/DeepSeek-V4-Flash 官方 config.json(无 kv_lora_rank);DeepSeek 官方新闻页《V4 Preview》。


4. 网卡计数器中最能指示物理链路隐性劣化的指标

题干:在大规模数据并行训练中,训练步耗时(Step Time)偶尔突发飙升 5 倍,但没有机器报错崩溃。排查某台机器的网卡计数器时,最可能指示硬件物理链路存在隐性劣化的指标是:

  • A. rx_bytes(接收字节数)
  • B. rx_crc_errors / fcs_errors(物理帧循环冗余校验错误导致反复重传)
  • C. tx_packets(发送数据包数)
  • D. rx_unicast(接收单播包数)

答案:B

rx_crc_errors 在内核 netdev 标准统计中的定义就是「收到的 CRC 校验失败的包数」,对应 IEEE 802.3 的 aFrameCheckSequenceErrors(部分驱动/ethtool -S 里叫 rx_fcs_errors)。这类错误是纯物理层(L1)问题:光模块收发功率劣化或老化、光纤弯折/脏污、铜缆受干扰、连接器氧化。它的行为特征恰好匹配题干「不崩溃、不报错,但 step time 偶发飙升」——帧被丢弃后触发重传/降速(RoCE 场景下还会引发 PFC 反压),于是只体现为性能抖动。
A/C/D 全是
流量类计数器
(字节数 / 包数 / 单播包数),只反映业务量,涨跌与链路质量无关。
实操:ethtool -S <iface> | egrep 'crc|fcs|symbol|align|pause'、ethtool -m <iface> 查光功率;RDMA 场景再看 perfquery / ibdiagnet 的 symbol errors。
判读要点:计数器必须看增量而非绝对值——开机以来累积的少量 CRC 错误无害,要盯的是「按分钟计的持续增长」。与之容易混淆的两类指标:rx_missed_errors / rx_no_buffer_count 是接收环无空闲描述符导致的丢包(软件侧,Ring 太浅或软中断打满),rx_pause_ctrl_phy 是 802.3x 反压计数(拥塞侧);两者都不指向物理链路劣化。IB/RoCE 的等价信号是 symbol errors 与 link down/recovery 次数。

依据:Linux 内核文档 Documentation/networking/statistics.html(rx_crc_errors = “Number of packets received with a CRC error”,映射 IEEE 802.3 aFrameCheckSequenceErrors)。


5. 单个长文本请求引发 CUDA OOM 的最常见生产根因

题干:在高并发大模型推理服务中,显卡总显存 160GB(2×80GB),系统平稳运行中突然因某单个长文本请求引发 CUDA OOM 瞬间崩溃,最常见的生产触发根因是:

  • A. 客户端发送了 32k 超长 Prompt,系统未开启分块预填充(Chunked Prefill),导致中间激活值张量瞬时爆炸打满显存
  • B. 客户端关闭了浏览器
  • C. 宿主机磁盘被日志占满
  • D. 服务器时间与 NTP 服务器不一致

答案:A

根因是 prefill 阶段的峰值激活显存随序列长度放大:不开启分块预填充时,一个 32k 的 prompt 必须在单步内整体处理,中间激活张量按 32k 分配;开启 chunked prefill 后,单步只处理 max_num_batched_tokens 限定的那一段,峰值显存随之下降。vLLM 文档明确:关闭 chunked prefill 时 max_num_batched_tokens 必须大于 max_model_len,否则服务启动即可能崩溃——这正说明「单步可塞进整个上下文」是它的默认前提。
量化(示意,hidden=4096、intermediate=14336、BF16、词表 128k):

  • MLP gate+up 中间激活 ≈ 32768 × 14336 × 2 B × 2 ≈ 1.9 GB(单层瞬时)
  • 若对所有位置都算 logits:32768 × 128256 × 2 B ≈ 8.4 GB
  • 开 chunked prefill 且 max_num_batched_tokens=2048 时,上述张量按 1/16 缩小
    注:当前 vLLM 版本「chunked prefill is enabled by default whenever possible」,所以生产事故的形态通常是显式关闭它、或把 max_num_batched_tokens 设到 ≥ max_model_len,等价于没有分块。
    另一条常被忽略的路径:即使开了 chunked prefill,如果 gpu_memory_utilization 定得过高、KV Cache 把剩余显存吃满,那么单次 prefill 的激活峰值仍可能顶破余量——此时调小 max_num_batched_tokens 比调小 batch 更直接。
    B/C/D 与显存无关。

依据:vLLM《Optimization and Tuning》(“chunked prefill is enabled by default whenever possible”、“process large prefills in smaller chunks”、“When chunked prefill is disabled, max_num_batched_tokens must be greater than max_model_len … vLLM may crash at server start-up”)。


6. K8s 中 Pod 看不到 GPU、容器内缺 NVIDIA 驱动库

题干:在 Kubernetes 集群中部署大模型时,若 Pod 无法识别 GPU 硬件,排查发现容器内缺失 NVIDIA 驱动库映射,通常是 Pod 的环境变量中缺失了哪项关键配置?

  • A. CUDA_VISIBLE_DEVICES=""
  • B. NVIDIA_VISIBLE_DEVICES=all 和 NVIDIA_DRIVER_CAPABILITIES=compute,utility
  • C. PYTHONUNBUFFERED=1
  • D. NCCL_DEBUG=INFO

答案:B

NVIDIA Container Toolkit 的 prestart hook 只认这两个环境变量:NVIDIA_VISIBLE_DEVICES 决定把哪些 GPU 交给容器(all / UUID 列表 / none / void),NVIDIA_DRIVER_CAPABILITIES 决定挂载哪些驱动库与二进制(compute / utility / graphics / video / all)。NVIDIA 官方 CUDA 基础镜像正是靠两行 ENV 保证容器内 CUDA 可用:ENV NVIDIA_VISIBLE_DEVICES all 与 ENV NVIDIA_DRIVER_CAPABILITIES compute,utility。
A 是反向操作:把可见设备清空,结果是彻底看不到卡(它是故障的成因,不是缺的配置);C 只影响 Python 输出缓冲;D 只影响 NCCL 日志级别,与驱动库挂载无关。
注:在 K8s 里 NVIDIA_VISIBLE_DEVICES 由 device plugin 的 Allocate() 按分配的 UUID 自动写入;NVIDIA_DRIVER_CAPABILITIES 若完全不设置,toolkit 的默认值本身就是 utility,compute,因此更常见的翻车方式是显式把它设成不含 compute/utility 的值(例如空串或只留 graphics)。
分层排查顺序(先排掉与驱动库无关的原因,再落到这两个变量):

  1. 节点是否分到卡:kubectl describe node <node> | grep nvidia.com/gpu——Capacity 有值而 Allocatable 为 0 说明卡被标记 Unhealthy(ECC/Xid 掉卡)。
  2. 插件是否注册:ls /var/lib/kubelet/device-plugins/*.sock(kubelet 重启会清空该目录,插件须能自动重注册)。
  3. 容器内看设备与变量:ls -l /dev/nvidia*、env | grep NVIDIA——节点在、env 在而库缺失,就是 toolkit/CDI 的问题,可用 nvidia-container-cli list 确认实际挂了哪些库。
  4. 注意 /proc/driver/nvidia/gpus 不按可见性分区,不能用来判断容器里能看到几张卡。

依据:NVIDIA Container Toolkit《Specialized Configurations with Docker》(未设置时默认能力为 utility, compute;NVIDIA_VISIBLE_DEVICES 取值 all/none/UUID 列表/void);NVIDIA 官方 CUDA 基础镜像中的上述两行 ENV。


7. 异步 Checkpoint 导致恢复出的权重不可逆损坏

题干:在实现千卡异步 Checkpoint 保存时,如果仅在 Python 层通过后台线程将张量拷贝到 Host CPU 内存,可能会导致恢复出的权重出现不可逆的数据损坏,核心原因是:

  • A. Python 的垃圾回收机制(GC)把变量释放了
  • B. GPU 到 CPU 的内存拷贝是基于 CUDA Stream 异步非阻塞的,若未在拷贝前显式同步,CPU 读到的是未计算完的脏数据
  • C. NVMe 写入速度过快导致电平跳变
  • D. 宿主机未安装 NVMe 驱动

答案:B(机制描述需更正)

真正的机制是:tensor.to('cpu', non_blocking=True) / cudaMemcpyAsync 只是把 D2H 拷贝入队到 CUDA Stream 后立即返回,此时拷贝尚未完成。若后台线程不同步就去写盘,或训练主循环在拷贝完成前进入下一步并覆盖了源张量,落盘的就是「半旧半新」的撕裂数据——权重混合了两个 step 的值,不可逆。正确做法是:让拷贝与生产 kernel 在同一 stream 上保序,并在拷贝之后用 stream/event 同步(或等 future.result()),再复用源张量、再读 host buffer。PyTorch DCP 的 async_save 之所以安全,正是因为它在异步写盘前先把状态 stage 到内部 CPU buffer,与训练循环解耦。
A 不成立(有引用就不会被 GC 回收);C 的「电平跳变」与 D 的 NVMe 驱动与显存拷贝语义无关。

⚠️ 题目本身有问题:选项 B 写的是「未在拷贝前显式同步」,方向说反了。异步拷贝的失败模式是拷贝之后没有同步(源张量被下一步覆盖、或 host buffer 未就绪就被读);在同一 stream 上入队时,拷贝前的「同步」不但无必要,还会把异步拷贝退化成同步拷贝、吃掉全部收益。答案仍取 B(其余选项均不成立),但该选项应表述为「未在拷贝后同步 / 未做 stream 保序」。

依据:PyTorch CUDA 语义文档(to()/copy_() 的 non_blocking 参数 “lets the caller bypass synchronization when it is unnecessary”,且需 pinned memory 才能真正异步);PyTorch《Asynchronous Saving with DCP》教程(async_save 先拷贝到内部 CPU buffer,以保证写盘期间权重不被改动)。


8. 昇腾 910B 上 Loss 变 NaN,A100 正常

题干:在华为昇腾 910B 上训练大模型时,训练 Loss 突然变为 NaN,但在 NVIDIA A100 上完全正常。最常见的硬件计算层根因是:

  • A. 昇腾机房温度过低
  • B. 昇腾部分 MatMul 算子底层默认启用了非严格 IEEE 754 累加模式,需配置环境变量显式开启高精度累加模式
  • C. 交换机 MTU 设置过大
  • D. NPU 固件版本过新

答案:B(但变量名需修正)

真实根因确实落在算子计算精度路径不同:CANN 对部分算子会把 FP32 下转成 HF32(尾数仅 11 位,接近 FP16),或把 FP32 算子降到 FP16 执行;长序列 / 大 K 维度下累加误差被放大,越界后溢出为 NaN——同一份脚本在 NVIDIA 侧走的是另一套算子实现与累加序,所以表现为「A100 正常、910B NaN」。可核查的开关有:torch_npu.npu.matmul.allow_hf32(默认 False)、torch_npu.npu.conv.allow_hf32(默认 True)、ATC 的 --allow_hf32(Conv 默认开、Matmul 默认关)、ENABLE_HF32_EXECUTION、算子级 cube_math_type(KEEP_DTYPE / ALLOW_FP32_DOWN_PERCENTAGE / USE_FP16 / USE_HF32)、以及 --precision_mode(allow_fp32_to_fp16 / force_fp32 / must_keep_origin_dtype / allow_mix_precision)。另有两个影响累加序的开关会放大数值差异:HCCL_DETERMINISTIC(通信算子)与 ATB 的 ATB_MATMUL_SHUFFLE_K_ENABLE。A/C/D 与 NaN 无关。

⚠️ 题目本身有问题:不存在题干所描述的那个环境变量。逐一核对昇腾官方文档(CANN 环境变量参考、torch_npu 环境变量列表、ATC 参数说明、算子接口说明、MindStudio 大模型训练精度定位指南)后,未发现任何「高精度累加模式 / 严格 IEEE 754 累加」的环境变量;真实存在的只有上列精度模式开关(其中大多数是下调精度的开关,如 HF32/FP16 下转)与累加序/确定性开关,且它们多以 Python 属性、ATC 参数或算子入参的形式出现,并非「环境变量」。因此本题应按「算子默认精度路径不同,需显式指定保原 dtype / 高精度模式」来理解(如 precision_mode=must_keep_origin_dtype 或 force_fp32、matmul.allow_hf32=False),而不是题干给出的那个不存在的变量名。
另有两类同样常见、且已被官方文档记录的根因:CANN / 算子版本缺陷(例如 npu_fused_infer_attention_score 输出 NaN 的已知案例),以及 FP16 未正确配置 Loss Scaling 导致梯度溢出。排查顺序建议:先比超参与环境变量 → 再比三方库与 CANN 版本 → 再用 msProbe/msAgent 做溢出检测定位到算子。

依据:昇腾社区《–allow_hf32 算子调优选项》(Conv 默认开、Matmul 默认关);《torch_npu.npu.matmul.allow_hf32》(默认 False)与《torch_npu.npu.conv.allow_hf32》(默认 True);《配置精度模式》(--precision_mode);《大模型训练精度定位指南》(GPU→NPU 迁移精度 Checklist);CANN 环境变量参考(HCCL_DETERMINISTIC)。


9. TorchElastic(torchrun)的掉线自愈流程

题干:在千卡分布式训练任务中,当某台机器掉线时,若底层依赖 TorchElastic(torchrun)框架,其标准的高可用自愈恢复流程是:

  • A. 其余所有机器忽略该掉线节点继续单向计算
  • B. 触发 Rendezvous 重新选举,终止所有旧 Worker 进程并拉起新副本从最近检查点重新组建通信环路
  • C. 将掉线节点上的张量直接拷贝到 CPU 内存
  • D. 自动降级为单机 CPU 训练

答案:B

TorchElastic 的容错语义是全停全启,而不是「剔除坏节点继续跑」:官方原文 For a training job with n workers, if k<=n workers fail all workers are stopped and restarted up to max_restarts,且 ALL surviving workers are killed immediately。新成员通过 rendezvous(--rdzv-backend / --rdzv-endpoint / --rdzv-id)重新组成新的 WorkerGroup,rank 与 world size 都会重新分配——官方明确警告 RANK is NOT stable、DO NOT hard code assumptions about WORLD_SIZE。
A 在同步数据并行下不成立:缺一个 rank 的梯度,AllReduce 无法完成,且会破坏各副本一致性;C 是把显存状态搬到 CPU,与容错无关;D 不会自动降级为 CPU 训练。
注:torchrun 不负责从 checkpoint 恢复——它只负责重启进程组,load_checkpoint(...) 是训练脚本自身的责任(官方原文 Make sure to checkpoint your progress.)。
工程含义:由于「全停全启」,ckpt 频率直接决定单次故障的算力损失;且重启要重建通信组并 warmup,规模越大固定开销越贵。TORCHELASTIC_RESTART_COUNT / TORCHELASTIC_MAX_RESTARTS 可观测重启次数,超出上限后任务以失败结束而非无限重试。

依据:PyTorch《torchrun (Elastic Launch)》文档(fault tolerance 段落、rendezvous 三参数、RANK is NOT stable、Make sure to checkpoint your progress)。


10. FlashDecoding 相比 FlashAttention-2 的本质突破

题干:相比传统 FlashAttention-2,FlashDecoding 技术在超长上下文推理的 Decode 阶段之所以能带来数倍提速,其最本质的算法突破是:

  • A. 将模型权重全部量化为 INT1
  • B. 沿着 Key/Value 的序列长度维度(KV Length)切分并在多 SM 上并发计算 Partial Softmax,最后再归约,打满 GPU 占用率
  • C. 直接丢弃超过 2k 的历史上下文
  • D. 借助 CPU 协处理器加速计算 Attention

答案:B

Decode 阶段 query 长度只有 1,而 FlashAttention-2 的并行维度是 batch × query 分块,batch=1 时只能占满极少数 SM——官方原话是 With a batch size of 1, FlashAttention will use less than 1% of the GPU!(A100 有 108 个 SM)。Flash-Decoding 的做法是新增一个并行维度:K/V 的序列长度——把 K/V 切成若干 chunk,各 chunk 在多个 SM 上并行计算「局部 attention + log-sum-exp」,最后按 LSE 加权归约出精确结果(本质是把 online softmax 的合并再做一次,因此不是对分块输出取平均)。这样并行度不再受 batch 限制,attention 耗时在 batch=1 下几乎不随上下文增长。官方数据:长序列下端到端解码最高 8× 加速,attention 本身最高 50×,且 FlashAttention 2.2 起、xFormers 0.0.22 起可用。
三步流程:① 把 K/V 切成 chunk(实际只是张量视图,不产生 GPU 算子);② 每个 chunk 并行跑一次 FlashAttention,并额外输出每行一个标量——该 chunk 的 log-sum-exp;③ 用这些 LSE 对各个 split 的输出做加权归约。online softmax 的增量合并因此被用了两次:chunk 内部一次,chunk 之间再一次。这也是它不能用简单平均合并分块输出的原因。
A 是量化(与 attention 并行度无关);C 是丢弃历史上下文(质量不可接受,且非该技术);D 与 CPU 协处理器无关。

依据:Stanford CRFM《Flash-Decoding for long-context inference》(“adds a new parallelization dimension: the keys/values sequence length”、三步切分与 LSE 归约、“up to 8x speedups in decoding speed”、“attention itself is up to 50x faster”)。


答案速览

题号 1 2 3 4 5 6 7 8 9 10
答案 B C B B A B B B B B

题目本身有问题的题:第 3 题(V4 未使用 MLA,事实错误)、第 7 题(同步时机方向写反)、第 8 题(所述环境变量不存在)。第 6、9 题的选项表述略有简化,但答案唯一、不受影响。

四、多选题(10 题 · 30 分)

答案速览:1. ABC 2. ABC 3. ABC 4. ACD 5. ABC 6. ABC 7. ABC 8. ABC 9. ABD 10. ABCD
需修正的题面:第 1 题 A 项措辞(“两两配对”)、第 5 题 D 项(职责错配)、第 10 题 C 项(来源与版本名)。


1. GPU 慢节点(Straggler)的标准排查手段

题干:生产集群中诊断某台 GPU 机器沦为“慢节点(Straggler)”的标准排查手段包括:

  • A. 运行全集群节点两两配对的 NCCL AllReduce 带宽测试(all_reduce_perf),检查离群点
  • B. 使用 nvidia-smi -q -d PERFORMANCE 检查是否有 GPU 因供电异常或过热触发硬件保护性降频
  • C. 检查 lspci -vvv 确认 GPU 的 PCIe 协商链路是否从 Gen5 x16 意外降级为 Gen1/x1
  • D. 仅调大训练任务的超时等待时间,不做物理定位
    答案:ABC
  • A 对(措辞需修正):跨节点跑 all_reduce_perf 并比对每个 rank 的 busbw,是定位慢节点的标准第一步。
    nccl-tests 的 -a/--average 支持 Rank0 / Avg / Min / Max 聚合,Min 与 Max 的差距就是离群点的直接证据;-c 做结果校验(大集群上很慢,默认 1 次),-n/-w 控制迭代与预热,-z 3 加屏障。
    典型命令(可直接执行):
    mpirun -np 16 -x NCCL_DEBUG=WARN ./all_reduce_perf -b 8 -e 8G -f 2 -g 1 -n 50 -w 10 -a Max
    ⚠️ 但"全集群节点两两配对"不是标准做法:万卡规模下两两组合是 O(N²),不可行;正确口径是在同一个作业的通信域/节点集合上跑(或按机架/TOR 分组抽样),再按 rank 看离群。A 的核心意图(带宽基线 + 离群点)成立,故计对。
  • B 对:nvidia-smi -q -d PERFORMANCE 输出 Performance State(P0–P12)与 Clocks Event Reasons,可判定是否被硬件保护性降频。
    当前文档中的原因名为:SW Power Cap、HW Slowdown、HW Thermal Slowdown、HW Power Brake、SW Thermal Slowdown、Board Limit、Reliability。
    注意 HW Slowdown 是伞形项——HW Thermal Slowdown 或 HW Power Brake 任一激活即置位,降频幅度可达 2 倍以上;HW Power Brake 常由外部供电/主板断言触发,而非 GPU 自身过热。
    批量监控用 DCGM 的 DCGM_FI_DEV_CLOCK_THROTTLE_REASONS。
  • C 对:lspci -vvv 看 LnkCap / LnkSta:Gen5 = 32GT/s、Gen1 = 2.5GT/s;Width x16 掉到 x1 或速率回落到 Gen1,会让 NCCL 单机带宽断崖下跌。
    常见诱因:转接卡/线缆/retimer、BIOS ACS 与电源状态、riser 接触不良。辅以 nvidia-smi topo -m 确认 NVLink/PCIe 拓扑是否与预期一致。
  • D 错:调大超时只是把症状藏起来——慢节点仍在拖慢全作业,白烧 GPU 机时,且会掩盖真正的 hang,使定位更难。
    超时只应用于"先二分确认是 hang 还是 slow"的场景,不是处置手段。

依据:NVIDIA/nccl-tests README(all_reduce_perf 参数与 -a 聚合语义);NVIDIA nvidia-smi 文档(-q 的 Performance State / Clocks Event Reasons 及原因名清单,现文档用 HW Power Brake 而非 HW Power Brake Slowdown);NVML API 文档 nvmlClocksEventReasons;PCIe 各代链路速率定义。


2. RoCEv2 微突发丢包与训练性能剧烈抖动

题干:大规模 RoCEv2 网络发生“微突发丢包”与训练性能剧烈抖动时,关键的网络排查与调优手段包括:

  • A. 检查交换机各端口是否存在 Pause 帧风暴(rx_pause 计数器持续激增)
  • B. 调优交换机 ECN 门限水线(Kmin / Kmax),使其在缓冲区堆积早期尽早触发网卡降速
  • C. 确保全链路(网卡、交换机端口)统一开启 MTU 9000(巨帧),避免大包跨跳分片
  • D. 关闭交换机 PFC 流量控制并退回不可靠 UDP 广播
    答案:ABC
  • A 对:rx_pause 持续激增说明上游端口在发 PFC Pause 帧,即缓冲区已顶到 PFC 门限。
    PFC 会带来队头阻塞、受害流(victim flow)扩散,严重时形成 PFC 死锁。
    排查口径:ethtool -S <dev> 看 rx_pause / tx_pause(RoCE 相关计数)、交换机侧看各端口与各优先级的 Pause 计数,同时看 NIC 侧 CNP 计数(rx_cnp)。
    若 Pause 与 CNP 同时暴涨,说明 ECN 生效太晚、PFC 在兜底。
  • B 对(须配对调参):DCQCN 的 ECN 标记是 Kmin/Kmax 之间的概率标记:交换机打标 → 接收端回 CNP → 发送端降速。
    下调 Kmin/Kmax = 更早标记、更早降速,能在微突发堆积早期抑制注入速率,避免直接触发 PFC。
    但不是越低越好:Kmin 过低会频繁误降速,链路利用率下降、吞吐抖动。
    必须与 NIC 侧 DCQCN 参数、交换机 headroom/shareroom(按 MTU 与链路速率计算)、PFC watchdog 一起调。
    NVIDIA Onyx 的 RoCE 模式中,TC3 的 ECN 绝对门限可配范围示例为 150–1500。
  • C 对(关键是一致性):RoCE 场景下全链路统一巨帧(MTU 9000)是通行实践,MTU 不一致会导致跨跳丢弃/分片、PFC headroom 计算失配。
    "统一"比"9000"更重要:NIC、所有交换机端口(含上行/下行)必须一致;headroom 必须按 MTU × 链路速率重算,否则改完 MTU 反而更容易触发 Pause。
  • D 错:PFC 是 RoCE 无损语义的基石,关掉它等于让 RDMA 流量在有损链路上跑,丢包触发 go-back-N 重传风暴,性能更差。
    且 RoCEv2 是 UDP 单播封装(IP + UDP + IB BTH),根本没有"广播"一说,"退回不可靠 UDP 广播"是伪命题。
    厂商确实提供 lossy / semi-lossless 模式(NVIDIA Onyx 有 lossless / semi-lossless / lossy 三档),但那是靠上层重传与 ECN 兜底的另一种设计,与"关 PFC + 广播"无关。

依据:NVIDIA Onyx User Manual「RDMA over Converged Ethernet (RoCE)」章节(lossless 依赖 PFC、ECN 依 RFC3168 语义与 CNP 回压、TC3 门限示例,并明确 “lossless does not mean that packets are absolutely never lost”);DCQCN 的 Kmin/Kmax 概率标记机制(厂商公开材料与 RoCE 调优实践文献)。


3. 长文本在线推理中 KV Cache 被打爆的生产暗雷

题干:在大模型长文本推理在线服务中,可能导致 KV Cache 迅速被耗尽并引发 OOM 崩溃的生产暗雷包括:

  • A. 外部流量突发大量超长 Prompt 请求,耗尽了预分配的 PagedAttention 显存池
  • B. 推理框架未显式配置安全的最大上下文限制(如 --max-model-len)
  • C. 客户端在 SSE 流式传输中单方面断开连接,而服务端未监听取消信号导致后台持续盲算生成
  • D. 开启了 FlashAttention-2 算子
    答案:ABC
  • A 对(需精确表述):PagedAttention 的 KV 池是启动时按显存预算预分配的固定池(由 --gpu-memory-utilization 与显存 profiling 决定大小),突发超长 Prompt 会按块快速吞掉整池。
    精确表述:在 vLLM V1 里池耗尽通常先表现为抢占/重算、排队变长、TTFT 飙升,而不是立刻 OOM;只有当激活、CUDA Graph、workspace 等非 KV 显存同时抬升时才演化为硬 OOM。
    所以 A 描述的风险真实,但"耗尽即 OOM"是简化说法。
  • B 对:--max-model-len 不显式指定时会从模型 config 推导(官方原文:“If unspecified, will be automatically derived from the model config”)。
    对 1M 上下文的新一代模型,这等于默认放行极端长请求;另有 -1 / auto 模式(按显存自动选最大长度)。
    生产必须显式设安全上限,并在请求侧用 max_tokens 二次约束。
    ⚠️ 精度补充:该配置缺失在 vLLM 上更多表现为启动期报错(“The model’s max seq len (X) is larger than the maximum number of tokens that can be stored in KV cache”)或运行期抢占风暴,而非必然 OOM;但"未显式限制最大上下文"确实是标准暗雷。
  • C 对:SSE 场景下客户端断连而服务端不监听取消信号,会产生孤儿请求。
    vLLM 官方文档原话:“Cancelling the asyncio task alone does NOT abort the underlying engine request”——请求会继续占槽位、继续生成到已死的连接上。
    正确做法:在断开回调里显式调 engine_client.abort(request_id),并做 stream 停止 + 任务取消 + await 收尾。
  • D 错:FlashAttention-2 通过分块 + online softmax 避免物化 N×N 注意力矩阵,是显存下降手段,属于缓解措施而非风险项。
    选错 attention 后端可能影响数值与吞吐,但不会导致 KV Cache 被耗尽。

依据:vLLM Engine Arguments 文档(max_model_len 默认从模型 config 推导、-1/auto 模式、gpu_memory_utilization 默认 1.0(可扩展 KV 缓存按实测空闲显存定容)或 0.92、kv_cache_memory_bytes);vLLM 官方 API 文档中 realtime/streaming 连接的 cleanup() 说明(孤儿请求与显式 abort() 的必要性);vLLM 社区对该 “max seq len larger than KV cache” 报错的官方处置建议(降 max_model_len / 增 TP / 降 max_num_batched_tokens)。


4. Checkpoint 性能与工程可靠性治理最佳实践

题干:大模型训练断点续训(Checkpoint)性能与工程可靠性治理的最佳实践包括:

  • A. 采用分层多级保存:GPU ➔ Host RAM (Pinned) ➔ 本地 NVMe ➔ 远端对象存储(Ceph/S3)
  • B. 保存时仅持久化模型权重,丢弃随机数种子(RNG state)与优化器状态
  • C. 利用多进程/多线程异步 I/O 刷盘,主训练进程在显存拷贝到内存后立即继续 Forward
  • D. 限制集群并发写入远端存储的客户端连接数,避免千卡瞬时刷盘打瘫存储网关
    答案:ACD
  • A 对:分层多级保存(GPU → Pinned Host RAM → 本地 NVMe → 远端对象存储 / Ceph / S3)是通行结构:显存到 pinned 内存的 D2H 拷贝是"快照点",本地 NVMe 做中转落地区,远端做持久层。
    公开实践(ByteCheckpoint)把存储层次归纳为内存、本地磁盘、远端 HDFS/NAS;工程上"先写本地快路径、再异步上传"能显著降低训练停顿。
    注意:本地 NVMe 只是加速层,不是持久层,节点损坏即丢;只有远端落盘完成才算真正 checkpoint。
  • B 错:只存权重会让续训不等价。
    优化器状态(Adam 的 m/v)不恢复,等价于换了个优化器重启,loss 曲线抖动甚至劣化。
    RNG 状态(Python/NumPy/CUDA,每 rank 独立)与 data iterator 位置不恢复则破坏确定性重放,可能重复或跳过数据。
    Megatron-LM 的 checkpoint state 中 rng_state、optimizer、train_data_iterator 都是一等公民;PyTorch DCP 文档同样强调要捕获每 rank 独有状态(数据位置 + RNG)。
  • C 对(有前提条件):异步 I/O + 主训练进程在 D2H 完成后立即继续,是"零停顿 checkpoint"的标准做法(DCP async_save 先把状态拷到 staging buffer 再后台上传;需以 backend="cpu:gloo,cuda:nccl" 初始化进程组)。
    前提必须说清:需要足够的 pinned host 内存(≈ 单次在飞分片大小),且在 D2H 拷贝完成前不能复用/释放 staging buffer,否则会拿到脏数据。
  • D 对(是缓解,不是根治):千卡级瞬时齐刷会给存储的元数据/控制面造成巨大压力。
    ByteCheckpoint 明确记录:海量 checkpoint 读写请求 “can overload the master node, causing delays in file metadata operations”(NameNode 串行 concat、SDK 冗余元数据调用都会放大)。
    缓解手段:分片写 + 汇聚写(避免所有 rank 写同一文件)、层级化/树状上传、本地 staging 后批量上传、存储侧 QoS 与带宽预留、跨作业错峰。
    ⚠️ 单纯"限并发连接数"会把吞吐一起限死,应配合聚合写与存储侧限速使用。

依据:ByteCheckpoint 论文(arXiv 2407.20143:重分片负担、重 I/O(175B / 4096 卡存 HDFS 约 200s)、元数据与 NameNode 过载、异步流水线 D2H→序列化→上传、RNG 与优化器状态打包);PyTorch DCP 与 Azure Databricks AI Runtime 文档(DCP 分片并行写、async_save、本地 staging 后上传、.metadata 原子完成标志、RNG 与数据管线位置);NVIDIA Megatron-LM checkpoint state 键(rng_state 等)。


5. K8s 万卡集群先进拓扑调度器的核心能力

题干:在 Kubernetes 平台上调度万卡 AI 算力集群时,先进拓扑调度器(如 Volcano / Scheduler Plugins)的核心能力包括:

  • A. Gang Scheduling(成组调度,保证分布式训练 Pod“全赢或全输”,彻底杜绝死锁)
  • B. NUMA / NVLink 拓扑感知调度(尽量将单任务多卡绑定在同一 CPU NUMA 节点与 NVSwitch 域)
  • C. 跨机柜网络感知调度(尽量将同一训练任务收敛在同一 TOR 交换机下的同一机架内)
  • D. 节点故障探测联动(遇到坏卡自动打上 NoSchedule 污点并触发驱逐自愈)
    答案:ABC(D 属职责错配)
  • A 对(结论成立,"彻底"是夸大):Volcano 的 gang 插件实现成组调度:未就绪任务视为更高优先级、按 minAvailable 判断是否成组放行,避免"半个作业占着资源等另一半"导致的资源死锁。
    ⚠️ 但"彻底杜绝死锁"不成立:gang 只消除资源分配层面的部分分配死锁;NCCL/集合通信 hang、作业间依赖环、网络层 PFC 死锁都不在 gang 的射程内。
  • B 对(能力分布在两层):Volcano 有 numa-aware 插件,按 none / best-effort / restricted / single-numa-node 策略过滤与打分,打分公式 score = weight × (100 − 100 × numaNodeNum / maxNumaNodeNum),需 kubelet cpu-manager-policy=static 与 topology-manager 配合。
    职责边界:调度器做的是节点级 NUMA 打分;真正的"卡落在哪个 NUMA / NVSwitch 域"由 kubelet topology manager + GPU/NPU device plugin(NVIDIA device plugin、昇腾 device plugin)在设备分配阶段完成。
    所以 B 描述的意图正确,但能力横跨调度器与 kubelet 两层。
  • C 对:Volcano 的 network-topology-aware 插件 + HyperNode CRD:把 IB/RoCE/NVSwitch 等多级网络拓扑抽象成树(节点 → TOR → 上层交换机 → …)。
    作业通过 networkTopology 声明 mode: hard|soft 与 highestTierAllowed / highestTierName,实现"尽量收敛到同一 TOR / 同一性能域"。
    已知约束:需较新的调度器插件版本(厂商文档示例要求 ≥1.21.1)、不支持抢占、HyperJob 类负载的该配置会被忽略、拓扑运行期变更不保证正确性。
  • D 错(本题关键核实点):"探测坏卡 + 自动打污点 + 驱逐"不属于调度器能力,调度器只是污点的消费者。 职责边界如下:
    • 探测层:DCGM/GPU 健康监控、node-problem-detector、device plugin 的健康上报、NVSentinel 的 gpu-health-monitor / syslog-health-monitor 等,产出健康事件(NVSentinel 的 HealthEvent 含 event.agent、isFatal、errorCode 等字段)。
    • 隔离层:NVSentinel 的 fault-quarantine 依据规则做 cordon / taint / label(示例污点 nvidia.com/gpu-error=fatal:NoSchedule),恢复时只撤销自己加的那一份;若节点已被他人 cordon/taint,则记录为 pre-existing 且不动它。
    • 驱逐层:drain/驱逐由独立的 node-drainer 组件负责,fault-quarantine 自身不做驱逐(默认示例用 NoSchedule,只挡新调度,不驱逐运行中 Pod)。
    • 调度层:Volcano / kube-scheduler 在 predicates 阶段读取 cordon 与 taint,不发起探测。Volcano 官方插件清单(gang / conformance / DRF / nodeorder / predicates / priority 等)里没有节点健康或硬件故障探测类插件。
    • 额外一层:驱逐"正在运行的训练 Pod"还需要作业控制器(Volcano Job 控制器、训练 Operator、Kueue 或外部重调度器)删除并重建 PodGroup,调度器不会因硬件故障自行驱逐运行中的 Pod。
    • 昇腾侧同理:NPU 故障触发重调度依赖 device plugin 与集群调度套件上报,调度器只是消费方。

依据:Volcano 官方 Plugins 文档(action 为 enqueue/allocate/preempt/reclaim/backfill;插件清单中无健康探测类);华为云 CCE Volcano 文档(NUMA 亲和策略与打分公式;多级网络拓扑调度与 HyperNode、network-topology-aware 插件、hard/soft 模式与已知约束);NVIDIA NVSentinel 文档(Fault Quarantine 的 cordon/taint/label 语义与恢复规则、Node Drainer 的独立职责、HealthEvent 触发条件)。


6. Triton / CUDA 算子开发中的性能反模式

题干:在使用 Triton / CUDA 进行算子深度开发时,会导致 GPU 性能断崖式下跌的典型反模式包括:

  • A. 显存访问未对齐(Misaligned Access),导致无法触发连续内存合并读取(Coalesced Access)
  • B. 同一个 Warp(32 个线程)内部执行了严重分化的 if-else 分支(Warp Divergence)
  • C. 频繁读写片外全局显存(Global Memory),未充分利用片上共享内存(Shared Memory)
  • D. 使用 BF16 精度进行矩阵乘法计算
    答案:ABC
  • A 对:对齐决定向量化宽度。
    Triton 的 AxisInfo 分析为每个维度推导 contiguity / divisibility / constancy,Coalesce pass 据此选择 layout 与 sizePerThread,最高目标 128-bit(16 字节)向量化访存。
    基址或 stride 未对齐会让 divisibility 退化为 1,向量化失效,退化成窄访存甚至逐元素访存,访存受限 kernel 直接断崖。
    CUDA 侧同理:非合并/非对齐访存会降低每个 32 字节 sector 的利用率、事务数翻倍。
  • B 对:SIMT 下同一 warp 内分支分化会串行执行两条路径,发散区段的有效并行度可低至 1/32。
    Volta 之后的 independent thread scheduling 改善了前进保证与调度,但不恢复发散分支的吞吐;短分支体的谓词化(predication)只省了跳转开销,分支体大时依然亏。
  • C 对:全局显存延迟是数百周期量级且带宽有限,数据复用必须靠片上存储(shared memory / 寄存器)与分块(tiling)。
    矩阵乘的经典优化就是把块载入 shared/寄存器再 tl.dot;反复直接读写 global memory 会让 kernel 长期卡在访存受限区。
  • D 错:BF16 在 Ampere 及以后的 Tensor Core 上就是标准的高吞吐矩阵乘精度(8 位指数、与 FP32 同量级的动态范围),用 BF16 做 matmul 是性能使能项,不是反模式。
    真正该警惕的精度反模式是:低精度累加、FP8 不做缩放/amax、以及为了精度把 matmul 退回 FP32 而放弃 Tensor Core。

依据:Triton 编译器文档(Memory Coalescing 与 AxisInfo 的 contiguity-divisibility-constancy 三元组、Coalesce pass、TMA 路径的 16 字节向量化目标);CUDA 编程模型文档(warp = 32 线程 SIMT 执行、分支分化串行化、合并访存与 sector 利用率、片上存储与延迟隐藏)。


7. 昇腾(Ascend NPU)生态部署前沿开源大模型的主要工程挑战

题干:国产昇腾(Ascend NPU)生态下部署开源前沿大模型(如 DeepSeek-V4-Flash / Qwen3)时的主要工程挑战包括:

  • A. CANN 算子库对最新前沿算子(如动态稀疏 Attention、特定量化 Kernel)的覆盖存在滞后
  • B. HCCL 通信原语与 NCCL 在部分高级重叠特性(如 P2P Buffer 注册、自定义拓扑)上存在语义差异
  • C. 开源推理引擎(vLLM、SGLang)官方主干优先适配 CUDA,NPU 分支需持续维护与同步
  • D. 昇腾芯片不支持运行标准 Linux 操作系统
    答案:ABC
  • A 对:CANN 算子/内核覆盖对最前沿结构存在滞后。
    第三方实测研究(16 卡昇腾 910 + CANN + vLLM-Ascend)指出:MoE、DeepSeek Sparse Attention、异构多头路径、MTP 草稿头 “are exactly the components whose Ascend support proved least mature”。
    关键结论是组合的交叉积未经测试(“each feature may work in isolation, but their cross-product is untested”),且为了让插件跑起来并产出正确结果需要 12 处源码级补丁,不少最难的问题表现为静默数值错误而非崩溃。
    动态稀疏 Attention、特定量化 Kernel 的滞后与此一致。
  • B 对:HCCL 是 CANN 中 NCCL 的对位物,但语义与能力集不一一对应:
    • 拓扑处理路径不同:NCCL 自动探测 NVLink 拓扑;HCCL 历史上依赖 rank file 手工描述 NPU 互联关系,较新版本才提供自动生成工具。
    • 调优旋钮集合不同(如 HCCL_BUFFSIZE 等,与 NCCL 的 NCCL_ALGO/PROTO/BUFFSIZE 体系不对等)。
    • NCCL 侧的高级重叠/注册类能力——ncclCommRegister 用户缓冲区注册、Userbuffers 流水线重叠、更细的算法与协议选择——在 HCCL 侧没有 1:1 对应;NPU 上的通算重叠多由上层库(MindSpeed / MindSpeed-Core、torch_npu)另行实现。
      因此"语义差异 + 高级重叠特性缺位"的判断成立。
  • C 对:开源推理引擎主干优先适配 CUDA,NPU 支持以独立厂商插件/分支形态存在(如 vllm-ascend),需持续跟进上游。
    同一实测研究把该插件描述为 “a young, fast-moving reimplementation whose tested configurations are a subset of what upstream vLLM supports”,并记录了并行策略脆弱(TP 被头分组因子卡在 TP=8、序列并行因分片张量尺寸不一致被关闭、PP>1 时静默数值污染)、图编译与投机解码路径的兼容问题。
  • D 错:昇腾完全运行标准 Linux。
    CANN 支持 openEuler、Ubuntu、EulerOS、CentOS、BCLinux、Kylin V10、UOS、Debian、SLES、Tlinux 等发行版(aarch64 / x86_64),安装方式与常规 Linux 软件栈一致,并提供官方容器镜像。
    驱动需匹配内核头文件、部分发行版会触发驱动源码编译,但"不支持运行标准 Linux"是明显错误项。

依据:arXiv 2607.08215《On the Limitations of Non-GPU AI Accelerators for Large-Model Inference》(CANN/HCCL 定位、算子与特性覆盖缺口、12 处 vLLM-Ascend 补丁、并行与数值故障、静默损坏);华为 CANN 软件安装指南(支持 OS 清单与安装约束);HCCL 与 NCCL 的对比实践材料(拓扑配置方式、调优差异);NVIDIA NCCL 用户缓冲区注册文档。


8. 生产推理网关(API Gateway)必备的容灾与防护机制

题干:大模型生产推理网关(API Gateway)必须具备的核心容灾与防护机制包括:

  • A. 动态限流与排队降级(基于 Token 消耗速率与 P99 队列延迟进行智能过载保护)
  • B. 客户端断开连接感知(Client Disconnect Detection,及时中止孤儿请求释放显存)
  • C. 实例健康探活与熔断(对持续报错或 TTFT 异常暴增的 Worker 自动剔除)
  • D. 将所有外部请求的超时时间均配置为无限大(Infinity)
    答案:ABC
  • A 对:过载保护要按成本而不是只按请求数。
    基于 token 消耗速率(含输入 + 输出预算)做准入与排队,基于 P99 排队延迟/队列深度做降级与甩载,超限返回 429/503 并带 Retry-After,把"过载"转化为可控排队而非雪崩。
    仅按 QPS 限流对长短混合负载无效——一个 1M-token 请求的成本约等于上千个短请求。
  • B 对:客户端断连感知必须做到引擎层的取消传播。
    vLLM 官方文档明确:只取消 asyncio 任务不会中止引擎请求,请求会继续占槽位、生成到已死连接上(孤儿请求),必须显式 abort(request_id)。
    网关侧要在 HTTP/2 RST_STREAM 或上下文取消时把取消沿调用链下传,否则显存与队列槽位被白占。
  • C 对:健康探活 + 熔断:liveness/readiness 探针做基础剔除;业务级熔断按错误率与 TTFT 异常抬升做被动离群剔除(passive outlier ejection),对持续报错的 Worker 摘流量,恢复后按比例回灌。
    注意 TTFT 是比 QPS 更灵敏的劣化信号。
  • D 错:把超时设为无限大是典型反模式——挂住的请求会长期占住连接、KV Cache 与队列槽位,故障从单实例扩散成全站,且彻底失去背压。
    正确做法是分层超时(连接 / TTFT / 总时长)+ 超时预算内的有限重试 + 取消传播(deadline propagation),并区分"可重试"与"不可重试"错误。

依据:vLLM 官方文档(客户端断连与显式 abort() 的必要性、孤儿请求占槽位);vLLM Engine Arguments(max_num_seqs 等与过载相关的调度旋钮);NVIDIA Dynamo 等推理栈的 Request Cancellation 文档(取消需传播到引擎);K8s 探针语义与熔断/离群剔除的通用实践。


9. PyTorch CUDA OOM 的精准分析手段

题干:排查定位 PyTorch 训练中的 CUDA Out of Memory (OOM) 时,推荐使用的精准分析手段包括:

  • A. 使用 torch.cuda.memory_summary() 打印显存分配器的详细分布表(Allocated vs Cached)
  • B. 捕获并导出 PyTorch Memory Snapshot(显存快照),在浏览器中可视化显存分配时序火焰图
  • C. 查看操作系统层的 Swap 交换区大小
  • D. 借助 Nsight Systems (nsys) 捕获 CUDA 运行时显存峰值开销
    答案:ABD
  • A 对:torch.cuda.memory_summary() 打印分配器明细表,区分 Allocated / Active / Requested / Reserved(Cached)、non-releasable 内存与各 segment 占用,是判断"真占用 vs 缓存碎片"的第一手材料。
    配套 torch.cuda.memory_allocated() 与 memory_reserved() 看差值,即可快速区分内存泄漏与碎片化。
  • B 对:官方推荐的深挖手段:
    torch.cuda.memory._record_memory_history(max_entries=...) 记录 → torch.cuda.memory._dump_snapshot("snap.pickle") 导出 → 拖到 **https://pytorch.org/memory_viz**,得到"分配随时间"的交互式图,每个张量可点开看分配栈。
    离线可用仓库脚本:python torch/cuda/_memory_viz.py trace_plot snap.pickle -o snap.html
    它能把"迭代间不释放的常驻张量"(泄漏)、碎片、以及 OOM 那一刻的最大分配者一并暴露。
  • C 错:操作系统 Swap 与显存无关——CUDA 分配器不参与换页,GPU 显存不会被 swap 出去(NPU 同理)。
    换页机制只在主机内存 OOM、或显式使用统一内存/可超售分配(cudaMallocManaged + HMM)时才有意义;PyTorch 默认分配器不走这条路。
    用 Swap 排查 CUDA OOM 是方向性错误。
  • D 对:nsys profile --cuda-memory-usage=true 会跟踪 CUDA 显存分配/释放与设备内存占用随时间的变化,配合 nsys stats 的显存类报表可定位峰值来源。
    实践注意:多进程 / 多 rank 场景要确保跟踪到正确的子进程,否则内存数据会为空。
    PyTorch 侧还可叠加 torch.profiler(profile_memory=True, with_stack=True) 导出内存时间线。

依据:PyTorch 官方博客《Understanding GPU Memory 1: Visualizing All Allocations over Time》与 torch_cuda_memory 文档(_record_memory_history / _dump_snapshot / _memory_viz.py、memory_viz 站点、memory_summary 的 Allocated vs Cached 口径);NVIDIA Nsight Systems 用户手册与官方论坛(--cuda-memory-usage 选项与子进程跟踪注意事项)。


10. Megatron-LM 等分布式框架中通信与计算深度重叠的技术

题干:在 Megatron-LM 等分布式框架中,能够实现通信与计算深度重叠(Overlap)的技术包括:

  • A. TP 与 Sequence Parallelism 中 ReduceScatter / AllGather 与 GEMM 算子的异步流水线重叠
  • B. Pipeline Parallelism 中 1F1B(One Forward One Backward)调度机制
  • C. DeepSeek-V4 DualPipe / DualPipe-V2 中的双向流水线前向后向通信重叠
  • D. DDP(数据并行)中反向传播阶段的 Bucket 异步梯度同步与反向计算重叠
    答案:ABCD(C 的名称有误,见 ⚠️)
  • A 对:Megatron 的 TP 重叠(tp_comm_overlap=True,要求 sequence_parallel=True、tensor_model_parallel_size >= 2)分两层:
    • bulk overlap(默认开启):把与计算无直接依赖的 TP 通信与线性层计算重叠。
    • pipelined overlap:把有依赖的通信改造为流水——激活/梯度 all-gather 拆成多次 P2P ring 交换,reduce-scatter 改造为 GEMM 输出的 P2P ring 交换 + 接收端归约,与 GEMM 流水并行。
      可用现成配置模板(如 userbuffers_bf16_*_tp4_mbs1_seqlen*),依赖 Userbuffers / NVLink。
  • B 对:1F1B 是流水线调度机制,让不同 micro-batch 的前向与反向交错执行,从而把 PP 的 P2P 发送/接收(相邻 stage 的激活与梯度传输)压在其他 micro-batch 的计算之下,并把气泡从 GPipe 的 O(micro-batch 数) 降到 (PP−1)(F+B)。
    精确表述:1F1B 提供的是"交错机会",真正的重叠来自"P2P 在飞时算别的 micro-batch"。
  • C 对(机制真实,命名错误):双向流水线 + 前向后向通算重叠真实存在且已在生产使用。
    官方名称是 DualPipe(源自 DeepSeek-V3 技术报告),“DualPipe-V2” 不是官方名称;DeepSeek-V4 确实存在,其技术报告明确写了 “we adjust the DualPipe 1F1B overlapping scheme to accommodate the increased pipeline communication”,即 V4 训练仍在用(调整过的)DualPipe 方案。
    故本选项描述的机制正确,但题面把来源与版本名写错了。
  • D 对:DDP 的 Reducer 把梯度按桶聚合(bucket_cap_mb 可调;参数按 Model.parameters() 大致逆序入桶,以匹配反向中梯度就绪顺序),每个参数注册一个 autograd hook,桶填满即异步发起 allreduce。
    官方文档原话:“DDP’s performance advantage comes from overlapping allreduce collectives with computations during backwards”。
    两个工程要点:所有进程必须按桶索引顺序发起 allreduce(顺序不一致会结果错误或反向 hang);用 TorchDynamo 编译整图会破坏重叠,需要 DDPOptimizer 在桶边界切图,梯度累积时用 no_sync。

⚠️ 题目本身有问题(第 10 题 C 项):

  1. DualPipe 出自 DeepSeek-V3 技术报告(arXiv 2412.19437),2025-02-27 作为开源周第四天项目在 deepseek-ai/DualPipe 仓库开源,要求 PyTorch 2.0+;气泡公式 (PP/2−1)(F&B+B−3W)、参数 2×、激活 PP+1。它不是"V4 的算法"。
  2. 不存在官方 “DualPipe-V2”。仓库中唯一相近的是 DualPipeV——由 DualPipe “cut-in-half” 得到的 V 形调度(仓库注明来自 Sea AI Lab 的博客),特点是激活降到 PP/2、设备数减半;它是另一个调度,不是"第二版"。另有非官方博客把某版本称为 “DualPipe 2.0”,无可信来源支撑。
  3. DeepSeek-V4 存在(2026-04-24 发布预览版并开源权重:V4-Pro 1.6T 总参 / 49B 激活,V4-Flash 284B / 13B 激活,1M 上下文,技术报告 arXiv 2606.19348)。所以"V4 是否存在"这一质疑可以排除;真正需要纠正的是把 DualPipe 归给 V4,并杜撰出 “V2”。

依据:NVIDIA Megatron Bridge《Communication Overlap》文档(TP/SP 的 bulk 与 pipelined overlap、all-gather/reduce-scatter 的 P2P ring 改造、DDP 的 overlap_grad_reduce / overlap_param_gather);PyTorch DDP 官方 notes(Reducer 分桶、autograd hook、按桶序 allreduce、与反向计算重叠、DDPOptimizer);deepseek-ai/DualPipe 仓库 README(来源、DualPipeV 与 Sea AI Lab 归属、气泡与显存对比表、依赖);DeepSeek-V3 技术报告;DeepSeek 官方 V4 发布公告与 V4 技术报告。


五、简答题(第 1–10 题)

1. 大模型 GPU 显存中"静态显存"与"动态显存"的本质区别,及推理场景的构成

要点:

本质区别 = 生命周期与可预测性

  • 静态显存:进程/服务启动阶段一次性申请、运行期基本不变(或只增不减),容量可由参数量与框架配置静态估算;超配的典型表现是"服务起不来 / 加载即 OOM"。
  • 动态显存:随请求数、批大小、序列长度实时申请与释放,峰值由调度策略(连续批处理、chunked prefill、并发上限)决定;典型表现是"跑着跑着 OOM、请求被抢占、吞吐抖动"。
  • 二者不是 CUDA/PyTorch 官方术语,而是运维口径;官方拆法是 weights / activations / KV cache / 框架与 context 开销。

推理场景构成(以 vLLM 类服务为例)

  • 静态部分:模型权重(含 FP8/INT4 的 scale/zero-point)、CUDA context 与已加载 kernel 镜像(cuBLAS/cuDNN/FlashAttention)、cuBLAS/cuDNN workspace 上限、通信库 buffer(NCCL)、CUDA Graph 捕获池、进程级固定开销。
  • 动态部分:prefill 激活峰值(与 batch×seq 相关的中间张量)、KV Cache(∝ 并发序列数 × 序列长度 × 每 token 字节数)、attention split-K 等临时 workspace、以及 caching allocator 预留但未使用的碎片。

生产排查

  • torch.cuda.memory_allocated()(真实张量)vs memory_reserved()(缓存池)区分"用了"和"占着";nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv 看进程总占用(含 context 与非 PyTorch 分配)。
  • vLLM 在 profiling 后按 gpu_memory_utilization(默认 0.9)扣掉静态部分,剩余按 block 分配给 KV Cache;静态部分估错(权重精度、context 开销)会直接压缩可用 KV 池 → 抢占与排队。
  • 调参顺序:先压动态峰值(max_num_seqs、max_num_batched_tokens、chunked prefill),再考虑提 gpu_memory_utilization,最后才是换卡/换精度。

依据:vLLM CacheConfig 文档(gpu_memory_utilization 默认 0.9);vLLM 源码 vllm/v1/worker/gpu_worker.py::determine_available_memory;PyTorch torch.cuda.memory_* 文档。

2. torch.cuda.set_per_process_memory_fraction() 的作用与多进程隔离的生产意义

要点:

官方语义(已核对源码 docstring)

  • 签名 torch.cuda.set_per_process_memory_fraction(fraction, device=None) -> None;fraction 必须是 float,范围 0~1,否则分别抛 TypeError / ValueError;device=None 表示当前设备。
  • 作用是限制本进程 PyTorch caching allocator 在该 device 上的可分配上限 = 该 device 可见总显存 × fraction;越限时由 allocator 抛 CUDA OOM(不是驱动层硬限)。
  • 官方提示:“the total available free memory is less than the total capacity”——比例是按标称可见容量算的,别按 free 估。
  • 边界:只约束 PyTorch 分配器。CUDA context、其他框架(TensorRT/自研 CUDA)、以及 allocator 之外的分配都不受约束;必须在进程开始分配显存前调用。

多进程隔离的生产意义

  • 单卡多进程(多实例、多租户、一个容器内多 rank)时,给每个进程设 fraction,防止单个进程吃满整卡导致邻居 OOM 或整机抖动;是 MIG/MPS 之外最轻量的软隔离手段。
  • 与 CUDA_VISIBLE_DEVICES / MIG 组合时注意基数:fraction 相对"可见显存"取比例,MIG 实例里可见容量已缩小。
  • 与 PYTORCH_CUDA_ALLOC_CONF(expandable_segments、max_split_size_mb)区别:后者调分配器行为与碎片,不设上限。
  • 同类替代:vLLM 用 gpu_memory_utilization 做"每实例上限",并明确支持同卡两实例各设 0.5。
1
2
# 示意:单卡起 2 个进程,各限 45%(需在首次分配前调用)
CUDA_VISIBLE_DEVICES=0 python -c "import torch; torch.cuda.set_per_process_memory_fraction(0.45, 0); print('ok')"

依据:PyTorch 源码 torch/cuda/memory.py(set_per_process_memory_fraction / get_per_process_memory_fraction);vLLM CacheConfig 文档。

3. 分布式预训练为何必须统一 RNG Seed;DP 与 TP 对初始化的种子处理差异

要点:

为什么必须统一

  • 权重初始化必须一致:所有 DP 副本必须从逐位相同的初始权重出发,否则梯度平均、优化器状态、checkpoint 语义全部失效。
  • 可复现与续训:数据顺序、dropout、随机增广、MoE 路由都必须能从相同 RNG 状态重放;激活重计算(activation recomputation)会重放前向,RNG 不可复现 → 梯度不一致。
  • 调试价值:只有"同一 seed 必得同一 loss 曲线",才能把性能问题与随机性区分开。

DP 与 TP 的差异(Megatron-LM 实现,已核对源码)

  • _set_random_seed:seed = seed_ + 100 * pp_rank(保证不同 PP stage 不同种子);仅当 data_parallel_random_init=True 时再 + 10 * dp_rank。默认不含 dp_rank → 所有 DP rank 初始化完全一致。随后 random.seed / np.random.seed / torch.manual_seed 全部设上,再调用 TP 的 RNG 初始化。
  • model_parallel_cuda_manual_seed:offset = seed + 2718,tensor_model_parallel_seed = offset + tp_rank,data_parallel_seed = seed,MoE 另设 expert_parallel_seed = seed + 1024 + 100*ep_rank + etp_rank;三条流分别注册为 data-parallel-rng / model-parallel-rng / expert-parallel-rng。
  • 含义:DP 副本共享同一 seed(初始化一致);TP 组内参数分片来自同一份初始化(用同一 default 流生成完整 master weight 再切片,所以初始化不能按 tp_rank 偏移),但 TP 区域内的 dropout 等随机性按 tp_rank 偏移 → 各分片 mask 不同,而 DP 组内同一 TP rank 的 mask 一致。
  • 数据侧:默认按 DP rank 偏移采样种子(--use-same-sampling-seed-across-dp-ranks 关闭 offset_sampling_seed_by_dp_rank,默认开启偏移),保证各 rank 看到不同数据。

工程坑

  • 种子必须在任何 CUDA/模型构造之前设置;之后不要再用 torch.cuda.manual_seed_all 覆盖;DataLoader worker、增广、MoE gating 都要走 tracker。
  • 要严格确定性需 torch.use_deterministic_algorithms(True) + CUBLAS_WORKSPACE_CONFIG=:4096:8,并接受部分算子变慢。

依据:Megatron-LM 源码 megatron/training/initialize.py::_set_random_seed、megatron/core/tensor_parallel/random.py::model_parallel_cuda_manual_seed、megatron/training/config/common_config.py::RNGConfig、megatron/training/arguments.py。

4. Kubernetes GPU 集群中的"僵尸 GPU 进程"及其排查与清理

要点:

现象与定义("僵尸进程"是运维说法,非官方术语)

  • 容器/训练进程已退出或被 kill(Pod 已删除或 Terminated),但 GPU 上残留未回收的 CUDA context/device 占用:显存不释放、util 卡住或 SM 无响应;nvidia-smi 里仍能看到进程,但宿主机 /proc/<pid> 已不存在或属于已死容器。
  • 常见成因:SIGKILL 后驱动未回收、容器 runtime 异常、内核态 hang(如 uvm 卡死)、XID 事件(掉卡类)、PID namespace 不一致导致"看不见也杀不掉"。

排查

1
2
3
4
nvidia-smi                                                       # 进程表 + 显存
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv
lsof /dev/nvidia* 2>/dev/null | head -20 # 或 fuser -v /dev/nvidia*
dmesg -T | grep -i xid | tail -20 # 驱动侧 XID
  • 交叉验证:kubectl describe node <node> 的 nvidia.com/gpu 分配数 vs 实际 Pod;宿主机 ps -ef 找进程;nvidia-smi 报 “No running processes found” 但显存仍被占 → 多为 context 残留。

清理顺序(由轻到重)

  1. 宿主机 kill -9 <pid>;确认 /dev/nvidia* 已无持有者。
  2. 确认无任何应用占用该 GPU(含 CUDA、图形、其他 nvidia-smi 实例)且以 root 执行 nvidia-smi --gpu-reset -i <idx>。官方限制:Ampere 起 NVLink 连接的 GPU 可单独 reset,Ampere NVSwitch 系统需 Fabric Manager(Hopper 及以后不再依赖),且"GPU reset is not guaranteed to work in all cases"、“not recommended for production environments”。
  3. 仍无效 → 重启节点(冷启动);XID 79/119/120/154 这类"掉卡/需 reset"事件基本只能重启。

K8s 侧

  • device plugin 通过 NVML 事件集监听 XID critical error / double-bit ECC 等并把设备标记 unhealthy(可用 DP_DISABLE_HEALTHCHECKS / DP_ENABLE_HEALTHCHECKS 调整忽略或启用的 XID 列表),表现为分配失败或节点可用 GPU 数变少。
  • 标准动作:cordon + drain 节点 → 清残留进程 → 重新健康检查 → uncordon;把"GPU 健康"接入节点自动隔离(taint/告警)以免反复派任务。

依据:NVIDIA nvidia-smi 官方文档(--gpu-reset、--query-compute-apps);NVIDIA/k8s-device-plugin 源码 internal/rm/health.go。

5. 什么是 CUDA Context;为何单卡起过多 Python 进程会让可用显存大幅缩水

要点:

机制

  • CUDA context 是进程(确切说是线程所属)与某张 GPU 之间的运行上下文:包含 GPU 虚拟地址空间与页表、已加载的 kernel 镜像(cubin/SASS、常量内存、全局数据)、流/事件/内存分配器等状态。Runtime API 首次调用时隐式创建 primary context,进程内共享。
  • 每个进程各有自己的 context;硬件以时间片轮转调度不同 context 的工作队列,不同 context 的 kernel 不能并发执行——所以既吃显存又掉利用率,还有上下文切换开销。

为什么显存缩水

  • 每个 context 都有一份独立的 kernel 镜像、常量/全局数据、页表与驱动预留;NVIDIA 论坛口径:每个用 CUDA 的进程都需要一个 context,“occupies a few hundred MB(具体随驱动版本与 GPU 型号变化)”,早期实测约 200 MB/进程量级。
  • 因此单卡起 N 个独立进程,会先被吃掉 N × 数百 MB 的"隐形显存",再叠加各进程自己的权重/激活;再算上框架加载的 cuBLAS/cuDNN/flash-attn kernel 镜像,缩水更明显。
  • 排查方法:nvidia-smi --query-compute-apps=pid,used_memory --format=csv 的进程占用 减去 torch.cuda.memory_allocated(),差值即 context + 非 PyTorch 分配。

缓解

  • MPS(Multi-Process Service):多进程共享一个 CUDA context,Volta 起 client 直接向 GPU 提交工作,减少时间片切换与重复 context 开销;MIG 则是硬件分区,每实例独立 context + 独立显存,隔离性最好。
  • 减少进程数(一个进程内多 device context)、及时销毁不用的 context(torch.cuda.empty_cache() 只能回收 allocator 缓存,回收不了 context)。
  • CUDA_MODULE_LOADING=LAZY:只加载实际用到的 kernel,显著降低 context 显存与启动时间(驱动级 11.7 / runtime 级 11.8,需 R515+ 驱动);注意它把 kernel 加载推迟到首次执行,显存已吃满的程序要留余量。

依据:NVIDIA MPS 官方文档(Architecture);NVIDIA Developer Forums(per-process CUDA context 占用);CUDA C++ Programming Guide “Lazy Loading”。

6. NCCL_CROSS_NIC=1 与 NCCL_NET_GDR_LEVEL=5 的硬件前提与生产收益

要点:

⚠️ 题目本身有问题:NCCL_NET_GDR_LEVEL=5 不是官方文档化的取值。官方文档只列 LOC/PIX/PXB/PHB/SYS(整数值 0/1/2/3/4),并写明"Values greater than 4 will be interpreted as SYS";NCCL 源码里旧式数字通过 levelsOldToNew[] = {PATH_LOC, PATH_PIX, PATH_PXB, PATH_PHB, PATH_SYS, PATH_SYS} 映射,即 5 等价于 SYS(= 永远启用 GDR),>5 会被 clamp 到 5。生产上建议直接写 NCCL_NET_GDR_LEVEL=SYS,语义更清晰、不会被后续版本改掉。

NCCL_CROSS_NIC=1

  • 官方语义:0 = 同一 ring/tree 始终用同一张 NIC(避免跨 rail);1 = 允许同一 ring/tree 使用不同 NIC;2 是默认值(尽量同 NIC,但性能更好时允许不同)。源码 NCCL_PARAM(CrossNic, "CROSS_NIC", 2)。
  • 硬件前提:节点内 ≥2 张 HCA(官方原文"This has no effect on systems with only one NIC");且拓扑允许交叉——官方描述 1 适合"all of a node’s NICs attach to one switch"的平面式组网。
  • 生产收益:让同一 communicator 的不同 channel/ring 分散到多张 NIC,避免单口热点,提升跨节点聚合带宽(all-to-all 类收益最明显)。
  • 反例:严格 rail-optimized 组网(每 NIC 独立 rail、跨 rail 走慢速上行)应设 0,否则把流量推到跨 rail 路径反而变慢;默认 2 是折中。

NCCL_NET_GDR_LEVEL=5(= SYS)

  • 前提:GPU 与 NIC 双方都支持 GDR(源码 ncclTopoCheckGdr 检查两侧 gdrSupport)、RDMA HCA(ConnectX-4 及以上)、内核 peer-memory 支持(源码 ncclIbPeerMemSupport(),走 nv_peermem 或 DMA-BUF)、且拓扑能被 NCCL 识别。
  • 默认行为:未设置时源码取 netGdrLevel = PATH_PXB,即路径距离 > PXB(跨 NUMA)时 NCCL 会静默关闭 GDR;设 5/SYS 则强制启用。
  • 收益:RDMA 直接读写显存,省掉 host bounce buffer 的两次 PCIe 拷贝 → 降延迟、提有效带宽、省 CPU 与 host 内存带宽;在容器内拓扑不可见、路径被误判为 PHB/SYS 的场景,显式设置可避免降级。
  • 风险:若 NIC 与 GPU 确实跨 NUMA 且只有 PCIe,强制 GDR 可能不如走 host 内存(官方在 NCCL_NET_GDR_READ 条目下明确"reading directly from GPU memory … is known to be slightly slower … on some platforms, such as PCI-E");send 方向的 GDR 读还受 NCCL_NET_GDR_READ 控制。
  • 验证:NCCL_DEBUG=INFO 看初始化日志中通道行是否带 /GDRDMA(源码打印 ... via NET/IB/<dev>/GDRDMA);nvidia-smi topo -m 看 GPU-NIC 亲和;用 NCCL_NET_GDR_LEVEL=0 做 A/B 对照(NCCL 自身也给了这条 HINT)。

依据:NVIDIA NCCL 官方环境变量文档(NCCL_CROSS_NIC、NCCL_NET_GDR_LEVEL、NCCL_NET_GDR_READ);NCCL 源码 src/graph/paths.cc、src/graph/search.cc、src/transport/net.cc、src/transport/net_ib/。

7. DeepSeek-V4 的 MLA 相比 GQA 为何能降低长文本 KV Cache 与跨节点搬迁压力

要点:

⚠️ 题目本身有问题:DeepSeek-V4 确实存在(2026-04-24 官方发布预览版:V4-Pro 1.6T 总参/49B 激活、V4-Flash 284B/13B 激活、标配 1M 上下文、开源权重),但它不采用 MLA。官方公告写的注意力是"Token-wise compression + DSA (DeepSeek Sparse Attention)“;V4-Pro 官方 config.json 中已无 MLA 的核心参数 kv_lora_rank,改为 num_key_value_heads: 1 + compress_ratios(SWA/CSA/HCA 交替的序列维压缩)+ index_topk: 1024。MLA 是 V2/V3 的机制(V3.2-Exp 在 MLA 上加了 DSA 稀疏选择),V4 用"序列维压缩"替代了 MLA 的"维度维低秩压缩”,仅 Q 侧仍保留低秩(q_lora_rank: 1536、o_lora_rank: 1024)。下面按 V2/V3 的 MLA 作答——这才是"MLA vs GQA"的正确事实。

MLA 的机制(V2/V3)

  • 每个 token 的 K/V 不按 head 展开缓存,而是压成一个共享低秩 latent c_KV(kv_lora_rank=512)+ 一个解耦 RoPE 分量(qk_rope_head_dim=64),每 token 每层只缓存 576 个元素。
  • 推理时把上投影矩阵 W_UK/W_UV 吸收进 W_Q/W_O,attention 直接在 latent 上算,完整 K/V 永不物化。

与 GQA 的对比

  • GQA 靠"减少 KV head 数"省显存(如 8 组),但每 token 仍需缓存 K 和 V 两份:2 × n_kv_heads × head_dim × 层数 × 字节数。
  • 官方论文(arXiv:2505.09343)表格:DeepSeek-V3 70 KB/token(61 层),LLaMA-3.1-405B 516 KB/token,Qwen-2.5-72B 327 KB/token → 约 7.3 倍差距,且 MLA 不减少 attention head 数(128),质量损失远小于"砍 KV head"。

为什么长文本与跨节点网络压力骤降

  • KV 显存与搬运量 ∝ 每 token KV 字节数 × 序列长度 × 并发数。1M token 单序列:MLA 量级约 70 GB(8 卡节点仍可容纳),传统 GQA 量级约 516 GB → 必须跨节点分片,KV 长期在网络上来回搬。
  • PD 分离场景:prefill 节点算完要把 KV 传给 decode 节点;大 TP/EP 下 KV 分片还要跨节点交换——每 token 字节数降 7 倍,网络搬迁量同比下降,使"KV 跨节点搬"从不可行变为可行,也是百万上下文能被"普惠"的底层原因。
  • 与 DSA 的分工:DSA(top-k 稀疏选择)降的是计算量,MLA 降的是缓存与搬运量,两者正交。

依据:DeepSeek-V2 论文(arXiv:2405.04434)、DeepSeek-V3 官方技术报告与 Insights 论文(arXiv:2412.19437、arXiv:2505.09343);DeepSeek 官方 V4 发布公告;HuggingFace deepseek-ai/DeepSeek-V4-Pro 官方 config.json。

8. 什么是"合并访存(Coalesced Memory Access)",未合并对显存带宽利用率的影响

要点:

机制

  • 一个 warp 的 32 个线程的访存会被硬件按 32 字节 sector 为最小事务单位合并;L1 cache line 为 128 字节 = 4 个 sector。
  • 连续线程访问连续地址(线程 i 访问 base + 4i 的 float):整个 warp 覆盖 128 字节,落在 4 个 sector,1 次请求 : 4 个 sector,每个字节都被用到,效率 100%。
  • 跨步/随机访问(如 stride = 32 个元素):每个线程落在不同 sector,1 次请求 : 32 个 sector,每次只用到其中 4 字节。

致命后果

  • 有效带宽直接除以 8(最坏 1/32):NVIDIA 官方博客实测 NCU 显示"only 4.0 of the 32 bytes transmitted per sector are utilized",DRAM 的 sectors_read/sectors_write 相差 8 倍。
  • HBM 物理带宽是硬上限(H100 HBM3 约 3.35 TB/s 量级),访存密集 kernel(GEMM epilogue、attention、embedding gather、MoE scatter)会提前撞上带宽墙,算力闲置、GPU 利用率塌陷。
  • 附带伤害:L1/L2 被无效数据污染(cache thrashing),命中率进一步下降;功耗与延迟上升,且问题在 profiler 之外往往不可见。

常见来源与修复

  • 来源:transpose 后未 materialize 的 stride 访问、paged KV Cache 的非连续 block、gather/scatter 索引、未对齐起始地址(跨 sector 边界)、AoS 结构体布局。
  • 修复:保证最内层维度连续(NCHW→NHWC、AoS→SoA)、向量化访问(float4/128-bit)、让 block/切片对齐 32B/128B、padding 对齐。
  • 排查:Nsight Compute 关注 “sectors per request”、“Global Load/Store Efficiency”、“DRAM throughput %” 三个指标。

依据:NVIDIA 官方技术博客 “Unlock GPU Performance: Global Memory Access in CUDA”;NVIDIA Developer Forums 关于 128B cache line / 32B sector 结构的说明。

9. vLLM 等推理框架中的"抢占(Preemption)"机制与两种策略

要点:

机制

  • 连续批处理下,调度器每个 step 都要为 running 请求分配 KV Cache block。当 block 池耗尽(allocate_slots 返回 None),必须"抢占"某个正在运行的请求(按优先级/最晚到达选择),释放其 block 给当前请求,被抢占者回到 waiting 队列。
  • 抢占不丢请求,只带来延迟毛刺:TTFT/ITL 抖动、可能的重复计算、吞吐下降。它是过载保护机制,不是 bug。

V0 的两种策略(已核实配置项)

  • SchedulerConfig.preemption_mode: Literal['swap', 'recompute'](vllm/core/scheduler.py 的调度逻辑;CLI 为 --preemption-mode),默认 recompute:
    • RECOMPUTE(重计算,默认):block_manager.free() 直接释放该请求的 GPU blocks,reset_state_for_recompute() 清零已算 token,请求回到 waiting;恢复时重新 prefill(prefix caching 可复用部分)。省显存、省 PCIe 带宽,代价是重复算力(长序列代价高)。
    • SWAP(交换):block_manager.swap_out() 把该请求的 KV blocks 拷到 CPU 侧 swap 空间(swap_space,默认 4 GiB/GPU),请求进 swapped 队列,恢复时 swap_in 拷回。省算力,代价是 PCIe/NVLink 传输。
    • 官方 docstring 明确:“We use recomputation by default since it incurs lower overhead than swapping”;例外是 beam search 等"一个 sequence group 含多个 sequence"的场景,recompute 不支持 → 改用 swapping。

⚠️ 题目本身有问题(口径过时):上述"两种策略"是 V0 的事实。vLLM V1(当前默认引擎)已移除 GPU↔CPU 的 KV Cache 交换——官方 V1 指南原文"vLLM V1 no longer requires KV cache swapping to handle request preemptions",特性表把 “GPU <> CPU KV Cache Swapping” 标为 Removed;V1 scheduler 只有重计算一条路径(_preempt_request:置 RequestStatus.PREEMPTED、num_computed_tokens = 0、放回 waiting 队首)。

生产观测与调参

  • 指标:vllm:num_preemptions(累计计数)、vllm:request_num_preemptions(直方图),日志行 “Preemptions: N”。
  • 调参:降 max_num_seqs / max_num_batched_tokens、适度提高 gpu_memory_utilization(注意同卡其他进程)、开启 prefix caching(降低 recompute 成本)、chunked prefill 平滑显存曲线。

依据:vLLM 官方 V1 用户指南(Swapping 已移除);vLLM vllm/config/scheduler.py API 文档(PreemptionMode = Literal['swap','recompute']、默认 recompute 的 docstring);vLLM 源码 vllm/v1/core/sched/scheduler.py::_preempt_request、vllm/v1/metrics/loggers.py。

10. 构建 RDMA 无损网络时为何必须严格配置 DSCP 到 CoS 的优先级映射

要点:

为什么必须映射

  • PFC(IEEE 802.1Qbb)是 L2 的按优先级流控机制:CoS/PCP 只有 3 bit(8 个优先级),暂停帧只作用于某个优先级。
  • RoCEv2 是 L3(UDP 4791)承载,跨 L3 后 L2 的 PCP 不再传递,必须靠 IP 头的 DSCP(6 bit)在每一跳重新映射到设备内部的 priority/CoS。因此映射必须在整条路径(NIC→Leaf→Spine→Leaf→NIC)上一致,NIC 侧与交换机侧也要一致。
  • NVIDIA 参考配置(Cumulus/Spectrum 默认):RoCE 数据 DSCP 26 → priority 3(该优先级开 PFC 并分配无损 buffer),CNP(拥塞通知)DSCP 48 → priority 6(严格优先级),其余流量 → priority 0。NIC 侧 mlnx_qos 的 dscp2prio、trust 模式(trust dscp)与 pfctx/pfcrx 必须两端一致(官方原文:“The values of pfctx and pfcrx must be identical”),也可用 dscp_force 强制打标。

不配置 / 配不一致的严重后果

  • 报文落到默认优先级(0)→ 该队列没有 PFC、没有无损 buffer,无损语义彻底失效;拥塞(典型 incast)时直接丢包 → RoCEv2 go-back-N 重传、QP 重试/超时 → 集合通信从"变慢"恶化到"hang"(NCCL watchdog 超时)。
  • 半配置(部分交换机或部分 NIC 映射不同)→ PFC 在错误的优先级上发暂停帧,把同优先级的其他业务一起停住(head-of-line blocking);极端情况下形成 PFC 风暴/死锁:链路长时间被 pause、缓冲区循环依赖无法解除,整片网络停摆。
  • CNP 未走严格优先级或 ECN 阈值/标记不一致 → DCQCN 闭环断裂:发送端要么不降速继续丢包,要么过度降速浪费带宽。
  • 可观测性被破坏:PFC 计数器只在启用了 PFC 的优先级上可见(官方原文),映射错了连计数都看不到,故障只能靠猜。

排查与落地

  • NIC 侧:mlnx_qos -i <dev> 看 trust 模式、dscp2prio、PFC 使能位;ethtool -S <dev> 看 per-priority 的 pause 计数与丢包;NIC 侧还可看 PFC 风暴计数器 tx_pause_storm_warning_events / tx_pause_storm_error_events,并用 pfc_stall_prevention(默认 8 s,auto 模式 100 ms)做兜底。
  • 交换机侧:核对 DSCP→TC/priority 映射表、PFC 使能优先级、ECN 阈值与队列水位,确保全网一套配置。
  • 主机与通信库对齐:NCCL 的 RoCE traffic class 字段(NCCL_IB_TC,默认 AUTO 即未配置时为 0)需与交换机映射的 DSCP 对齐,否则改了半天交换机而主机仍用默认标记。

依据:NVIDIA Cumulus Linux “RDMA over Converged Ethernet - RoCE” 文档(DSCP 26→priority 3、DSCP 48→priority 6、PFC/ECN 默认);NVIDIA “HowTo Configure RoCEv2 Lossless Fabric”(trust DSCP、pfctx/pfcrx 一致);NVIDIA DOCA “Flow Control”(PFC 风暴/停滞防护计数器);NCCL 官方环境变量文档(NCCL_IB_TC)。


六、简答题(第 11–20 题)

11. Checkpoint 保存引起的慢存储 I/O 阻塞与分层存储设计

要点:

物理机制(为什么它会阻塞)

  • 同步 Checkpoint 的语义是:所有 rank 到达保存点后停止前向/反向,把参数、梯度、优化器状态、LR 调度器、RNG、数据加载器位置全部落盘,写完才恢复计算。体积口径:混合精度 + Adam ≈ 16 bytes/参数(fp16 参数 2 + fp16 梯度 2 + fp32 master 4 + m 4 + v 4),175B ≈ 2.8 TB。
  • 千卡集群里数千个进程在同一秒开始写,DeepSpeed/Megatron 默认 per-rank 一个分片文件 → 单次 Checkpoint 产生数百到数千个文件,瞬间打满元数据服务器(create/open/close/rename)+ 存储聚合带宽。若存储无法在分钟级吸收完这次写入,训练被迫暂停。
  • 写放大有五个来源:元数据放大(文件数)、跨 OST 未对齐写(false sharing)、POSIX page cache 冗余拷贝、EC 重建读放大(≈k 倍)、torch.save 单线程 pickle 序列化。
  • 损耗可量化:设每步 T_step、每 K 步存一次、同步写耗时 T_ckpt,额外吞吐损耗 = T_ckpt / (K·T_step + T_ckpt)。典型数字:T_step 1–2 s、T_ckpt 5–10 min、K=500 → 约 42% 的算力被空转吃掉,且会顺带打满存储、影响同集群其他租户(噪声邻居)。

分层存储的三级流水(唯一可行解)

1
2
GPU HBM --DMA--> CPU pinned --独立进程--> 本地 NVMe --限速后台--> 并行FS/对象存储
(同步段 <100ms) (异步 1-2s) (完全后台 30-60min)
  1. L0 GPU HBM → CPU pinned memory(唯一同步段):预分配 pinned 池,用 cudaMemcpyAsync + 独立 CUDA stream;利用 fwd/bwd 期间张量不可变,把 D2H 拷贝打散到整个迭代中与计算 kernel 交错(DataStates-LLM,HPDC’24 的做法)。
  2. L1 CPU → 本地 NVMe(异步段):序列化与落盘必须放独立进程而非线程(GIL 隔离);大块写用 O_DIRECT/io_uring 绕过页缓存;若节点有 NVMe 且启用 GPUDirect Storage,L0+L1 可合并为一次 HBM→NVMe 的 DMA。
  3. L2 本地 NVMe → 共享存储/对象存储(完全后台):错峰 + 限速,把瞬时风暴摊平成长尾流。限速值不能设 0(否则永远写不完),且必须在存储端做全局聚合限速,单节点限速挡不住多节点并发。
  • 一致性三件套:全局 barrier → 写临时目录 → 原子 rename → 最后写 done 标记(顺序不能反,否则恢复时读到"有标记但内容为空"的损坏 Checkpoint);NFS 的 rename 不保证原子性。
  • 分级频率:高频本地 Checkpoint(每 1000–2000 步,供节点级故障恢复,本地保留 2–3 份)+ 低频全局归档(每 2–4 小时,供灾难恢复)。
  • 关键参数:NeMo/Megatron 的 dist_ckpt_parallel_save: True(每 DP rank 独立写自己的 shard)与 dist_ckpt_torch_dist_multiproc(每 rank 额外保存进程数,默认 2);PyTorch 侧对应 torch.distributed.checkpoint 分片保存。

为什么全局存储不需要匹配 GPU 峰值带宽:某厂商对 40 个 1024+ GPU 生产训练 run(18 个集群、>85,000 次 Checkpoint)的统计显示,异步 Checkpoint 落到全局存储的速率是 50–200 GB/s,与模型规模无明显相关性,万亿参数模型也远低于 1 TB/s。前提是绝大多数故障是单卡/链路级(Meta Llama 团队的研究:整机故障只占故障总数的约 5%),可从节点本地 Checkpoint 恢复。

依据:NVIDIA/IBM DataStates-LLM(arXiv:2406.10707, HPDC’24);ZeRO 论文的 16Ψ 显存口径;NeMo/Megatron 分布式 Checkpoint 文档;PyTorch torch.distributed.checkpoint 与 async_save(2.4 引入)文档;VAST Data 生产集群 Checkpoint 量化分析。

12. 未开启客户端断开检测为何引发"显存滚雪球式崩溃"

要点:

物理机制(一条僵尸请求如何吃光 KV Cache)

  • 流式推理(SSE/HTTP chunked)中,客户端断开(关页面、客户端超时、网络抖动、网关主动断连)后 TCP 层会到达 FIN/RST,但服务端如果不在生成循环里主动检测断开状态,就会继续为这条请求逐 token 解码。
  • 在连续批处理下,这条请求始终留在 running batch:每步仍为它算一次 attention,它占用的 KV Cache 块一直不释放。KV Cache 是按块管理的固定大小池(PagedAttention 的 block pool),池子大小在启动时由 gpu_memory_utilization 决定,不会自己长大。
  • 雪崩链条:僵尸请求占块 → 可分配块减少 → 调度器无法接纳新请求或被迫抢占 + 重算(preemption + recompute),吞吐塌陷 → 排队变长 → 客户端超时重试 → 请求数放大 → 更多 KV 被占 → 直至 gpu_cache_usage_perc 打满、CUDA OOM / EngineCore 崩溃。长上下文与投机解码会放大单请求的 KV 占用,使雪崩来得更快。
  • 二次放大(PD 分离场景):decode 端释放 KV 槽位必须与 prefill 端协商。SGLang 官方安全更新(2026-10)披露的跨请求数据暴露事件,根因正是客户端断开触发 abort,而 RDMA 写入仍在飞行中,槽位被提前释放并分配给了新请求——即"释放得太早"会造成 KV 污染,所以正确做法是 hold-then-free:等各 prefill rank 确认(ABORT_ACK)后再释放,并用超时兜底。

正确做法

1
2
3
4
5
# vLLM 官方文档给出的标准写法(示意)
async for request_output in results_generator:
if await request.is_disconnected():
await engine.abort(request_id) # 关键:显式中止,触发 KV 块回收
final_output = request_output
  • 网关/负载均衡层要做连接级取消透传,不要让断连在代理处被吞掉;给每条请求设 max_tokens 与整体超时,作为兜底。
  • 必须监控:num_running_reqs / num_queue_reqs 是否只增不减、KV Cache 使用率、单请求驻留时长;对"running 数不降但输出 token 数不涨"设告警——这正是僵尸请求的指纹。

依据:vLLM 官方文档 AsyncLLMEngine.abort() 与 request.is_disconnected() 示例(entrypoints/api_server.py);SGLang 官方安全更新(2026-10,KV 槽位提前释放导致的跨请求暴露)与其 deferred KV release 修复。

13. Single-Bit 与 Double-Bit ECC 的运维处理策略本质区别

要点:

先确认命令与口径:nvidia-smi -q -d ECC 是合法用法,输出分 Volatile(本次驱动加载/复位后的累计,复位即清零)与 Aggregate(设备全生命周期累计)两套计数,各自再按 Single Bit / Double Bit 拆分,并按硬件单元细分(Device Memory、Register File、L1/L2 Cache、Texture Memory…)。做历史趋势必须用 Aggregate,判断"这张卡当前状态"才用 Volatile,两者混用会得出完全相反的结论。规模化采集建议走 DCGM,nvidia-smi --query-remapped-rows 单独看行重映射。

SBE(可纠正,Single Bit):数据无损,是"预警信号",处理策略是观察而非下线

  • ECC 硬件自动纠正,应用无感、数据正确。因此不能设绝对值告警——一旦设了,一个集群会刷出告警风暴。正确做法是速率/增量告警(如 15 分钟滑动窗口内新增超过阈值),看增长斜率。
  • 关键判据是它有没有触发行重映射:HBM 有备用行,重映射是正常的自愈机制。Xid 63 表示"重映射条目已成功写入 InfoROM",属于信息类事件(与之相对,Xid 64 表示重映射记录失败,这个要关注)。只有重映射行数耗尽/失败,才进入 RMA 流程。
  • 处置:标记观察、纳入节点健康分;不隔离、不换卡;配合 nvidia-smi --query-remapped-rows 跟踪 Remapped Rows 与 Row Remapping Failure。注意 retire 页数是持久化的,重启不会归零。

DBE(不可纠正,Double Bit):数据已损坏,是"停服信号",处理策略是零容忍

  • 错误无法被纠正,读出的数据已经是错的。训练对显存一致性要求极高,静默数据错误会污染模型参数/梯度且难以察觉(表现为 loss 异常、结果静默出错)。应用层会收到 uncorrectable ECC error,驱动侧对应 Xid 48(NVIDIA 另有 Xid 171 = DRAM、Xid 172 = SRAM 区分错误来源)。
  • 处置:立即终止任务 → 隔离该 GPU(不可复位后继续复用)→ 走 RMA。不能"复位一下再跑",因为 DBE 的语义是数据完整性已被破坏,而训练不会自检。
  • 自动化边界:DBE 可以接入自动化(告警 → 调用调度系统 API 下线节点 → 人工复核),但换卡决策建议保留人工确认;同时要有熔断——短时间内批量 DBE 说明是共性环境问题(供电/散热/批次),自动批量隔离会打穿备用池。

一句话区别:SBE 影响的是这张卡的未来寿命(观察趋势、允许继续服务);DBE 影响的是已经产出的数据是否正确(立即止损、不允许继续服务)。二者的判据、告警方式、自动化权限三者完全不同。

依据:NVIDIA XID Errors 官方文档(Xid 48 / 63 / 64 条目,Xid 171 DRAM、Xid 172 SRAM);NVIDIA GPU Memory Error Management(Row Remapping、RAS Repair);NVML/nvidia-smi -q -d ECC 的 Volatile/Aggregate 语义。

14. FlashDecoding 的切分逻辑,及它与 FlashAttention-2 解决并行度瓶颈的差异

要点:

FlashAttention-2 的并行度来源与它的天花板

  • FA2 的并行维度是 batch × head × query 分块,加上块内 warp 级的任务重新划分(减少非 matmul FLOPs、减少 shared memory 通信)。这在训练/prefill 阶段够用,因为 query 长度本身就很大。
  • 但 decode 阶段 query 长度 = 1:每个 (batch, head) 只有一行 query,沿 query 方向根本切不动,并行度被压缩到 batch × heads。batch 小、上下文长时(长上下文场景的常态),GPU 占用率可以低到 “A100 108 个 SM 里用不到 1%”(官方博客原话:as little as <1% of an A100's 108 SMs)。此时算力不是瓶颈,并行度才是。

FlashDecoding 新增的维度:KV 序列长度(split-KV)

  1. 切:把 KV Cache 沿序列维度切成若干 chunk;
  2. 并行算:query 与每个 chunk 并行做 FlashAttention,除局部输出外额外写出每行的 log-sum-exp;
  3. 归约:第二个 kernel 用 log-sum-exp 对各 split 的结果做 rescale 后合并(split-K 式归约)。

softmax 因此变成两级可迭代计算:split 内部(同 FlashAttention)+ split 之间(最终归约)。这样batch=1 也能把 GPU 打满(只要上下文足够长),且耗时在上下文长度上近似恒定(官方数据:CodeLlama-34B 长序列端到端解码最高 8× 加速,attention 本身相对 FlashAttention v2 最高 50×;seqlen 131k 时 ~106 µs vs FA2 的 ~4592 µs)。

两者关系与工程取舍

  • 互补而非替代:FlashDecoding 的每个 split 内部用的就是 FlashAttention-2 的 kernel;FA2 优化的是"单次 kernel 效率与块内并行",FlashDecoding 增加的是"原本不存在的并行维度"。
  • split 数要自适应:上下文短时 split 带来的额外归约开销大于收益,所以需要运行时 dispatcher 自动在 FlashAttention / FlashDecoding 之间选择(FlashAttention 包 ≥ v2.2、xFormers ≥ v0.0.22 已内置自动分发与 Triton 回退)。生产上真正要盯的是长上下文 + 小 batch 的组合——这才是 FlashDecoding 生效的窗口。

依据:PyTorch 官方博客《Flash-Decoding for long-context inference》(Tri Dao、Daniel Haziza、Francisco Massa、Grigory Sizov,2023-10);FlashAttention-2 论文/README 关于并行维度与 work partitioning 的表述。

15. 仅靠 CPU/Memory Request 与 Limit 为何挡不住 GPU 算力噪声邻居

要点:

根本原因:GPU 算力不在 cgroup 的管辖范围内

  • CPU/Memory 的 request/limit 能生效,是因为 kubelet 把它们翻译成 cgroup 的控制器(cpu.max/cpu.weight、memory.max),由内核调度器强制执行。
  • GPU 走的是另一条路:Device Plugin 把 nvidia.com/gpu 表达成"整数个同质设备",容器拿到的只是挂载进来的 /dev/nvidia* 与驱动库。"1 张卡"没有对应的带宽/算力配额语义——调度器只知道"这个 Pod 有 1 张卡",不知道"这 1 张卡只许用 1/8 的 SM 和 1/4 的 HBM 带宽"。
  • cgroup 的 devices 控制器只管"能不能访问",没有 CUDA 时间片控制器,也没有显存带宽控制器。NVIDIA 没有把 SM 时间片暴露给 cgroup v2。

因此同卡邻居可以造成的四类伤害

  1. 算力抢占:SM 时间片轮转由驱动做,没有公平性保证,邻居的持续大 kernel 会让你的 kernel 排队;
  2. 访存争抢:HBM 带宽与 L2 被邻居打满,你的算子直接掉速(访存密集算子受影响最大);
  3. 显存无隔离:显存是先到先得,一个进程把显存吃满,同卡其他租户直接 OOM;
  4. 共享故障域:邻居的 CUDA 错误/上下文异常、copy engine 占用、NVLink/PCIe 带宽占用都会外溢。

时间片复用(time-slicing)不是隔离:NVIDIA device plugin 的 replicas 是"靠虚增设备数实现超卖"——官方原文说明单卡 replicas: 4 会让节点通告 4 个 GPU、双卡节点通告 8 个。插件通告的是数量不是属性,它无法表达"这 4 个是同一张物理卡的时间片、共享显存与故障域"。没有显存隔离、没有故障隔离、没有 QoS。

隔离强度谱系(从硬到软,选型要匹配多租户的安全要求)

方案 隔离强度 代价
MIG(A100/H100/H200 硬件切分,独立 SM/显存/L2 slice,实例数上限 7) 硬件强制 + QoS profile 需预配置、实例数有限、调度器要感知
MPS 进程级共享,可用 CUDA_MPS_ACTIVE_THREAD_PERCENTAGE 限制 SM 占比 无显存隔离、故障域共享
Time-slicing 无 仅超卖,靠自觉
HAMi 类 API 拦截 可做显存/算力限额 依赖 CUDA API 拦截,绕过路径有风险,无硬件隔离语义

生产处置:要真隔离就上 MIG 或节点池隔离(唯一干净解);要超卖就明确接受降级,并用 DCGM 按 GPU 粒度采集 SM Active / Tensor Active / 显存带宽,把"谁在干扰谁"变成可归因的证据;调度侧用亲和/反亲和避免把两个高密度任务放到同一张物理卡上。注意:kubectl top 与 CPU/Memory 告警对此完全失明,必须另建 GPU 维度的观测。

依据:NVIDIA device plugin 官方 time-slicing 文档(replicas 原文);NVIDIA MIG 用户指南与各卡型实例上限;Kubernetes Device Plugin / DRA 机制;NVIDIA Device Plugin for Kubernetes 源码(getAllocateResponse 的 MPS 共享分支)。

16. NCCL_ASYNC_ERROR_HANDLING / TORCH_NCCL_ASYNC_ERROR_HANDLING 的作用、语义与当前状态

要点:

⚠️ 题目本身有问题:NCCL_ASYNC_ERROR_HANDLING 从来就不是 NVIDIA libnccl 的环境变量。查 NVIDIA 官方 NCCL 环境变量清单,其中没有该条目(NCCL 库侧与之相关的是 API ncclCommGetAsyncError 和 2.23 引入的 NCCL_IB_RETURN_ASYNC_EVENTS)。它是 PyTorch ProcessGroupNCCL 自己读取的变量,只在用 PyTorch 训练时生效。因此"新版为 TORCH_NCCL_ASYNC_ERROR_HANDLING"的表述要修正为:PyTorch 给自己的 NCCL 相关变量统一加了 TORCH_ 前缀,以区别于 libnccl 的原生变量。

核实结论(逐版本源码核对)

版本 读取的变量名 默认值 源码证据
PyTorch ≤ 2.1 NCCL_ASYNC_ERROR_HANDLING 1(TearDown) parseEnvVarIntDefault(NCCL_ASYNC_ERROR_HANDLING, 1 /*TearDown*/)
PyTorch ≥ 2.2 TORCH_NCCL_ASYNC_ERROR_HANDLING 3(SkipCleanUp) getCvarInt(TORCH_NCCL_ASYNC_ERROR_HANDLING, 3 /*SkipCleanUp*/)
  • 引入/改名版本:PyTorch 2.2(同一版本把默认值从 1 改成 3)。PyTorch 2.2 release note 原文:Change default NCCL_ASYNC_ERROR_HANDLING to 3:SkipCleanUp to avoid calling ncclCommAbort which in some cases hangs。
  • 旧名是否已废弃:是。 在 PyTorch 2.2 与当前 main 的 torch/csrc/distributed/c10d/ProcessGroupNCCL.cpp 中检索不到无 TORCH_ 前缀的变量名,旧名写了也不会被读取(静默无效)——这是生产环境里最典型的一个"配置了但没生效"陷阱。
  • 取值语义(官方文档原文口径):0 = 不做任何异步错误处理;1 = 中止 NCCL communicator 并拆掉进程;2 = 只中止 communicator;3 = 拆掉进程但不中止 communicator(默认)。

核心作用与生产价值

  • watchdog 检测到集合通信超时/异步错误后,把"静默 hang"转成"显式异常 + 进程退出"。默认行为(3)已经会让进程退出,所以设 0 等于主动放弃快速失败:任务会一直挂着占住 GPU,直到外层超时或人工介入。
  • 与配套变量一起才构成完整的"故障现场"能力:TORCH_NCCL_TRACE_BUFFER_SIZE(Flight Recorder,PyTorch 2.4 起可用)、TORCH_NCCL_DUMP_ON_TIMEOUT、TORCH_NCCL_DESYNC_DEBUG、TORCH_NCCL_ENABLE_MONITORING / TORCH_NCCL_HEARTBEAT_TIMEOUT_SEC。超时 dump 里会带每个 rank 的 OpType / SeqNum / NumelIn / NumelOut / Timeout(ms)——把全部 worker 的 OpType 与 SeqNum 对齐,就能看出在哪一步分叉(集合通信操作不匹配是最高频的 NCCL hang 根因)。
  • 不要盲目设 =1:默认改成 3 正是为了避开 ncclCommAbort 在某些场景下自身挂死的问题。要"更快失败"应该调 watchdog 超时(NCCL 后端自 PyTorch v2.2 起默认 10 分钟;以所用版本源码为准),而不是把 3 改回 1。
  • 它不修复任何故障,只是把 hang 变成 crash + 现场,让容错/调度系统能感知并重启——这是"有效训练时间占比"能守住的前提。

依据:PyTorch 官方文档《PYTORCH ProcessGroupNCCL Environment Variables》(TORCH_NCCL_ASYNC_ERROR_HANDLING 条目,默认 3);PyTorch v2.1.0 与 v2.2.0 的 torch/csrc/distributed/c10d/ProcessGroupNCCL.cpp 源码对比;PyTorch v2.2.0 release notes;NVIDIA 官方 NCCL 环境变量文档(确认无 NCCL_ASYNC_ERROR_HANDLING)。

17. 生产镜像构建规范为何禁止把数百 GB 权重打进 Docker 镜像

要点:

1)镜像分层与内容寻址机制决定了"没有增量"

  • 每一层是内容寻址(sha256(内容))且不可变。改一个权重文件 → 生成一个全新的数百 GB 层,旧层仍被 manifest 引用、仍占仓库与节点磁盘。模型每出一版,仓库就要多吃一份全量体积(存储成本 = 权重体积 × 版本数)。
  • 层是 gzip 压缩的 tar,拉取时要先下载再解压再写 overlay2,落盘量 ≈ 镜像大小;节点的 upperdir/layer 目录会被迅速撑满。

2)分发放大:万卡同时拉 = 打爆 registry + 打爆节点盘

  • 数百 GB × N 个节点同时拉取,registry 出口带宽瞬间打满,所有节点都变慢(拉取时间 ∝ 镜像大小 ÷ 分到的带宽)。
  • 会与 kubelet 的镜像 GC 直接冲突:imageGCHighThresholdPercent 默认 85(磁盘使用率超过 85% 开始回收镜像)、imageGCLowThresholdPercent 默认 80(降到 80% 停止)。大镜像会把节点推入 ImageFsPressure / KubeletDiskPressure,出现反复 GC → 拉取 → 再 GC 的循环,甚至驱逐 Pod。
  • 失败模式直接可见:ImagePullBackOff、拉取超时、Pod 长时间 ContainerCreating。

3)缓存击穿:同一个 tag 覆盖推送是灾难放大器

  • 生产上真实发生过的形态:镜像 tag 没变但内容被重新 push → 所有节点都要重拉那个增量层 → 几十个 Pod 同时拉 → 每个节点只分到很少带宽。一次实测:只有约 130 MB 的增量层,拉了 56.9 秒(同节点上已缓存该层的 Pod 只用 181 ms)。换成数百 GB 的权重层,这个放大就是小时级。
  • 注意:P2P 分发(Dragonfly/spegel 类)只对出现在 containerd certs.d 路径下的域名生效,用别的域名访问同一个 registry 就不走加速。

4)工程正确做法

  • 镜像只装"环境":基础镜像放 OS + 驱动兼容层 + 框架 + 依赖;权重、数据集、Checkpoint 全部走数据面(对象存储 / 并行文件系统),启动时拉取或挂载。
  • 加载侧加速:safetensors + mmap(避免一次性全量读 + 避免反序列化)、fastsafetensors(成组张量直接传到 device 内存再实例化,官方报告 4.8×–7.5×)、GPUDirect Storage(kvikio / torch.cuda.gds)。
  • 分发侧加速:节点级缓存(本地 NVMe / JuiceFS 缓存组)、P2P 分发、懒加载(eStargz / Nydus / SOCI + 对应 snapshotter)——这些方案的共同前提恰恰是"数据不在镜像层里"或"镜像层可寻址"。
  • 版本管理:镜像 tag 与模型版本解耦,权重用对象存储的版本/校验和(checksum)管理,避免"同名 tag 覆盖"造成的缓存击穿。

依据:OCI 镜像规范与 containerd/overlay2 的层机制(内容寻址、不可变层、copy-up);Kubernetes kubelet 镜像 GC 参数默认值(imageGCHighThresholdPercent 85 / imageGCLowThresholdPercent 80);IBM《Speeding up Model Loading with fastsafetensors》(arXiv:2505.23072, IEEE CLOUD 2025);NVIDIA GPUDirect Storage 文档。

18. HCCL 与 NCCL 在通信挂死(Hang)排查手段上的核心异同

要点:

相同点(方法论层面是一致的)

  • 分层定界:都按"通信域初始化 → 参数面建链 → 任务下发执行"三阶段切分,先判断卡在哪一段,再往下钻。
  • 全 rank 日志对齐:都要把每个 rank 的日志按时间戳排开,回答"谁先报错、谁在等谁"——NCCL 侧看 watchdog 的 OpType / SeqNum 是否分叉,HCCL 侧看各 device 在同一通信域(identifier[...])内的等待链路。
  • 二分消融:换小规模复现、禁用 RDMA(NCCL 是 NCCL_IB_DISABLE=1)、换算法/关闭流水优化做对照,都是两边通用的收敛手段。
  • 抓卡死时刻的栈:pstack / gdb / py-spy 在两边都是"卡在哪一行代码"的最后手段。
  • 都有超时环境变量:把"无限等待"变成"有限等待 + 显式报错"。

核心差异(工程手段层面差异很大)

  1. 日志体系与可见性。NCCL 的日志由 NCCL_DEBUG=INFO + NCCL_DEBUG_SUBSYS=ALL 直接打在训练进程的 stdout,随容器日志一起被采集。HCCL 的日志分 host 侧 plog 与 device 侧日志,device 侧需要专门工具(如 msnpureport)导出;而算子级的下发日志还要单独开关 HCCL_ENTRY_LOG_ENABLE(取值 0/1)。也就是说 HCCL 的"第一现场"默认是不完整的,必须提前在镜像/启动脚本里把日志开关和采集链路配好,出事后再去开往往已经错过现场。
  2. 超时参数的分层语义不同。NCCL 侧的超时主要落在 PyTorch 的 ProcessGroupNCCL watchdog(库侧只有链路级的 NCCL_IB_TIMEOUT,默认 20、取值 0–31,与 NCCL_IB_RETRY_CNT,默认 7)。HCCL 侧是执行级 HCCL_EXEC_TIMEOUT(取值 1s–17340s)+ 建链级 HCCL_CONNECT_TIMEOUT(取值 120s–7200s)两把旋钮,且建链超时是 HCCL 特有的一类高频故障(对应故障码 EI0006"建链接超时",NCCL 侧没有同等级的独立类别)。
  3. 拓扑来源不同,因此多了一整类故障。NCCL 自己探测 NVLink/PCIe/RDMA 拓扑并据此选算法与路径;HCCL 依赖显式的集群信息 / rank table 描述通信域拓扑,于是"集群信息校验失败""集群信息协商失败"是 HCCL 独有的故障大类——排查时先核对 rank table 与实际节点、IP、端口是否一致,再怀疑通信本身。
  4. 工具链生态不同。NCCL 生态:nccl-tests(点对点 + 集合通信二分定位)、PyTorch Flight Recorder(TORCH_NCCL_TRACE_BUFFER_SIZE / TORCH_NCCL_DUMP_ON_TIMEOUT)、NCCL_ALGO 强制算法做对照。HCCL 生态:官方故障诊断按阶段配套故障码体系(EI0001 环境变量配置错误、EI0002 执行通信操作超时、EI0006 建链接超时等)、通信域 identifier 追踪等待关系、HCCL_OP_RETRY_ENABLE 控制通信算子重试、以及关闭流水优化(如 TASK_QUEUE_ENABLE)做对照实验。
  5. 典型根因分布不同。NCCL 侧最常见的是集合通信操作不匹配(一部分 rank 走 AllReduce、另一部分走 AllGather,谁也等不到谁)与 PFC/ECN 拥塞导致的超时。HCCL 侧有一类特色死锁:多通信域并发下发导致的闭环死锁——同一进程同一 HCCL Context 内,不同模块(C++ 侧与 Python 侧接口混用)未统一管理算子下发顺序,导致算子下发乱序 + 设备间同步等待交织成环。官方结论是:所有通信域算子必须在每个 device 上串行下发,由统一调度器管理。这类问题的定位靠 HCCL_ENTRY_LOG_ENABLE=1 看下发日志 + pstack/gdb 抓栈 + 逐级追踪 rank 间等待关系。

一句话总结:方法论同源(分层 + 全 rank 对齐 + 二分 + 抓栈),差异集中在"现场可见性"与"超时/拓扑的显式性"——HCCL 把更多东西交给了显式配置(rank table、日志开关、两级超时),因此排查能力高度依赖事前把日志与配置准备好;NCCL 的自动探测与 stdout 日志让"事后取证"更容易,但也因此更容易出现静默降级(如 fallback 到 Socket)这类隐蔽问题。

依据:昇腾社区 CANN《HCCL 集合通信库故障诊断》文档(定位思路、通信域初始化/参数面建链/任务下发执行三阶段)与《多通信域并发下发导致 HCCL 通信死锁问题分析与解决方案》官方技术文章;HCCL 常见问题总结(EI0001/EI0002/EI0006)与 HCCL_ENTRY_LOG_ENABLE 环境变量文档;NVIDIA 官方 NCCL 环境变量文档。

19. 推测解码的工程落地风险与"接受率 + 动态熔断"的必要性

要点:

原理与收益的边界:draft 模型猜 k 个 token,target 模型一次前向并行验证,接受率 α 决定实际加速比。α 高且并发低时接近"免费加速";α 一旦塌陷,verify 的成本照付、被拒的 draft token 全白算,有效吞吐会低于不投机——生产上出现过"接受率下降导致整体变慢"的实测案例。

四类落地风险

  1. α 漂移(最主要):draft 与 target 的分布不匹配会直接压低 α。触发因素包括:换领域数据(代码/多语言/长文档)、采样参数变化(temperature / top-p 调高)、draft 模型量化、开启动态 EPLB 后专家分布变化(昇腾社区已收录"动态 EPLB 开启后投机推理接受率下降"这一典型案例)。
  2. 并发下收益反转:低并发、单请求时收益最大;高并发时 GPU 已被 batch 填满,verify 的额外算力不再"免费",反而挤占吞吐——同一套投机配置在 1 用户和 32 用户下的结论可能完全相反。
  3. 显存与 KV 放大:draft 模型自身的 KV Cache + 每步多算 k 个 token 的 KV,长上下文下会挤占并发度,把"吞吐优化"变成"并发下降"。
  4. 结构化输出冲突:语法/JSON 约束会让被判定非法的 draft token 失效,有效 α 低于统计值;逐请求统计里的 num_draft_tokens 通常已经是扣除后的口径,读指标时要注意。

为什么必须实时监控 α 并设熔断

  • α 是唯一能直接回答"投机现在到底在赚钱还是在亏钱"的观测量,且它是随流量、模型、采样参数、专家负载持续漂移的——上线前离线测出来的 α 不能作为生产依据。
  • 可采集口径:vLLM 在逐请求响应里提供投机解码统计(需显式开启相应开关,如 --per-request-spec-decode-stats 类参数,以所用版本文档为准),第三方工具(NVIDIA AIPerf 等)把它归一化为 draft_acceptance_rate、mean_acceptance_length、num_accepted_draft_tokens / num_draft_tokens、num_spec_tokens 等字段。核心派生指标就是接受率与平均接受长度(mean_acceptance_length == 1.0 表示"完全没接受",即纯亏损状态)。
  • 熔断兜底的设计:以滑动窗口的 α 与实测 TPOT/吞吐为输入——α 低于阈值、或 TPOT 相比纯 target 解码反而劣化时,自动降 k → 关投机 → 回落到纯 target 解码,并告警;恢复条件要带滞回(连续多个窗口达标才开回),避免在阈值附近反复抖动切换(切换本身要重建 CUDA graph / 重编译,抖动代价很高)。
  • 配套:把 α 与 TPOT 一起进监控看板,按模型版本 × 流量类型 × 采样参数分组看——全局平均 α 会把"某一类流量崩了"平均掉。

依据:vLLM 投机解码与逐请求指标文档;NVIDIA AIPerf《Per-Request Speculative-Decoding Acceptance》(draft_acceptance_rate / mean_acceptance_length / num_accepted_draft_tokens 字段定义与 vLLM 适配说明);昇腾社区"动态 EPLB 开启后投机推理接受率下降"典型案例。

20. SRE 视角的高可用算力监控体系:必须采集的四大维度

要点:

方法论前提:四个维度必须按"信号来源"正交切分,再按"责任层"归因。切错的代价是"查了两周网络,结果根因是慢节点"——因为平面内自洽,但层与层之间没有对照。归因的唯一判据是:这个现象换一台机器 / 换一个集群 / 换一份代码,还跟不跟着走(跟节点走 → 硬件;跟镜像/驱动走 → 系统软件;跟调度位置走 → 调度;跟代码走 → 应用)。

维度一:任务 / 应用面(业务 SLO 与训练进度)

  • 训练:MFU、samples/sec、单 step 耗时(看方差胜过看均值——方差大即有 Straggler)、loss 曲线、日志最后更新时间(最便宜的 Hang 检测,日志超过 5 分钟无输出就告警,比任何探针都更早发现 NCCL Hang)。
  • 推理:TTFT、TPOT(两者由不同阶段决定,必须分开看且看 P99 不看均值)、请求队列长度、错误率、单请求驻留时长。
  • 作用:这是唯一能把"用户感知"翻译成可量化判据的维度;OutOfMemoryError、CUDNN_STATUS_NOT_INITIALIZED 这类瞬时事件长采样间隔根本采不到,只能靠用户日志。

维度二:GPU / 算力硬件面

  • 利用率要组合读,单看一个会误导:GR_ENGINE_ACTIVE(Util)vs SM_ACTIVE vs SM_OCCUPANCY vs PIPE_TENSOR_ACTIVE。“Util 打到 100% 而 SM Active 接近 0” 是 NCCL 阻塞的特征指纹(GPU 在轮询式地占着引擎但没在算)——这是不看业务代码就能识别集体卡顿的唯一判据。
  • SM Clock:最容易被漏掉、却是慢节点的主要信号(曾出现 2048 卡任务 MFU 长期波动,最后定位到 256 台里有一台的 8 张卡 SM 时钟长期跑 1.1 GHz、其他机器 1.9 GHz;这类问题在交换机上永远查不到)。
  • 显存要分"容量占用"与"显存带宽利用率"两个量;Xid(31/48/63/64/79/119/120 分级处置)、ECC(SBE/DBE,Volatile vs Aggregate 口径不能混)、降频标志位、PCIe 链路速率与降速(16x → 2x 不报错只变慢)、NVLink 带宽与错误计数、温度与功耗。

维度三:网络 / 通信面

  • 无损机制是否生效:PFC 暂停帧计数、ECN 标记率、队列深度、headroom 余量、CNP Slow Restart(隐性丢包与降速的强信号)、RDMA 重传计数(长期应为 0)。
  • 链路质量:CRC 错包(健康链路应长期为 0)、收发光功率与温度(光模块 DDM)、端口翻转次数、协商速率(静默降速必须主动比对基线)、IB 侧 SymbolErrors / LinkErrorRecovery / LinkDowned。
  • 所有计数器类指标必须按增量看(rate()/increase()),瞬时值会把一次突发抹平;必须采集链路两端的计数器(两端可能不对称)。

维度四:存储 / I-O 面

  • 读写带宽与 IOPS、await 与 %util、元数据操作延迟(小文件场景 MDS 才是真瓶颈,光看带宽会误判)。
  • Checkpoint 专项:单次写入耗时、对训练的阻塞时长、异步上传队列深度。
  • 水位要容量与 inode 两个维度都看(df -h 与 df -i),加上缓存命中率与 NVMe SMART/寿命。
  • 判据:顺序读达标而随机读极慢是"符合预期"的,不能据此说存储坏了——先分清顺序/随机、单线程/多线程、首次(冷缓存)/第二次(热缓存),这四个维度决定结论完全相反。

配套的采集纪律与看板

  • 采样间隔必须匹配事件尺度:一个训练 step 几百毫秒到几秒,一次 AllReduce 只有百微秒到毫秒级,长间隔采样对毫秒级事件是瞎的;计数器类指标按增量看,事件型信息靠日志补。
  • 告警三层设计:收敛(for 持续时间,过滤毛刺)→ 聚合(group_by cluster+node)→ 抑制(inhibit_rules 建模因果关系,不要用长期 Silence 代替 inhibit);必须用 absent() 类告警捕获"指标断采"(普通阈值告警在指标消失时根本不会触发)。
  • 容量水位看板:GPU 分配率(>85% 关注)、有效算力比 = MFU 均值 / 目标 MFU(<80% 告警,比分配率更能反映真实利用)、节点可用率(<95% 关注)、网络重传率、存储网水位(>70% 关注,Checkpoint 突发需要余量)。
  • 基线是前提:交付验收阶段就要存档点对点带宽、集合通信带宽、单节点 MFU、nvidia-smi topo -m 卡间拓扑、网卡-交换机端口对应关系;任何网络侧/硬件侧变更前后都要重跑基线——"运行三个月突然变慢"追到最后几乎都是三个月里某次没人记录的变更。

依据:AI Infra 运维排障知识库(四平面/四层责任面、DCGM DCGM_FI_PROF_* 指标口径与组合读法、告警收敛/聚合/抑制三层设计);DCGM 与 dcgm-exporter 文档;《集群监控告警》面试题集(容量水位阈值、告警分级)。



七、这套卷子该怎么用

试卷本身的设计意图是对的:用生产现象倒逼机制理解。像「所有卡满载 100% 却无输出」这种描述,比任何教科书定义都更能让人记住「忙轮询等待」和「真在算」的区别——前者的 GPU 利用率是假的,瓶颈在等一个永远不来的集合通信对端。

但勘误那 11 条也说明一个问题:AI Infra 的知识半衰期很短。这份卷子里过时的部分,几乎都集中在「模型架构」和「框架版本行为」两类:

  • 模型架构:DeepSeek-V4 弃用 MLA,是从 V3 到 V4 的一次真实架构换代。任何按 V3 口径记住的 KV Cache 公式,到 V4 上都不成立。
  • 框架版本行为:vLLM V1 移除 swap、PyTorch 2.2 改超时变量名与默认值——这类变更不会大张旗鼓地公告,但会让旧笔记静默失效(NCCL_ASYNC_ERROR_HANDLING 写了不报错、也不生效,是最典型的坑)。

所以用这套卷子自测时,建议这样处理:

  1. 现象类题目(为什么卡死、为什么 OOM、为什么掉速)——放心当考点,这些物理机制不会变。
  2. 涉及具体模型架构的题目——先查当前版本的 config.json 和官方技术报告再作答。
  3. 涉及具体环境变量、API、默认值的题目——一律回官方文档核对当前版本,不要用记忆里的值。
  4. 涉及组件职责边界的题目——多问一句「这个动作到底是谁做的」,调度器、设备插件、健康检查控制器、作业控制器之间的职责经常被混着说。

最后一条尤其值得强调。多选第 5 题的 D 项把「探测坏卡 → 打 NoSchedule 污点 → 驱逐」当成调度器的能力,这是很常见的误解。真实的分工是:探测与隔离由 NVSentinel 这类健康检查组件(fault-quarantine 打污点、node-drainer 驱逐)和作业控制器负责,调度器只是消费污点做决策。搞混这个边界,排查时就会去翻调度器日志找一个根本不存在的逻辑。

这类职责边界问题,我在 推理服务调度的五层责任边界 里也专门拆过——每一层「看得见什么、看不见什么」,比「它有什么功能」更能决定你该去哪里查。


本文的答案与解析已同步沉淀进本地 AI Infra 知识库(ai-infra skill)的 references/troubleshooting.md 与 references/inference.md,可直接检索复用。

所有答案均尽量追至一手来源(NVIDIA 官方文档、PyTorch / vLLM 源码、Linux 内核定义、模型官方 config.json)。题目勘误仅针对题干表述,不代表原卷作者的技术判断有误——其中多数是版本演进导致的自然过时。