vLLMBackend
VllmBackend是 vLLM 自定义的torch.compile后端,仅在CompilationLevel.PIECEWISE下使用。- 它接收 Dynamo 生成的
fx.GraphModule,按配置切分成若干子图进行编译,并返回一个可调用的拼接图(或带输入复制包装的可调用函数)。 - 同时把 vLLM 的自定义后处理 pass( PostGradPassManager )注入到 Inductor 配置里,并管理编译缓存目录与命中策略。·
- 位置:
/vllm/vllm/compilation/backends.py
1 | class VllmBackend: |
@support_torch_compile [decorator]
用法:
直接装饰:
@support_torch_compile,自动按类型注解推断需要标记为动态的参数维度。带参数装饰:
@support_torch_compile(dynamic_arg_dims=..., enable_if=...),显式指定需要标记的动态维度,并按条件启用。
无参装饰时,装饰器返回的是一个“类处理函数”;带参装饰时,先构造参数,再返回处理函数。两种最终都会调用内部 _support_torch_compile(cls, dynamic_arg_dims, enable_if) 。
设备:每个rank都会独立执行模型的forward,因此编译也是在各自的GPU上发生,这部分编译包装会在每个rank上独立发生
1 | # in vllm/vllm/compilation.decorators.py |
TorchCompileWrapperWithCustomDispatcher
self.compiled_callable如何得到
1 | # in vllm/vllm/compilation.decorators.py |
这里的TorchCompileWrapperWithCustomDispatcher
1 | # compiled_callable 是包装类 TorchCompileWrapperWithCustomDispatcher 在__init__时通过 torch.compile(self.forward, fullgraph=True, backend=..., options=...) 得到的、可调用的已编译函数指针 |
dynamic_arg_dims是一个 “参数名 → 动态维度索引” 的映射,用来告诉 Dynamo 哪些输入张量的哪些维度在不同调用间会变化,需要被标记为动态形状,以生成可适配不同形状的图(或避免不必要的守卫/重编译)。
- 在装饰器的
call首次调用时,根据函数签名绑定实参后,调用torch._dynamo.mark_dynamic(arg, dims)对这些入参进行标记。支持负索引(如 -1 表示最后一维)。如果是 IntermediateTensors ,则会对其内部的每个张量按同样规则标记。 - 该映射只针对“调用时传入的输入张量”,例如 batch 的
input_ids、positions、中间张量等。模型权重参数不会在这里被标记动态。 - Qwen2/Qwen3 的实际映射(来自源码):
- vllm/vllm/model_executor/models/qwen2.py 的
@support_torch_compile(dynamic_arg_dims=...):input_ids: 0 (batch 或序列的第 0 维)positions: -1 (最后一维;Qwen2-VL 的位置张量维度在 MRoPE 情况下不同)intermediate_tensors: 0inputs_embeds: 0
- vllm/vllm/model_executor/models/qwen3.py 同样声明了
dynamic_arg_dims(Qwen3 继承自 Qwen2,forward沿用 Qwen2 的逻辑)。
- vllm/vllm/model_executor/models/qwen2.py 的
1 | # in vllm/vllm/compilation.decorators.py |
traces_files(基于当前vllm_config的追踪文件集合)
加入时机与内容:
- 必定加入:顶层
forward方法的源码文件,即self.original_code_object.co_filename(你的日志里就是 …/site-packages/vllm/…/qwen2.py )。 - 可能加入:Dynamo 追踪期间内联的所有函数的源码文件。装饰器在首次编译时临时“打补丁”了
InliningInstructionTranslator.inline_call,对每个被内联的函数,执行self.vllm_config.compilation_config.traced_files.add(code.co_filename)。
在 Qwen2/Qwen3 的典型路径下,会看到至少:
- 顶层:
vllm/model_executor/models/qwen2.py(Qwen3 继承未重写forward,所以日志指向 qwen2.py) - 层实现:
vllm/model_executor/layers/linear.py、vllm/model_executor/layers/layernorm.py、vllm/model_executor/layers/rotary_embedding/...、vllm/model_executor/layers/attention/... 等被 Python 层直接调用的函数/方法所在文件 - 工具/接口:若在 Python 层有函数调用并被 Dynamo 内联(例如某些 utils.py 中的纯 Python 函数),其文件也会加入
集合定义位置: vllm/vllm/config/compilation.py:359 有 traced_files: set[str]
具体集合取决于“首次编译时实际走过的 Python 调用路径”。因此除了“顶层forward文件”是确定的之外,其他文件集合需要以你的具体模型/配置的首轮输入为准。可以在编译后直接查看 get_current_vllm_config().compilation_config.traced_files 获取当前实例的实际集合。
1 | # in vllm/vllm/compilation/decorators.py |
日志对应时序(与“Start compiling function … qwen2.py …” 日志对齐)
- 设备与模型加载阶段
- 设置设备: gpu_worker.py:170 绑定到 cuda:{LOCAL_RANK} 。
- 加载模型: gpu_model_runner.py 完成权重加载,打印显存与耗时日志。
- 决策是否包 CUDA Graph: gpu_model_runner.py:2880-2960 。
- 预热与首次编译触发
- 预热调用: gpu_worker.py:345+ 遍历 compile_sizes 调用 _dummy_run ,进入模型的 call 。
- 首次编译入口: decorators.py:call 检测到 compiled_codes 为空,按 dynamic_arg_dims 标记输入的动态维度,调用 start_monitoring_torch_compile(…) 。
- 打印编译开始日志: logger.debug(“Start compiling function %s”, self.original_code_object) ,这就是你看到的 “Start compiling function <code object forward at …, file …/qwen2.py, line …>”。
- 收集追踪文件:同一 call 中,在首次执行 self.compiled_callable(*args, **kwargs) 前打补丁 InliningInstructionTranslator.inline_call ,将顶层 forward 的 co_filename 以及所有内联函数的 co_filename 加入 traced_files 。
- 编译完成:Dynamo/Inductor 完成编译后, bytecode_hook 把新字节码保存到 self.compiled_codes ,必要时把反编译源码 dump 到 compile_debug_dump_path() 。
- CUDA Graph 捕获(若启用)
- 完成预热后: gpu_worker.py:365+ 调用 capture_model() 。如启用 FULL 模式,会做一次全图捕获;如是 piecewise,按照分区逐段包装/捕获。
- 随后进入实际推理,调度至已编译图或 CUDA Graph。
1 | self.compiled_callable是什么?怎么得到的? |