跳到内容

预热

预热是一个强烈推荐的步骤,在 vLLM 服务器开始监听之前执行。它使用虚拟数据为每个 bucket 执行一次前向传播。目标是在服务器运行时,预编译所有图并将 bucket 边界内的任何图编译开销消除。每个预热步骤在 vLLM 启动期间都会被记录。

下面的示例展示了与“分桶机制”部分描述的相同分桶。每一行输出对应于单个分桶的执行。当一个分桶首次执行时,它的图会被编译,并且以后可以重用,从而避免了进一步的图编译。

INFO 08-01 22:26:47 hpu_model_runner.py:1066] [Warmup][Prompt][1/24] batch_size:4 seq_len:1024 free_mem:79.16 GiB
INFO 08-01 22:26:47 hpu_model_runner.py:1066] [Warmup][Prompt][2/24] batch_size:4 seq_len:896 free_mem:55.43 GiB
INFO 08-01 22:26:48 hpu_model_runner.py:1066] [Warmup][Prompt][3/24] batch_size:4 seq_len:768 free_mem:55.43 GiB
...
INFO 08-01 22:26:59 hpu_model_runner.py:1066] [Warmup][Prompt][24/24] batch_size:1 seq_len:128 free_mem:55.43 GiB
INFO 08-01 22:27:00 hpu_model_runner.py:1066] [Warmup][Decode][1/48] batch_size:4 seq_len:2048 free_mem:55.43 GiB
INFO 08-01 22:27:00 hpu_model_runner.py:1066] [Warmup][Decode][2/48] batch_size:4 seq_len:1920 free_mem:55.43 GiB
INFO 08-01 22:27:01 hpu_model_runner.py:1066] [Warmup][Decode][3/48] batch_size:4 seq_len:1792 free_mem:55.43 GiB
...
INFO 08-01 22:27:16 hpu_model_runner.py:1066] [Warmup][Decode][47/48] batch_size:2 seq_len:128 free_mem:55.43 GiB
INFO 08-01 22:27:16 hpu_model_runner.py:1066] [Warmup][Decode][48/48] batch_size:1 seq_len:128 free_mem:55.43 GiB

编译所有分桶可能需要一些时间。要跳过此步骤,您可以设置环境变量 VLLM_SKIP_WARMUP=true。请注意,这样做可能会在特定分桶首次执行时触发图编译。

警告

禁用预热对于开发目的来说是可以接受的,但我们强烈建议在生产环境中保持启用状态。

HPU 图捕获

HPU 图是 vLLM Intel® Gaudi® 硬件插件目前性能最高的执行方法。启用 HPU 图后,执行图会在预热后被跟踪(记录),然后在推理期间重放,从而显著降低主机开销。记录可能会消耗大量内存,在分配 KV 缓存时应考虑这一点。启用 HPU 图会影响可用 KV 缓存块的数量,但 vLLM 提供了用户可配置的变量来帮助管理内存使用。

当使用 HPU 图时,它们与 KV 缓存共享一个称为可用内存的通用内存池,这由 gpu_memory_utilization 标志控制,默认值为 0.9。在分配 KV 缓存之前,模型权重会被加载到设备上,并使用虚拟数据执行一次前向传播以估算内存使用量。只有在此步骤之后,才会应用 gpu_memory_utilization 标志。默认情况下,它将该点上 90% 的可用内存指定为可用。接下来,分配 KV 缓存,预热模型,并捕获 HPU 图。VLLM_GRAPH_RESERVED_MEM 环境变量定义了为 HPU 图捕获预留的内存部分。默认值为 0.1,10% 的可用内存预留用于图捕获(称为“可用图内存”),其余 90% 分配给 KV 缓存。

gpu_memory_utilization 参数并不代表 HPU 上的绝对内存使用量。相反,它指定了加载模型和运行配置文件传递后的内存余量。例如,如果设备总共有 100 GiB 内存,并且在加载模型权重和执行配置文件运行后有 50 GiB 可用内存,则 gpu_memory_utilization 的默认值会将 50 GiB 的 90% 标记为可用,留下 5 GiB 作为余量——无论设备总内存是多少。

