文章

Serverless GPU 推理冷启动治理:用 Warm Pool 与分阶段预热降低首次延迟

深入剖析 Serverless GPU 场景下大模型冷启动的六阶段瓶颈,从 Warm Pool 分层策略、分阶段预热机制到工程落地架构与指标体系,帮助团队在 GPU 成本与首 Token 延迟之间找到可验证的平衡方案。

背景问题:Serverless 省钱,但冷启动会放大首 Token 延迟

大模型推理团队越来越青睐 Serverless GPU 或弹性 GPU 池,因为它能把低峰期闲置成本降下来:没有请求时缩容,有突发流量时再拉起 GPU worker。问题在于,大模型服务的冷启动不是普通 Web 函数的冷启动。普通函数可能只是拉起容器、加载依赖;LLM 推理还要处理镜像启动、模型权重下载、权重从磁盘到 CPU 内存再到 GPU 显存、runtime 初始化、tokenizer 或推理服务预热、首个请求排队等步骤。

Modal 的冷启动文档把额外延迟拆成两类:一类是请求等待 warm container 的排队时间,另一类是新容器首次处理请求时必须完成的初始化工作;文档还明确指出,大模型权重如果在启动时从远端下载,可能把启动时间从秒级拉到分钟级,而提前下载或放入镜像 / Volume 可以显著降低启动耗时。Ray Serve 的 autoscaling 文档也强调,自动扩缩容会根据请求队列做决策,min_replicas=0 适合长时间无流量且能接受扩容尾延迟的场景。这两个事实放在一起,说明冷启动治理不能只靠”开自动扩容”,还要把模型生命周期拆开治理。

本文讨论的不是 Continuous Batching、PagedAttention、Prefix Caching 或 LoRA adapter 缓存,而是一个更靠近平台层的问题:当某个模型、租户或版本从冷状态被重新唤醒时,如何让首次请求不要被模型加载拖垮,同时又不把所有 GPU 长期预留成热实例。

冷启动到底慢在哪里

对 LLM Serverless 推理来说,冷启动可以拆成六段:

阶段典型问题关键治理动作
调度阶段请求没有可用 warm worker,只能等待新 worker 启动Warm Pool、buffer worker、预测式预热
容器阶段镜像拉取、依赖加载、Python import 慢预构建镜像、裁剪依赖、Memory Snapshot
权重阶段模型权重从对象存储或远端仓库读取本地 SSD 缓存、Volume、分片并行读取
GPU 阶段权重搬到显存、CUDA runtime 初始化预加载、显存保留、分层驱逐
服务阶段tokenizer、server、路由、健康检查尚未就绪readiness gate、warmup request、影子探测
首请求阶段首个真实请求承担编译、kernel、cache 初始化成本synthetic warmup、首请求隔离、冷/热指标拆分

很多团队只盯着”容器启动时间”,但大模型推理的更大成本经常在模型权重加载GPU 显存恢复。ParaServe 论文指出,Serverless LLM serving 的冷启动延迟会受到大模型体积影响,其中从远端存储获取模型是主要瓶颈之一;它通过并行化模型参数获取、加载和 runtime 初始化来降低冷启动时间。Tangram 则从另一个角度处理问题:利用 GPU 显存复用和 GPU affinity,让冷模型再次启动时尽量少搬运参数。

核心原理:把冷启动看成”模型生命周期流水线”

冷启动治理的核心不是某个单独参数,而是把模型从”不可服务”到”稳定服务”的过程定义成明确状态机:

COLD -> IMAGE_READY -> WEIGHTS_ON_DISK -> WEIGHTS_IN_CPU_MEMORY
     -> WEIGHTS_ON_GPU -> RUNTIME_READY -> WARMED -> SERVING
     -> IDLE -> EVICTING -> COLD

每个状态都要有可观测指标、超时阈值和回退动作。例如,WEIGHTS_ON_DISK 超时通常意味着对象存储、镜像层或本地缓存异常;WEIGHTS_ON_GPU 超时可能是 GPU 内存不足、驱逐策略过慢或并发加载太多;RUNTIME_READYWARMED 之间如果失败,往往说明健康检查过早放行,真实请求被迫承担预热成本。

把冷启动拆成状态机之后,平台就能做三件事:

  1. 把冷启动成本前移:在部署、扩容或流量预测阶段完成模型下载、权重加载和 warmup。
  2. 把冷启动成本隔离:不要让真实用户的第一个请求承担所有初始化成本。
  3. 把冷启动成本显性化:把 cold TTFT、warm TTFT、model load time、queue wait 分开统计。

