2026 大厂 AI Infra 生产排错测试(卷二)50 题全解:附 12 处题目勘误
这份试卷流传得挺广。它的定位很明确:不考概念背诵,而是把你直接扔进万卡集群的线上事故现场。题目质量整体在线——很多题干描述的现象(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_cacheAPI 文档;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.3aFrameCheckSequenceErrors)。
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)。
分层排查顺序(先排掉与驱动库无关的原因,再落到这两个变量):
- 节点是否分到卡:
kubectl describe node <node> | grep nvidia.com/gpu——Capacity有值而Allocatable为 0 说明卡被标记 Unhealthy(ECC/Xid 掉卡)。 - 插件是否注册:
ls /var/lib/kubelet/device-plugins/*.sock(kubelet 重启会清空该目录,插件须能自动重注册)。 - 容器内看设备与变量:
ls -l /dev/nvidia*、env | grep NVIDIA——节点在、env 在而库缺失,就是 toolkit/CDI 的问题,可用nvidia-container-cli list确认实际挂了哪些库。 - 注意
/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),需 kubeletcpu-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插件 +HyperNodeCRD:把 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 与集群调度套件上报,调度器只是消费方。
- 探测层:DCGM/GPU 健康监控、node-problem-detector、device plugin 的健康上报、NVSentinel 的
依据: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 项):
- 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 的算法"。- 不存在官方 “DualPipe-V2”。仓库中唯一相近的是 DualPipeV——由 DualPipe “cut-in-half” 得到的 V 形调度(仓库注明来自 Sea AI Lab 的博客),特点是激活降到 PP/2、设备数减半;它是另一个调度,不是"第二版"。另有非官方博客把某版本称为 “DualPipe 2.0”,无可信来源支撑。
- 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()(真实张量)vsmemory_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;PyTorchtorch.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 个进程,各限 45%(需在首次分配前调用) |
依据:PyTorch 源码
torch/cuda/memory.py(set_per_process_memory_fraction/get_per_process_memory_fraction);vLLMCacheConfig文档。
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 | nvidia-smi # 进程表 + 显存 |
- 交叉验证:
kubectl describe node <node>的nvidia.com/gpu分配数 vs 实际 Pod;宿主机ps -ef找进程;nvidia-smi 报 “No running processes found” 但显存仍被占 → 多为 context 残留。
清理顺序(由轻到重)
- 宿主机
kill -9 <pid>;确认/dev/nvidia*已无持有者。 - 确认无任何应用占用该 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”。 - 仍无效 → 重启节点(冷启动);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。
- RECOMPUTE(重计算,默认):
⚠️ 题目本身有问题(口径过时):上述"两种策略"是 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.pyAPI 文档(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_step1–2 s、T_ckpt5–10 min、K=500 → 约 42% 的算力被空转吃掉,且会顺带打满存储、影响同集群其他租户(噪声邻居)。
分层存储的三级流水(唯一可行解)
1 | GPU HBM --DMA--> CPU pinned --独立进程--> 本地 NVMe --限速后台--> 并行FS/对象存储 |
- L0 GPU HBM → CPU pinned memory(唯一同步段):预分配 pinned 池,用
cudaMemcpyAsync+ 独立 CUDA stream;利用 fwd/bwd 期间张量不可变,把 D2H 拷贝打散到整个迭代中与计算 kernel 交错(DataStates-LLM,HPDC’24 的做法)。 - L1 CPU → 本地 NVMe(异步段):序列化与落盘必须放独立进程而非线程(GIL 隔离);大块写用
O_DIRECT/io_uring绕过页缓存;若节点有 NVMe 且启用 GPUDirect Storage,L0+L1 可合并为一次 HBM→NVMe 的 DMA。 - 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 | # vLLM 官方文档给出的标准写法(示意) |
- 网关/负载均衡层要做连接级取消透传,不要让断连在代理处被吞掉;给每条请求设
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)
- 切:把 KV Cache 沿序列维度切成若干 chunk;
- 并行算:query 与每个 chunk 并行做 FlashAttention,除局部输出外额外写出每行的 log-sum-exp;
- 归约:第二个 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。
因此同卡邻居可以造成的四类伤害
- 算力抢占:SM 时间片轮转由驱动做,没有公平性保证,邻居的持续大 kernel 会让你的 kernel 排队;
- 访存争抢:HBM 带宽与 L2 被邻居打满,你的算子直接掉速(访存密集算子受影响最大);
- 显存无隔离:显存是先到先得,一个进程把显存吃满,同卡其他租户直接 OOM;
- 共享故障域:邻居的 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 库侧与之相关的是 APIncclCommGetAsyncError和 2.23 引入的NCCL_IB_RETURN_ASYNC_EVENTS)。它是 PyTorchProcessGroupNCCL自己读取的变量,只在用 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 参数默认值(
imageGCHighThresholdPercent85 /imageGCLowThresholdPercent80);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在两边都是"卡在哪一行代码"的最后手段。 - 都有超时环境变量:把"无限等待"变成"有限等待 + 显式报错"。
核心差异(工程手段层面差异很大)
- 日志体系与可见性。NCCL 的日志由
NCCL_DEBUG=INFO+NCCL_DEBUG_SUBSYS=ALL直接打在训练进程的 stdout,随容器日志一起被采集。HCCL 的日志分 host 侧 plog 与 device 侧日志,device 侧需要专门工具(如msnpureport)导出;而算子级的下发日志还要单独开关HCCL_ENTRY_LOG_ENABLE(取值 0/1)。也就是说 HCCL 的"第一现场"默认是不完整的,必须提前在镜像/启动脚本里把日志开关和采集链路配好,出事后再去开往往已经错过现场。 - 超时参数的分层语义不同。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 侧没有同等级的独立类别)。 - 拓扑来源不同,因此多了一整类故障。NCCL 自己探测 NVLink/PCIe/RDMA 拓扑并据此选算法与路径;HCCL 依赖显式的集群信息 / rank table 描述通信域拓扑,于是"集群信息校验失败""集群信息协商失败"是 HCCL 独有的故障大类——排查时先核对 rank table 与实际节点、IP、端口是否一致,再怀疑通信本身。
- 工具链生态不同。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)做对照实验。 - 典型根因分布不同。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 全白算,有效吞吐会低于不投机——生产上出现过"接受率下降导致整体变慢"的实测案例。
四类落地风险
- α 漂移(最主要):draft 与 target 的分布不匹配会直接压低 α。触发因素包括:换领域数据(代码/多语言/长文档)、采样参数变化(temperature / top-p 调高)、draft 模型量化、开启动态 EPLB 后专家分布变化(昇腾社区已收录"动态 EPLB 开启后投机推理接受率下降"这一典型案例)。
- 并发下收益反转:低并发、单请求时收益最大;高并发时 GPU 已被 batch 填满,verify 的额外算力不再"免费",反而挤占吞吐——同一套投机配置在 1 用户和 32 用户下的结论可能完全相反。
- 显存与 KV 放大:draft 模型自身的 KV Cache + 每步多算 k 个 token 的 KV,长上下文下会挤占并发度,把"吞吐优化"变成"并发下降"。
- 结构化输出冲突:语法/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)vsSM_ACTIVEvsSM_OCCUPANCYvsPIPE_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_bycluster+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写了不报错、也不生效,是最典型的坑)。
所以用这套卷子自测时,建议这样处理:
- 现象类题目(为什么卡死、为什么 OOM、为什么掉速)——放心当考点,这些物理机制不会变。
- 涉及具体模型架构的题目——先查当前版本的
config.json和官方技术报告再作答。 - 涉及具体环境变量、API、默认值的题目——一律回官方文档核对当前版本,不要用记忆里的值。
- 涉及组件职责边界的题目——多问一句「这个动作到底是谁做的」,调度器、设备插件、健康检查控制器、作业控制器之间的职责经常被混着说。
最后一条尤其值得强调。多选第 5 题的 D 项把「探测坏卡 → 打 NoSchedule 污点 → 驱逐」当成调度器的能力,这是很常见的误解。真实的分工是:探测与隔离由 NVSentinel 这类健康检查组件(fault-quarantine 打污点、node-drainer 驱逐)和作业控制器负责,调度器只是消费污点做决策。搞混这个边界,排查时就会去翻调度器日志找一个根本不存在的逻辑。
这类职责边界问题,我在 推理服务调度的五层责任边界 里也专门拆过——每一层「看得见什么、看不见什么」,比「它有什么功能」更能决定你该去哪里查。
本文的答案与解析已同步沉淀进本地 AI Infra 知识库(
ai-infraskill)的references/troubleshooting.md与references/inference.md,可直接检索复用。所有答案均尽量追至一手来源(NVIDIA 官方文档、PyTorch / vLLM 源码、Linux 内核定义、模型官方 config.json)。题目勘误仅针对题干表述,不代表原卷作者的技术判断有误——其中多数是版本演进导致的自然过时。