当有许多请求待处理时,vLLM 调度器会尝试尽快填满最大解码批次大小。一旦一个请求完成,解码批次大小就会减小。当这种情况发生时,vLLM 会为等待队列中的请求调度一次预填充迭代,以恢复先前的解码批次大小。在完全加载的情况下,解码批次大小通常达到最大值,因此捕获大型批次的 HPU 图至关重要。另一方面,提示迭代通常以非常小的批次大小(1-4)执行。

vLLM 服务器会记录上面列出的每个步骤,负值表示内存释放。

INFO 08-02 17:37:44 hpu_model_runner.py:493] Prompt bucket config (min, step, max_warmup) bs:[1, 32, 4], seq:[128, 128, 1024]
INFO 08-02 17:37:44 hpu_model_runner.py:499] Generated 24 prompt buckets: [(1, 128), (1, 256), (1, 384), (1, 512), (1, 640), (1, 768), (1, 896), (1, 1024), (2, 128), (2, 256), (2, 384), (2, 512), (2, 640), (2, 768), (2, 896), (2, 1024), (4, 128), (4, 256), (4, 384), (4, 512), (4, 640), (4, 768), (4, 896), (4, 1024)]
INFO 08-02 17:37:44 hpu_model_runner.py:504] Decode bucket config (min, step, max_warmup) bs:[1, 128, 4], seq:[128, 128, 2048]
INFO 08-02 17:37:44 hpu_model_runner.py:509] Generated 48 decode buckets: [(1, 128), (1, 256), (1, 384), (1, 512), (1, 640), (1, 768), (1, 896), (1, 1024), (1, 1152), (1, 1280), (1, 1408), (1, 1536), (1, 1664), (1, 1792), (1, 1920), (1, 2048), (2, 128), (2, 256), (2, 384), (2, 512), (2, 640), (2, 768), (2, 896), (2, 1024), (2, 1152), (2, 1280), (2, 1408), (2, 1536), (2, 1664), (2, 1792), (2, 1920), (2, 2048), (4, 128), (4, 256), (4, 384), (4, 512), (4, 640), (4, 768), (4, 896), (4, 1024), (4, 1152), (4, 1280), (4, 1408), (4, 1536), (4, 1664), (4, 1792), (4, 1920), (4, 2048)]
INFO 08-02 17:37:52 hpu_model_runner.py:430] Pre-loading model weights on hpu:0 took 14.97 GiB of device memory (14.97 GiB/94.62 GiB used) and 2.95 GiB of host memory (475.2 GiB/1007 GiB used)
INFO 08-02 17:37:52 hpu_model_runner.py:438] Wrapping in HPU Graph took 0 B of device memory (14.97 GiB/94.62 GiB used) and -252 KiB of host memory (475.2 GiB/1007 GiB used)
INFO 08-02 17:37:52 hpu_model_runner.py:442] Loading model weights took in total 14.97 GiB of device memory (14.97 GiB/94.62 GiB used) and 2.95 GiB of host memory (475.2 GiB/1007 GiB used)
INFO 08-02 17:37:54 hpu_worker.py:134] Model profiling run took 504 MiB of device memory (15.46 GiB/94.62 GiB used) and 180.9 MiB of host memory (475.4 GiB/1007 GiB used)
INFO 08-02 17:37:54 hpu_worker.py:158] Free device memory: 79.16 GiB, 39.58 GiB usable (gpu_memory_utilization=0.5), 15.83 GiB reserved for HPUGraphs (VLLM_GRAPH_RESERVED_MEM=0.4), 23.75 GiB reserved for KV cache
INFO 08-02 17:37:54 hpu_executor.py:85] # HPU blocks: 1519, # CPU blocks: 0
INFO 08-02 17:37:54 hpu_worker.py:190] Initializing cache engine took 23.73 GiB of device memory (39.2 GiB/94.62 GiB used) and -1.238 MiB of host memory (475.4 GiB/1007 GiB used)
INFO 08-02 17:37:54 hpu_model_runner.py:1066] [Warmup][Prompt][1/24] batch_size:4 seq_len:1024 free_mem:55.43 GiB
...
INFO 08-02 17:38:22 hpu_model_runner.py:1066] [Warmup][Decode][48/48] batch_size:1 seq_len:128 free_mem:55.43 GiB
INFO 08-02 17:38:22 hpu_model_runner.py:1159] Using 15.85 GiB/55.43 GiB of free device memory for HPUGraphs, 4.755 GiB for prompt and 11.095 GiB for decode (VLLM_GRAPH_PROMPT_RATIO=0.3)
INFO 08-02 17:38:22 hpu_model_runner.py:1066] [Warmup][Graph/Prompt][1/24] batch_size:1 seq_len:128 free_mem:55.43 GiB
...
INFO 08-02 17:38:26 hpu_model_runner.py:1066] [Warmup][Graph/Prompt][11/24] batch_size:1 seq_len:896 free_mem:48.77 GiB
INFO 08-02 17:38:27 hpu_model_runner.py:1066] [Warmup][Graph/Decode][1/48] batch_size:4 seq_len:128 free_mem:47.51 GiB
...
INFO 08-02 17:38:41 hpu_model_runner.py:1066] [Warmup][Graph/Decode][48/48] batch_size:1 seq_len:2048 free_mem:47.35 GiB
INFO 08-02 17:38:41 hpu_model_runner.py:1066] [Warmup][Graph/Prompt][12/24] batch_size:4 seq_len:256 free_mem:47.35 GiB
INFO 08-02 17:38:42 hpu_model_runner.py:1066] [Warmup][Graph/Prompt][13/24] batch_size:2 seq_len:512 free_mem:45.91 GiB
INFO 08-02 17:38:42 hpu_model_runner.py:1066] [Warmup][Graph/Prompt][14/24] batch_size:1 seq_len:1024 free_mem:44.48 GiB
INFO 08-02 17:38:43 hpu_model_runner.py:1066] [Warmup][Graph/Prompt][15/24] batch_size:2 seq_len:640 free_mem:43.03 GiB
INFO 08-02 17:38:43 hpu_model_runner.py:1128] Graph/Prompt captured:15 (62.5%) used_mem:14.03 GiB buckets:[(1, 128), (1, 256), (1, 384), (1, 512), (1, 640), (1, 768), (1, 896), (1, 1024), (2, 128), (2, 256), (2, 384), (2, 512), (2, 640), (4, 128), (4, 256)]
INFO 08-02 17:38:43 hpu_model_runner.py:1128] Graph/Decode captured:48 (100.0%) used_mem:161.9 MiB buckets:[(1, 128), (1, 256), (1, 384), (1, 512), (1, 640), (1, 768), (1, 896), (1, 1024), (1, 1152), (1, 1280), (1, 1408), (1, 1536), (1, 1664), (1, 1792), (1, 1920), (1, 2048), (2, 128), (2, 256), (2, 384), (2, 512), (2, 640), (2, 768), (2, 896), (2, 1024), (2, 1152), (2, 1280), (2, 1408), (2, 1536), (2, 1664), (2, 1792), (2, 1920), (2, 2048), (4, 128), (4, 256), (4, 384), (4, 512), (4, 640), (4, 768), (4, 896), (4, 1024), (4, 1152), (4, 1280), (4, 1408), (4, 1536), (4, 1664), (4, 1792), (4, 1920), (4, 2048)]
INFO 08-02 17:38:43 hpu_model_runner.py:1206] Warmup finished in 49 secs, allocated 14.19 GiB of device memory
INFO 08-02 17:38:43 hpu_executor.py:91] init_cache_engine took 37.92 GiB of device memory (53.39 GiB/94.62 GiB used) and 57.86 MiB of host memory (475.4 GiB/1007 GiB used)