Warm Pool:不是永远开着 GPU,而是保留”可快速接管”的热容量

Warm Pool 的常见误解是”把 min_replicas 调大”。这只是最粗的办法。真正的 Warm Pool 需要回答四个问题:哪些模型值得常驻、保留到生命周期的哪一层、由什么事件触发预热、什么时候驱逐。

层级策略适用对象
Hot常驻 GPU,完整 warmed高频主模型、核心付费租户、低延迟 API
Warm容器或权重常驻,但不一定占满 GPU中频模型、工作时间高峰、可预测批量任务
Cold完全按需加载长尾模型、低频实验模型、内部离线任务

这不是简单的 QPS 排名,还要看模型大小、加载时间、显存占用、SLO 级别、租户价值和是否存在周期性流量。

不是所有模型都要做到 WEIGHTS_ON_GPU。有些模型只需要做到 WEIGHTS_ON_DISK,这样既能避免远端下载,又不占 GPU 显存;有些高价值模型需要做到 RUNTIME_READYWARMED,保证真实请求进来时马上能服务。

warm_pool_policy:
  hot_models:
    target_state: WARMED
    min_workers: 2
    scaledown_window_seconds: 1800
  warm_models:
    target_state: WEIGHTS_ON_DISK
    min_workers: 0
    prefetch_window_minutes: 15
  cold_models:
    target_state: COLD
    min_workers: 0
    max_cold_start_seconds: 120

预热信号可以来自部署事件、历史流量、业务活动和实时队列。驱逐则不应该只看 idle time,还要看模型大小、冷启动代价、租户等级、当前 GPU 池压力和刚驱逐又被请求的概率。

Tangram 和 CrossPool 这类研究都指向一个共同事实:LLM 冷启动和多模型服务的核心瓶颈之一是 GPU 内存中”静态权重”和”动态请求状态”的管理。工程实现不一定照搬论文系统,但应该吸收它们的思想:让权重、KV cache、worker 生命周期和调度亲和性分开治理。

分阶段预热:不要让第一个真实请求做系统健康检查

一个可发布的 Serverless GPU 推理服务,至少应有四级预热。

构建期预热

构建期不跑 GPU,但要尽量把慢 IO 从运行时拿掉:固定镜像基础层、提前下载 tokenizer 和 config、把不变依赖固化到 image 或 Volume、记录模型 artifact digest,禁止运行时漂移。

启动期预热

启动期要把容器从”启动完成”推进到”可服务”。

class LLMWorker:
    def __init__(self, model_path: str):
        self.model_path = model_path
        self.ready = False
        self.model = None

    def startup(self):
        self.tokenizer = load_tokenizer(self.model_path)
        self.model = load_model_from_local_cache(self.model_path)
        self.model.to("cuda")
        self.model.eval()
        self.warmup()
        self.ready = True

    def warmup(self):
        prompt = "warmup"
        _ = self.model.generate(prompt, max_new_tokens=1)

注意,ready = True 必须放在 warmup 之后。否则服务发现层会把真实流量送进一个”容器活着但模型未热”的 worker。

发布期预热

新模型版本先进入 shadow warmup,不接真实流量;warmup 通过后再切 1% 流量;同时比较 cold TTFT、warm TTFT、错误率和 GPU memory peak。回滚时保留上一版本 warm worker 一段时间,避免回滚也冷启动。

流量期预热

流量期要面向突发:pending queue 达到阈值时,不只扩 replica,还要提前加载对应模型;对周期性流量使用 scheduled warmup;对高价值租户设置更高的 warm priority;对长尾模型使用本地权重缓存,而不是常驻 GPU。

工程落地架构

推荐把冷启动治理拆成五个组件:

  1. Traffic Classifier:识别模型、租户、SLO、请求类型。
  2. Warm Pool Manager:决定哪些模型保持在哪个状态。
  3. Artifact Cache Manager:管理镜像层、模型权重、本地 SSD、Volume。
  4. GPU Residency Controller:决定哪些权重留在 GPU、哪些降级到 CPU 或磁盘。
  5. Readiness & Warmup Gate:只有预热通过才允许真实流量进入。
Request -> Gateway -> Traffic Classifier -> Warm Pool Manager
       -> Worker Pool -> Readiness Gate -> LLM Runtime

Artifact Store -> Local Cache -> CPU Memory -> GPU Memory

这套架构的关键是:网关不应该只知道有多少 worker,调度器也不应该只知道 GPU 是否空闲。它们都需要知道模型是否热、权重在哪里、当前冷启动成本是多少。

