Skip to content

执行器与Worker — 练习

练习 1:Worker 初始化顺序

在多 GPU 场景中(4 × A100),分析 MultiProcExecutor 的初始化过程:

  1. 创建了多少个 Worker 进程?
  2. 每个进程如何知道使用哪个 GPU?
  3. NCCL 通信组如何建立?
参考答案
  1. 创建 4 个 Worker 进程,每个 GPU 对应一个。
  2. 通过 CUDA_VISIBLE_DEVICES 环境变量或 torch.cuda.set_device(local_rank) 设置。
  3. 每个 Worker 调用 torch.distributed.init_process_group(backend='nccl'),通过 rendezvous(如文件系统或 TCP)协调。TP=4 时所有 4 个 Worker 在同一进程组中。

练习 2:CUDA Graph 捕获分析

分析 CUDA Graph 捕获的条件和限制:

  1. 为什么 CUDA Graph 只在 decode 阶段使用?
  2. 捕获时输入形状如何确定?
  3. 如果 batch size 超过捕获时的形状会怎样?
参考答案
  1. Prefill 阶段的输入形状是动态的(不同 prompt 长度),无法用固定图捕获。Decode 阶段每步只生成 1 个 token,输入形状固定,适合图捕获。

  2. vLLM 会为多个 batch size 分别捕获图(如 1, 2, 4, 8, ..., max_num_seqs)。运行时选择最接近的已捕获 batch size 的图。

  3. 如果 batch size 超过最大捕获的图,会回退到普通执行(非图模式)。这也是为什么 vLLM 需要在 warmup 阶段为多个 batch size 捕获图。

练习 3:显存分配分析

在一个 80GB A100 上运行 7B 模型(FP16),分析显存分配:

  1. 模型参数占用多少显存?
  2. profiling 后剩余多少给 KV 缓存?
  3. 如果 block_size=16,能分配多少 block?
参考答案
  1. 7B 参数 × 2 bytes (FP16) = 14 GB。加上优化器状态和梯度(推理不需要),实际约 14 GB。

  2. 剩余约 80 - 14 = 66 GB(减去 CUDA context 和其他开销,实际约 60-65 GB)。

  3. 每个 block 的 KV 缓存大小 = block_size × head_dim × num_kv_heads × 2 (K+V) × 2 (FP16) bytes。假设 LLaMA-7B (32 heads, head_dim=128, 32 layers): 16 × 128 × 32 × 2 × 2 × 32 layers ≈ 8.4 MB/block。约 60 GB / 8.4 MB ≈ 7142 blocks。

拓展挑战

  • 阅读 gpu_model_runner.py_prepare_inputs 方法,理解输入批处理的详细逻辑
  • 分析 cudagraph_utils.py 中 CUDA Graph 捕获的底层实现
  • 研究 RayExecutor 如何在多节点上调度 Worker