采样器预热

采样器使用配置的解码策略(如贪婪或概率性)将模型 logits 转换为下一个 token 选择。其预热阶段会为代表性的批次大小和采样参数组合准备编译好的图变体或内部代码路径,以便第一个真实用户请求避免额外的编译或设置延迟。

预热确保了常见的超参数组合可以提前编译,并且贪婪和随机分支策略以及元数据刷新路径都会得到执行和稳定。它还处理批次增长或收缩场景,平滑后续的缩放行为。跳过采样器预热不会影响正确性——只会影响最早的各种采样请求的延迟情况。以下列表显示了缺少预热的结果。

  • 使用新配置的第一个请求(例如,第一次使用 high-temptop-k 路径,或在负载扩展后的第一个批次大小)可能会触发图重新编译,从而为该请求增加延迟。
  • 尾部延迟变化会增加,因为具有不同工作负载的早期请求可能会触发多个错开的编译。
  • 批次大小转换逻辑,其中路径被设置为 batch_changed=True,可能会在实时流量中产生初始化成本。

当以下情况发生时,此预热过程将被跳过:

  • VLLM_SKIP_WARMUP 被设置为 true。
  • 引擎配置为强制执行即时执行模式,在该模式下不需要进行图捕获或编译,并且采样器仍在首次按需运行时运行,但没有单独的预热调用。