指标体系:必须区分 cold 和 warm

如果只看平均延迟,冷启动问题会被掩盖。建议至少拆出以下指标:

指标含义用途
cold_start_rate请求命中冷 worker 的比例判断 Warm Pool 是否足够
cold_ttft_p95冷启动请求的 P95 首 Token 延迟衡量用户最差体验
warm_ttft_p95热请求的 P95 首 Token 延迟排除冷启动后的模型服务质量
model_load_time权重加载耗时定位远端存储、磁盘、CPU/GPU 搬运问题
queue_wait_time请求等待 worker 的时间判断扩容是否滞后
warmup_failure_rate预热失败比例防止未就绪 worker 接流量
warm_pool_costwarm worker 空闲成本评估成本和延迟取舍
eviction_miss_rate刚驱逐又被请求的比例调整驱逐策略

上线报表里要把这些指标按模型、租户、版本、GPU 池、区域拆开。冷启动通常不是全局平均问题,而是某几个模型或租户在特定时间段被反复击中。

适用场景

这套方法适合以下场景:

  • 多模型平台,只有少数模型高频,其余模型长尾。
  • 企业客户使用独立模型版本或专属 LoRA / adapter。
  • 推理平台允许 scale-to-zero,希望降低 GPU 空闲成本。
  • 模型体积较大,首次加载显著影响 TTFT。
  • 线上有明显周期性流量,例如工作时间、活动时间、批处理窗口。
  • 发布新模型时,首批用户经常遇到明显慢请求。

不适合的场景也很明确:如果服务必须全天候低延迟,且模型数量少、流量稳定,直接固定副本可能更简单。Warm Pool 的价值在于处理”成本不允许全热,但体验不允许全冷”的中间状态。

常见误区

误区一:只要自动扩容就能解决冷启动

自动扩容解决的是容量不足,不一定解决模型加载慢。Ray Serve 可以根据队列扩缩副本,但如果副本启动后还要从远端拉模型,用户仍然会等。

误区二:把所有模型都放进 GPU 才叫 Warm Pool

GPU 显存最贵。长尾模型更适合做到 WEIGHTS_ON_DISKWEIGHTS_IN_CPU_MEMORY,只有高频模型才需要 WEIGHTS_ON_GPUWARMED

误区三:健康检查通过等于模型可服务

容器 alive 不代表模型 warmed。健康检查至少要区分 process_aliveruntime_readymodel_loadedwarmup_passed

误区四:只看 P95 延迟就够了

冷启动通常影响小比例请求,但这些请求体验很差。必须单独统计 cold path 指标,否则平均值会误导决策。

上线检查清单

  • 模型权重是否有固定 digest。
  • 镜像是否包含必要依赖,避免启动时临时安装。
  • tokenizer、config、权重路径是否可从本地缓存读取。
  • warmup request 是否覆盖真实推理路径。
  • readiness gate 是否在 warmup 之后才放行。
  • 新旧版本是否能同时保留 warm worker。
  • 是否定义 Hot / Warm / Cold 模型分层。
  • 是否设置每层的目标状态和最大成本。
  • 是否为高价值租户设置 warm priority。
  • 是否有 GPU 显存水位和驱逐策略。
  • 是否限制并发模型加载数量,避免 IO 风暴。
  • 是否区分 cold TTFT 和 warm TTFT。
  • 是否记录 model load time、queue wait、startup pending。
  • 是否有 warmup failure 告警。
  • 是否记录 warm pool 空闲成本。

参考资料

常见问题

Serverless GPU 推理一定会有冷启动吗?
只要服务允许 scale-to-zero 或模型需要按需加载,就会存在冷启动风险;区别在于冷启动发生在容器、模型权重、GPU runtime 还是请求队列阶段。稳定低延迟服务通常不会完全消除冷启动,而是把冷启动概率和影响范围控制在可接受范围内。
Warm Pool 是不是把 min replicas 调高就够了?
不是。min_replicas 只能保留一定数量的 worker,不能自动解决模型权重在哪里、是否已经搬到 GPU、是否完成 warmup、是否适合当前租户和版本等问题。生产系统需要把 Warm Pool、artifact cache、GPU residency 和 readiness gate 一起设计。
如何判断冷启动治理是否有效?
不能只看平均延迟。至少要同时观察 cold_start_rate、cold_ttft_p95、warm_ttft_p95、model_load_time、queue_wait_time、warmup_failure_rate 和 warm_pool_cost。有效的治理应该让冷启动比例下降、冷路径尾延迟降低,同时 warm pool 成本保持在预算内。