引入新的采样行为时,例如 nucleus 过滤、惩罚或投机性元数据,请更新 sampling_configswarmup_sampler 中,以确保相应的图路径被预编译并准备就绪。

解码分桶配置环境变量间接决定了采样器预热哪些批次大小,因为采样器从解码分桶派生其测试批次大小。

执行预热

warmup_sampler 中实现,预热例程系统地遍历采样堆栈,处理一系列笛卡尔乘积模式,例如批次大小、温度、top-p 和 top-k,以及一个指示批次大小是否已更改的标志。要执行预热,请遵循以下步骤:

  1. 通过在不同的解码分桶批次大小前面加上 [0, 1] 来构建一个测试批次大小列表,因为这些批次必须始终进行预热。

  2. 定义 12 种采样配置,涵盖以下设置。每种配置都应出现两次——一次 batch_changed=True,一次 batch_changed=False——以处理与批次大小调整相关的任何内部快速路径或缓存失效逻辑。

  • 贪婪:temperature=0.0
  • 典型随机采样:temperature=1.0
  • 创意设置:0.7/0.9/top-k=50
  • 保守设置:0.3/0.95/top-k=20
  • 高温度设置:1.2/0.8/top-k=100
  • 仅 top-p 变体:例如 0.8/0.85/top-k=0
  1. 为每个批次大小准备虚拟数据,创建一个形状为 (batch_size, hidden_size) 的隐藏状态张量,并使用 model.compute_logits 计算 logits。

  2. 为每个批次大小实例化至少一个虚拟请求对象,提供占位符提示 token 和一个 KV 块。

  3. 对于每种配置,请遵循以下子步骤:

    1. 更新 SamplingParams,例如每个请求中的 temperaturetop_ptop_k
    2. 将请求标记为贪婪或随机以测试分支。
    3. 用填充的占位符填充 req_output_token_ids 并刷新内部采样元数据。
    4. 调用 _run_sampling 并传递 batch_changed,以便编译或执行已更改和未更改的批次大小代码路径。
    5. 重置每个迭代的采样器簿记集或列表。
  4. 完成一个批次大小的所有采样配置后,清除请求映射并继续。

  5. 执行 HPU 同步并记录成功。

日志

下面的示例展示了预热期间出现的典型日志序列。

INFO 09-22 16:39:42 [hpu_model_runner.py:3347] Warming up sampler with batch sizes: [0, 1, 138] and following configs:
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=0.0, top_p=1.0, top_k=0, batch_changed=True
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=1.0, top_p=1.0, top_k=0, batch_changed=True
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=0.7, top_p=0.9, top_k=50, batch_changed=True
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=0.3, top_p=0.95, top_k=20, batch_changed=True
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=1.2, top_p=0.8, top_k=100, batch_changed=True
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=0.8, top_p=0.85, top_k=0, batch_changed=True
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=0.0, top_p=1.0, top_k=0, batch_changed=False
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=1.0, top_p=1.0, top_k=0, batch_changed=False
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=0.7, top_p=0.9, top_k=50, batch_changed=False
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=0.3, top_p=0.95, top_k=20, batch_changed=False
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=1.2, top_p=0.8, top_k=100, batch_changed=False
INFO 09-22 16:39:42 [hpu_model_runner.py:3349] temp=0.8, top_p=0.85, top_k=0, batch_changed=False
INFO 09-22 16:39:42 [hpu_model_runner.py:3350] Starting sampler warmup...
INFO 09-22 16:39:43 [hpu_model_runner.py:3411] Sampler warmup completed successfully

如果全局跳过了预热,则不会显示这些日志。

碎片整理器预热

碎片整理器通过交换很少使用的、索引较高的块与较低的空闲索引来回收和压缩运行时稀疏的 KV 缓存块使用。其预热阶段会预编译小型交换图,以便后续的在线碎片整理能够以近乎零的图编译延迟执行。

当分配的最高块索引远高于实际使用的块数量(碎片化)时,碎片整理可能会在中途服务时触发。操作本身是一系列应用于键值缓存的交换内核。通过预热,所有代表性的填充大小都通过确定性的、最小化的交换提前编译。这确保了在线碎片整理成为一项可预测的、低延迟的维护任务。仅跳过碎片整理器预热不会损害正确性;它只会增加碎片化首次超过阈值(需要压缩)时出现偶发性延迟的风险。

省略预热的潜在后果包括:

  • 首次需要先前未见过的填充交换大小的碎片事件会在关键路径上触发图捕获和编译。
  • 编译延迟可能会表现为用户请求突然出现的尾部延迟峰值。
  • 不同进程中多个首次出现的交换大小可能都会触发单独的编译。

您可以禁用预热步骤本身或整个碎片整理功能。要跳过所有预热阶段,包括碎片整理器,请将 VLLM_SKIP_WARMUP 设置为 true。此外,如果您的执行模式支持,您可以通过设置 VLLM_DEFRAG_WITH_GRAPHS=false 来避免碎片整理器交换的图编译。这会导致交换回退到常规执行,而预热仍然会执行它们,而不会触发图捕获。

相关的环境变量

  • VLLM_DEFRAG_THRESHOLD:设置碎片触发启发式。默认值为 32;较低的值会使压缩更激进。
  • VLLM_DEFRAG_WITH_GRAPHS:确定交换路径是编译还是进行图处理。默认情况下,这遵循 bridge_mode == eager
  • VLLM_DEBUG=defrag:启用详细的碎片调试日志记录。
  • VLLM_SKIP_WARMUP:禁用所有预热阶段,包括碎片整理。

注意

禁用碎片整理器预热并不会关闭碎片整理本身。它只是跳过了预编译的图准备,这可能会将编译成本转移到首次实时碎片化事件。

碎片整理器预热过程

在主预热 (warmup_model) 期间,系统会在初始化 KV 缓存和碎片整理器后调用内部 warmup_defragmenter 方法。该过程通过以下预热步骤定义:

  1. 确认碎片整理器预热功能已启用,并且 cache_utils 交换实用程序已准备就绪。
  2. 建立填充阈值列表:[8, 16, 32, 64, 128, 256, 512]
  3. 选择一个最小有效的交换对 [(1, 0)],具有两个不同的块 ID。只需要两个实际的块。内部,每次交换调用都会填充到当前阈值长度,以便为该精确的填充大小生成编译好的图。
  4. 遍历每个阈值并调用一次交换。这会捕获或编译(取决于执行模式)该填充大小的交换图。
  5. 在阈值数量为奇数的情况下,多执行一次使用第一个阈值的交换。这会导致交换序列将 KV 缓存恢复到其原始状态(净逻辑变化为零)。
  6. 完成日志记录。

未来的碎片整理交换请求总是四舍五入或填充到这些已知阈值之一。所有操作的交换大小都会命中预编译路径,并避免按需编译延迟。

日志

以下示例展示了当至少有两个 KV 缓存块可用时出现的典型日志序列。

INFO 09-22 16:26:24 [hpu_model_runner.py:3428] Warming up defragmenter with thresholds: [8, 16, 32, 64, 128, 256, 512]
INFO 09-22 16:26:27 [hpu_model_runner.py:3452] Defragmenter warmup completed successfully

如果块数不足,例如测试配置极小或分配失败,预热将被优雅地跳过,您可能会看到类似以下示例的日志:

INFO 09-22 16:26:24 [hpu_model_runner.py:3428] Warming up defragmenter with thresholds: [8, 16, 32, 64, 128, 256, 512]
WARNING hh:mm:ss hpu_model_runner.py:#### Skipping defragmenter warmup, insufficient blocks (1)

要发出实时碎片整理的精细调试消息(而不仅仅是最小的预热交换),请将 VLLM_DEBUG=defrag 添加到环境变量中。这样您就可以看到交换的块数和压缩后的统计信息。