AI研究

llama.cpp冷启动最快约30秒但吞吐只有vLLM的约1/3、TTFT约5倍,同一个模型同一个GPU换引擎就从“能用”变成“不想用”,推理框架的选择到底该按“启动速度”还是“持续吞吐”来定?

👤 为我痴狂 👁 2 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-11-09
首页› AI› AI研究› 正文
I N F E R E N C E   E N G I N E   T R A D E O F F
启动速度与持续吞吐的二元权衡
llama.cpp 与 vLLM 的工程选型解剖
摘 要 / ABSTRACT

同一块 GPU、同一份模型权重,llama.cpp 冷启动可压缩到约 30 秒量级,而 vLLM 往往需要数分钟;反过来,在并发请求下 vLLM 的持续吞吐可达 llama.cpp 的约 3 倍,首 token 延迟(TTFT)差距接近 5 倍。这种“启动快则吞吐弱、吞吐强则启动慢”的现象并非实现缺陷,而是两套引擎在内存管理、批处理调度与量化内核上的结构性取舍。本文以“启动速度—持续吞吐”二元权衡为分析主线,逐层拆解 KV Cache 分页、连续批处理、CUDA Graph、量化内核与冷启动链路,给出可复现的基准测试方法与按场景选型的决策矩阵,并讨论投机解码、分离式推理等前沿方向对这一权衡的重塑。

01  问题的提出:一次“换引擎即换体验”的现场复盘

先把场景摆出来。一台单卡工作站,一张 24GB 显存的消费级或入门级专业卡,加载一个 7B~14B 量级的开源权重。用 llama.cpp 的 llama-server 起服务,从进程拉起、权重映射、上下文初始化到端口可用,熟练配置下可以压到 30 秒上下;换成 vLLM,同样的权重、同样的卡,冷启动往往要几分钟,中间还伴随显存预分配、CUDA Graph 捕获、算子编译/预热等动作。可一旦服务真正跑起来、并发请求压上去,画风立刻反转:vLLM 的持续吞吐大约是 llama.cpp 的 3 倍,首 token 延迟(Time To First Token,TTFT)差距接近 5 倍。

于是就有了那个非常真实的抱怨:同一个模型、同一块 GPU,换个引擎就从“能用”变成“不想用”。这句话里其实藏着两个不同的“不想用”——启动慢的时候不想用,是因为迭代调试、弹性伸缩、故障恢复都被拖住了;吞吐低的时候不想用,是因为一旦有真实并发,排队时间迅速劣化,用户体验断崖式下跌。二者指向的并不是同一个工程目标。

笔者认为,把这个问题简化成“llama.cpp 和 vLLM 谁更好”是一次范畴错误。它们优化的目标函数根本不同:llama.cpp 的基因是“在尽可能广的硬件与尽可能轻的依赖上,把模型跑起来”,vLLM 的基因是“在数据中心级 GPU 上,把每一滴显存和每一个 SM 周期都榨成吞吐”。前者天然偏向冷启动与部署灵活性,后者天然偏向持续吞吐。选型的本质,是判断你的负载形态更靠近哪一端,而不是给两个引擎排座次。

“启动速度”和“持续吞吐”不是同一维度的指标,把它们放进同一张排行榜比较,就像用百米成绩去评价马拉松选手的耐力——数字都是真的,结论却会误导决策。

为了让后面的讨论有落点,这里先给出本文的分析主线:推理引擎的性能由“内存管理策略 × 批处理调度策略 × 内核与图优化程度”三者共同决定,而这三者恰恰是冷启动成本的主要来源。换句话说,吞吐优势与启动劣势在多数情况下是同一套设计的两面,而不是可以随意拆开的独立开关。理解了这条因果链,才能判断哪些差距可以被配置抹平、哪些只能靠架构演进消解。

02  背景与概念界定:两种引擎的设计哲学分野

2.1 先把几个指标定义清楚

讨论性能之前必须先统一口径,否则“3 倍”“5 倍”这类数字很容易被误读。本文采用如下定义:

  • 冷启动时间(Cold Start):从进程创建到服务端能够接受并正确响应第一个请求的总耗时,包含权重加载、显存/内存分配、图捕获、预热等全部阶段。
  • TTFT(Time To First Token):请求到达至首个输出 token 返回的时延,主要由预填充(prefill)阶段决定,与输入长度强相关。
  • TPOT(Time Per Output Token):解码阶段每个输出 token 的平均间隔,反映单请求的生成速度。
  • 持续吞吐(Sustained Throughput):在稳定并发负载下单位时间完成的总 token 数(输入+输出),通常以 tokens/s 计,是衡量单位算力经济性的核心指标。

这四个指标并非彼此独立。TTFT 与吞吐在批处理框架下存在直接张力:把更多请求塞进同一批次可以提升吞吐,但会拉长每个请求的排队与预填充等待,推高 TTFT。这个张力在后面的章节会反复出现。

2.2 llama.cpp:从“能在任何地方跑”出发

llama.cpp 最初以纯 C/C++ 实现、零重依赖为目标,核心卖点是在 CPU、Apple Silicon、消费级 GPU 乃至各类边缘设备上以量化形式运行 LLaMA 系模型。它的 ggml 后端支持多种量化格式(Q4_K_M、Q5_K_M、Q8_0 等),并逐步扩展出 CUDA、Metal、Vulkan、ROCm 等后端。其服务端组件 llama-server 提供 OpenAI 兼容接口,但调度层相对朴素:早期以单序列或简单并行槽位(slot)为主,后续版本引入了基于 slot 的连续批处理雏形,但整体仍以“低依赖、易部署、单机可用”为优先。

本文评述:llama.cpp 的工程价值不在于峰值性能,而在于它把“推理可用性”的下限抬得极高。一个没有 CUDA 工具链、没有 Python 环境、甚至没有 root 权限的机器,编译一个二进制加一个 gguf 文件就能跑。这种设计选择必然牺牲数据中心级的调度复杂度——而调度复杂度恰恰是吞吐的主要来源之一。

2.3 vLLM:以 PagedAttention 为原点的吞吐机器

vLLM 的核心贡献是 PagedAttention(Kwon 等,SOSP 2023),它把 KV Cache 按固定大小的块(block)管理,像操作系统虚拟内存分页一样按需分配、非连续存放,从而把 KV Cache 的显存浪费从传统的 60%~80% 压到个位数百分比。在此之上,vLLM 实现了连续批处理(continuous batching)、前缀缓存(prefix caching)、CUDA Graph 捕获、chunked prefill 等一整套面向吞吐的机制。这些机制共同构成了它对 llama.cpp 的吞吐优势,也共同构成了它冷启动更重的原因。

笔者认为,理解 vLLM 的关键不是记住它有哪些特性,而是理解它的默认假设:显存是稀缺且需要被精确调度的资源,请求是持续到达且长度不一的,GPU 的空转是最大的浪费。所有机制都是为这个假设服务的。一旦你的场景不满足这个假设——比如单用户、低并发、频繁重启——这些机制就从优势变成了负担。

设 计 哲 学 对 照
维度 llama.cpp vLLM
首要目标可移植性与低依赖GPU 吞吐与显存效率
KV Cache 管理预分配连续缓冲分页块管理(PagedAttention)
批处理slot 并行,调度较简连续批处理 + chunked prefill
量化ggml 量化族,CPU/GPU 通吃以 FP16/BF16 为主,AWQ/GPTQ/FP8 支持
启动路径mmap 权重 + 轻量初始化显存预分配 + 图捕获 + 预热

03  原理拆解:吞吐差距的技术根源

要解释“约 3 倍吞吐、约 5 倍 TTFT”这个量级的差距,必须回到 GPU 的执行模型。解码阶段的自回归生成是典型的内存带宽受限(memory-bound)负载:每生成一个 token,都要把全部权重从显存读一遍。单请求时,GPU 的算力单元大量闲置,瓶颈在带宽;只有当多个请求被批处理、共享同一次权重读取时,算力利用率才会上升。这就是批处理提升吞吐的第一性原理。

3.1 KV Cache 的浪费率决定了你能塞进多少并发

传统实现中,KV Cache 按“最大序列长度 × 最大并发数”预分配连续显存。但真实请求长度分布高度长尾:多数请求很短,少数很长。预分配导致大量显存被闲置的“预留位”占据,可用并发数被严重压低。PagedAttention 的论文给出的实测是,既有系统在 KV Cache 上的显存浪费可达 60%~80%(Kwon et al., 2023)。vLLM 通过块表(block table)把逻辑连续的 KV 映射到物理非连续的块,浪费率降到个位数百分比,等效于可用批大小成倍提升。

本文评述:这个机制的价值常被简化为“省显存”,但更准确的表述是“把省下来的显存转化为并发度”。在内存带宽受限的解码阶段,并发度几乎线性地转化为吞吐——这正是 3 倍差距的主要来源之一。llama.cpp 的 slot 机制虽然也支持并行,但其 KV 缓冲的分配粒度更粗,长上下文场景下的浪费更明显,能稳定支撑的并发槽位数量受限。

3.2 连续批处理:让 GPU 不因“等一个请求”而空转

静态批处理要求一个批次内所有请求同时开始、同时结束,短请求必须等长请求,GPU 在批次尾部大量空转。连续批处理(continuous batching,又称 iteration-level scheduling)在每个解码步重新组装批次:已完成的请求立即退出,新到达的请求立即加入。Orca(Yu et al., OSDI 2022)系统性地提出了这一调度思想,vLLM 将其工程化并默认开启。

效果是直接的:在请求长度方差大的真实负载下,连续批处理能把 GPU 有效利用率从静态批处理的低位显著拉高。TTFT 的改善则来自另一个机制——chunked prefill,它把长输入的预填充切成小块,与解码请求交错执行,避免一个长 prefill 阻塞所有正在解码的请求。这解释了为什么 vLLM 的 TTFT 能比 llama.cpp 低近 5 倍:差距不只在单次 prefill 的速度,更在排队策略。

3.3 CUDA Graph 与前缀缓存:把重复开销摊薄

解码每一步的算子启动(kernel launch)开销在小模型、短序列时不可忽略。CUDA Graph 把一整步的计算图预先捕获为一张图,后续以单次图启动替代数十次 kernel launch,降低 CPU 侧调度开销与 GPU 空闲间隙。前缀缓存则让相同系统提示词的多个请求复用已计算的 KV,避免重复 prefill。二者都是“用启动期的准备换运行期的效率”的典型手段——注意这个句式,它会在第 4 章原样回响。

笔者认为,这三类机制可以统一为一个判断:vLLM 的吞吐优势,本质是把“请求间可共享的计算与内存”尽可能共享,把“请求间的等待”尽可能消除。共享需要索引结构(块表、前缀哈希),消除等待需要调度器状态机,这些结构都需要在启动时构建、在运行时维护,成本是真实存在的。

3.4 量化内核:llama.cpp 的隐性优势与显性代价

llama.cpp 的 ggml 量化内核(尤其是 K-quants 系列)在低比特下保持了很好的精度-速度平衡,且对 CPU 与统一内存架构(如 Apple Silicon)做了深度优化。在显存受限的单卡上,Q4 量化让更大的模型得以装入,这本身是吞吐之外的另一种“可用性”。但量化内核的多样性也意味着:不同量化格式走不同的反量化路径,部分格式在 GPU 上的矩阵乘效率不及 FP16/BF16 的原生 Tensor Core 路径。vLLM 主推 FP16/BF16 与 AWQ/GPTQ/FP8,其内核针对数据中心 GPU 的 Tensor Core 做了更集中的优化。

本文评述:这里存在一个常被忽略的公平性问题。拿 llama.cpp 的 Q4 量化对比 vLLM 的 FP16,吞吐差距里有一部分来自精度差异而非引擎差异;反过来,拿两者同为 FP16 对比,llama.cpp 的显存占用劣势又会放大。严谨的对比必须锁定精度与量化格式,这一点在第 5 章的方法论里会具体展开。

04  冷启动链路解剖:30 秒与数分钟之间发生了什么

现在把镜头切到启动阶段。冷启动不是单一动作,而是一条由多个阶段串成的链路,每个阶段的耗时来源不同,可优化空间也不同。

冷 启 动 阶 段 拆 解(示 意)
阶段 llama.cpp 典型行为 vLLM 典型行为
进程与运行时单二进制,秒级Python 运行时 + 依赖导入,数十秒
权重加载mmap 映射 gguf,按需缺页读 safetensors 并拷入显存
显存分配按上下文与槽位预分配按 gpu_memory_utilization 预占大块显存
图/内核准备惰性,首次请求时编译/加载CUDA Graph 捕获 + 预热前向
就绪判定端口监听即可完成 profiling 与图捕获后
注:上表为定性归纳,具体耗时随版本、模型规模、硬件与配置变化,实际数值须以本地实测为准。

可以看到,vLLM 的启动慢并非“代码写得慢”,而是它把大量运行期优化提前到了启动期:显存预分配是为了避免运行中碎片化,CUDA Graph 捕获是为了消除每步的 kernel launch 开销,profiling 是为了确定安全的批大小上限。这些动作在服务生命周期内会被摊薄——服务跑得越久、负载越重,单位请求分摊的启动成本越低。反之,如果服务频繁重启、生命周期只有几分钟,这些成本就永远摊不薄。

本文评述:这引出一个非常实用的判断准则——启动成本是否值得,取决于服务生命周期内能承载多少请求。设冷启动成本为 C,稳态吞吐为 T,则启动开销的“摊销阈值”大致是服务需要处理约 C×T 个 token 才能回本。对一个每天重启数十次的调试环境,这个阈值永远达不到;对一个 7×24 的在线服务,它几分钟就跨过了。这条准则比任何排行榜都更贴近真实决策。

llama.cpp 的 mmap 加载是另一个值得单独说的点。gguf 文件通过内存映射按需缺页,意味着进程拉起时并不需要把全部权重读进内存,首请求触发缺页后再逐步加载。这让“端口可用”的时间大幅提前,但也意味着第一个请求可能比后续请求慢——冷启动快,冷首请求未必快。工程上如果只测“服务起来没有”,很容易高估 llama.cpp 的实际就绪速度。

05  可复现的基准方法:如何测出属于你自己的数字

“3 倍”“5 倍”这类数字如果不带条件,几乎没有决策价值。本节给出一套可复现的基准流程,目标是让读者在自己的硬件与负载上得到可信的对比数据,而不是照搬别人的结论。

5.1 控制变量清单

  • 模型与精度:同一权重来源,锁定等效精度。若必须使用不同量化格式,需在报告中明确标注,并单独做一组同精度对照。
  • 输入/输出长度分布:固定 prompt 长度与 max_tokens,或使用可复现的长度分布(如对数正态),避免用单一长度代表全部。
  • 并发模型:明确是固定并发(closed-loop)还是固定到达率(open-loop)。两者测出的 TTFT 分布差异很大。
  • 预热策略:区分冷启动首请求、预热后稳态、以及长时间运行后的稳态,三者分开报告。
  • 硬件与驱动:GPU 型号、显存、驱动版本、CUDA 版本、是否启用 ECC、是否共享 GPU。

5.2 一个最小可用的压测脚本骨架

下面给出一个基于 Python 的 open-loop 压测骨架,核心是控制到达率并记录每个请求的 TTFT 与总耗时。实际使用时替换 endpoint 与 payload 即可。

import asyncio, time, json, aiohttp, statistics

ENDPOINT = "http://127.0.0.1:8000/v1/completions"
PROMPT = "Explain the difference between paged attention and contiguous KV cache."
RATE = 4          # 每秒到达请求数
DURATION = 60     # 压测时长(秒)
MAX_TOKENS = 256

async def one_request(session, idx, results):
    payload = {
        "model": "local",
        "prompt": PROMPT,
        "max_tokens": MAX_TOKENS,
        "stream": True,
    }
    t0 = time.perf_counter()
    ttft = None
    async with session.post(ENDPOINT, json=payload) as resp:
        async for raw in resp.content:
            line = raw.decode("utf-8", "ignore").strip()
            if not line.startswith("data:"):
                continue
            body = line[5:].strip()
            if body == "[DONE]":
                break
            if ttft is None:
                ttft = time.perf_counter() - t0
    total = time.perf_counter() - t0
    results.append((idx, ttft, total))

async def main():
    results = []
    async with aiohttp.ClientSession() as session:
        tasks = []
        for i in range(int(RATE * DURATION)):
            tasks.append(asyncio.create_task(one_request(session, i, results)))
            await asyncio.sleep(1.0 / RATE)   # 固定到达率
        await asyncio.gather(*tasks)
    ttfts = [r[1] for r in results if r[1] is not None]
    print("requests:", len(results))
    print("TTFT p50/p95:",
          round(statistics.median(ttfts), 3),
          round(sorted(ttfts)[int(len(ttfts)*0.95)], 3))
    print("throughput tok/s:",
          round(len(results) * MAX_TOKENS / DURATION, 1))

asyncio.run(main())

这个脚本的关键设计点有三:固定到达率而非固定并发,能暴露排队效应;流式读取以精确捕捉首 token 时刻;分别统计 p50 与 p95,因为均值会掩盖长尾。实际工程中还应加入错误率统计与超时控制。

5.3 冷启动测量:别只测“端口起来”

冷启动测量建议分三个时间点记录:进程创建到端口监听(T1)、端口监听到首个请求返回首 token(T2)、首个请求完成(T3)。T1 反映框架初始化成本,T2 反映预热与首次计算成本,T3 反映冷首请求的端到端体验。很多“30 秒启动”的说法只覆盖 T1,而用户真正感知的是 T1+T2。建议在报告中同时给出三者,并注明是否清空了页缓存(echo 3 > /proc/sys/vm/drop_caches),否则 mmap 的冷启动优势会被高估。

06  关键数据与案例:公开基准与工程实测的交叉验证

公开文献与社区基准给出的量级,与本文开头的经验数字基本吻合,但条件差异很大,需要逐条辨析。

6.1 学术侧:PagedAttention 与连续批处理的原始数据

PagedAttention 论文报告,在相同延迟水平下 vLLM 相比 FasterTransformer 与 Orca 可实现 2~4 倍的吞吐提升,且 KV Cache 浪费率显著降低(Kwon et al., SOSP 2023)。Orca 论文则报告,连续批处理相比静态批处理在真实负载下吞吐提升可达数倍(Yu et al., OSDI 2022)。这两项工作共同奠定了“调度即吞吐”的认知基础。

本文评述:这些数字的参照系是同期的高性能推理系统,而非 llama.cpp。把论文里的倍数直接套到 llama.cpp 头上是不严谨的。llama.cpp 的定位本就不是吞吐竞赛,它的对照系是“能不能跑”。因此本文开头“约 3 倍”的说法,应当理解为特定硬件、特定模型、特定并发下的量级估计,而非普适常数。

6.2 工程侧:社区基准的常见结论与陷阱

社区中大量对比帖给出的结论高度一致:低并发(1~4)时两者差距不大,甚至 llama.cpp 因量化优势在显存受限时更实用;并发上升到 16、32、64 时,vLLM 的吞吐优势迅速拉开,TTFT 的长尾差距尤其明显。这与第 3 章的原理分析完全自洽——批处理收益随并发度增长,而 PagedAttention 的价值也随并发度显现。

但社区基准常见三个陷阱:其一,用不同量化格式对比,结论被精度差异污染;其二,只报平均 TTFT 不报 p95/p99,掩盖了长尾;其三,忽略预热,把冷首请求混入稳态统计。笔者认为,任何一份有决策价值的对比报告,都应至少包含“同精度、分并发、报分位数、标注预热状态”这四项信息,否则只能当作定性参考。

量 级 对 照(模 拟 整 合 数 据,仅 示 意 趋 势)
并发 llama.cpp 相对吞吐 vLLM 相对吞吐 TTFT 差距趋势
11.00.9~1.1接近
81.42.6vLLM 略优
321.85.0vLLM 明显优
642.06.2差距扩大
注:本表为基于公开基准趋势的模拟整合数据,用于说明量级关系,不代表任何具体硬件或版本的实测结果。实际数值须以本地基准为准。

这张表想传达的核心信息不是具体数字,而是曲线的形状:低并发区间两者几乎重合,高并发区间迅速分叉。这意味着选型的分水岭不在“哪个引擎绝对更快”,而在“你的负载落在曲线的哪一段”。如果你的服务峰值并发长期低于 4,为 vLLM 的吞吐优势付出的启动与运维复杂度可能并不划算;如果你的服务常态并发在 32 以上,llama.cpp 的吞吐天花板会成为硬约束。

07  对比与局限:谁在什么边界条件下更优

7.1 边界条件一:服务生命周期

这是最容易被忽略却最决定性的变量。生命周期短于启动摊销阈值的场景——CI 中的模型回归测试、按需拉起的一次性推理任务、Serverless 冷启动、开发调试——llama.cpp 的优势是结构性的,不是调参能追上的。生命周期长于阈值的场景——7×24 在线 API、批量离线生成、多租户平台——vLLM 的吞吐优势会迅速覆盖启动成本。

本文评述:Serverless 推理是这条边界最尖锐的试金石。在按请求计费、实例频繁伸缩的环境里,冷启动时间直接换算成金钱和超时率。这也是为什么近年出现了一批专门优化冷启动的推理系统(如面向 Serverless 的权重加载与快照技术),它们试图打破“吞吐强则启动慢”的绑定,第 9 章会展开。

7.2 边界条件二:硬件与显存预算

显存越紧张,llama.cpp 的量化优势越突出。一张 16GB 卡上跑 13B 模型,FP16 根本装不下,Q4 量化后不仅装得下还有余量做上下文。此时讨论 vLLM 的吞吐优势没有意义,因为它压根跑不起来这个配置。反过来,在 80GB 卡上跑 7B 模型,显存充裕,vLLM 可以把批大小拉到很高,吞吐优势充分释放。

另一个硬件维度是统一内存架构。在 Apple Silicon 上,llama.cpp 的 Metal 后端经过长期打磨,是事实上的首选;vLLM 对非 NVIDIA 平台的支持在持续完善,但成熟度与生态仍有差距。选型不能脱离硬件栈谈引擎。

7.3 边界条件三:功能完备度与运维复杂度

吞吐不是唯一维度。vLLM 在张量并行、流水线并行、多卡多机、前缀缓存、结构化输出、投机解码等功能上更完整,适合构建平台级服务;llama.cpp 在依赖极简、单二进制分发、跨平台一致性上更强,适合嵌入与边缘部署。运维层面,vLLM 的 Python 依赖栈更重,版本兼容与镜像体积是需要计入的成本;llama.cpp 的部署则轻得多。

笔者认为,把这两者对立起来本身就是一种资源浪费。在成熟的生产架构里,它们更常见的角色是分工而非替代:用 vLLM 承载高并发的在线主链路,用 llama.cpp 承担边缘节点、本地开发、离线小批量与降级备份。这种“双引擎”策略的复杂度,远低于在两个都不完美的选项里强行二选一。

场 景 倾 向 速 查
场景 更倾向 关键理由
本地开发/调试llama.cpp频繁重启,启动成本主导
边缘/嵌入式llama.cpp低依赖、跨平台、量化
在线高并发 APIvLLM连续批处理 + 分页 KV
批量离线生成vLLM吞吐即成本,启动可摊销
Serverless 按需视冷启动优化而定需专门优化,见第 9 章

08  决策路径:把“选型”变成可执行的工程流程

把前面的分析收敛成一套可操作的流程。以下步骤按顺序执行,每一步都有明确的产出物,避免“凭感觉选型”。

步骤一:刻画负载画像

采集至少一周的真实请求日志,统计四个分布:输入长度分布、输出长度分布、并发到达率随时间的变化、请求的突发性(峰值/均值比)。如果还没有线上流量,用业务预估构造三档场景(保守/基准/激进)。产出物是一页负载画像文档,它是后续所有测算的输入。

步骤二:计算启动摊销阈值

用第 4 章的准则估算:服务平均生命周期内预计处理的 token 总量,与冷启动成本折算的等效 token 量对比。若前者远大于后者,启动成本可忽略,选型重心转向吞吐与功能;若两者接近或前者更小,启动成本必须进入决策函数。产出物是一个明确的“启动是否重要”的布尔判断。

步骤三:在目标硬件上跑对照基准

用第 5 章的方法,在真实硬件上对两个引擎做同精度、分并发(1/8/32/64)、报分位数的对照测试。特别关注 p95 TTFT 与峰值并发下的错误率。产出物是一张对照表,标注每组数据的条件。

步骤四:做单位成本测算

把吞吐换算成单位 token 成本,把启动成本按生命周期摊销进去,再加上运维人力与镜像/依赖维护成本。产出物是每百万 token 的综合成本对比。这一步常会推翻前面的直觉——某些场景下,vLLM 的高吞吐带来的成本优势,会被更重的运维负担部分抵消。

步骤五:设计降级与混合策略

无论主选哪个,都应设计降级路径:主链路用 vLLM 时,边缘与备份用 llama.cpp;主链路用 llama.cpp 时,预留切换到 vLLM 的接口与数据通道。产出物是一份包含主备切换、容量水位、回滚条件的运维手册。

选型不是一次性的技术判断,而是一个随负载演化的持续过程。今天合适的引擎,在并发翻倍后可能就不再合适。把基准流程固化进 CI,比一次性选对更重要。

09  结论与展望:权衡正在被什么力量改写

回到最初的问题:推理框架的选择该按“启动速度”还是“持续吞吐”来定?本文的答案是——按“启动成本能否在服务生命周期内被摊销”来定,再按摊销后的单位成本与功能需求做二次筛选。这不是一个二选一的问题,而是一个关于时间尺度的判断。短生命周期场景里,启动速度是硬约束;长生命周期场景里,持续吞吐是硬约束;介于两者之间的,需要实测数据说话。

更重要的是,这条权衡线本身正在被技术进步改写。三个方向值得持续关注:

其一,冷启动优化正在从“框架层”下沉到“系统层”。权重快照、内存快照恢复、按需分页加载、预编译内核缓存等技术,目标是把 vLLM 类系统的启动成本压到接近 llama.cpp 的量级。一旦这个方向成熟,“吞吐强则启动慢”的绑定会被显著削弱。

其二,分离式推理(disaggregated prefill/decode)正在重构资源模型。把预填充与解码拆到不同实例,各自按负载特征独立扩缩容,可以让 TTFT 与吞吐分别优化,而不是在同一实例上互相牵制。这一方向在近年的系统研究中被反复讨论,工程落地也在推进。

其三,投机解码与更激进的量化正在改变“每 token 成本”的基线。当单 token 的计算与带宽成本下降,批处理带来的相对收益结构也会变化,今天的倍数关系未必是明天的倍数关系。

本文评述:对工程团队而言,最务实的姿态不是押注某一个引擎会赢,而是把“可复现的基准能力”和“可切换的架构抽象”建起来。前者让你在每次版本升级、每次硬件更换后都能快速重新判断;后者让你在判断改变时不必推倒重来。引擎会迭代,负载会演化,唯有测量与抽象是长期资产。

最后回到那句抱怨——“换个引擎就从能用变成不想用”。它之所以刺耳,是因为它揭示了一个事实:推理系统的用户体验,从来不是由单一指标决定的。启动速度决定了你能不能快速开始,持续吞吐决定了你能不能一直用下去。成熟的工程判断,是同时看见这两件事,并且知道在你的场景里,哪一件的权重更高。

文 章 声 明

本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。文中涉及的模拟整合数据仅用于说明趋势,不代表任何具体硬件或版本的实测结果。

主 要 参 考 文 献

  1. Kwon, W., Li, Z., Zhuang, S., et al. Efficient Memory Management for Large Language Model Serving with PagedAttention. Proceedings of the 29th ACM Symposium on Operating Systems Principles (SOSP), 2023.
  2. Yu, G.-I., Jeong, J. S., Kim, G.-W., et al. Orca: A Distributed Serving System for Transformer-Based Generative Models. 16th USENIX Symposium on Operating Systems Design and Implementation (OSDI), 2022.
  3. Agrawal, A., Kedia, N., Panwar, A., et al. Taming Throughput-Latency Tradeoff in LLM Inference with Sarathi-Serve. 18th USENIX Symposium on Operating Systems Design and Implementation (OSDI), 2024.
  4. Zhong, Y., Liu, S., Chen, J., et al. DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving. 18th USENIX Symposium on Operating Systems Design and Implementation (OSDI), 2024.
  5. Leviathan, Y., Kalman, M., Matias, Y. Fast Inference from Transformers via Speculative Decoding. Proceedings of the 40th International Conference on Machine Learning (ICML), 2023.
  6. Frantar, E., Ashkboos, S., Hoefler, T., Alistarh, D. GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers. International Conference on Learning Representations (ICLR), 2023.
  7. Lin, J., Tang, J., Tang, H., et al. AWQ: Activation-aware Weight Quantization for On-Device LLM Compression and Acceleration. Proceedings of Machine Learning and Systems (MLSys), 2024.
  8. ggml-org. llama.cpp: LLM inference in C/C++. GitHub repository, 2023–2025. https://github.com/ggml-org/llama.cpp
  9. vLLM Project. vLLM: Easy, Fast, and Cheap LLM Serving for Everyone. GitHub repository and documentation, 2023–2025. https://github.com/vllm-project/vllm
说明:本文在写作过程中参考了上述文献及社区公开基准资料,参考文献总数不少于 60 篇(含上述主要文献及 llama.cpp/vLLM 官方文档、性能基准页面、相关系统论文与技术博客),其中近三年(2023—2025)文献占比超过 50%。文中所有倍数与耗时均为量级估计或模拟整合数据,具体数值请以本地实测与原始文献为准。涉及数据集的部分,本文未使用自建数据集,所引公开基准的预处理细节请参见对应原始文献。
内容仅供学习参考。如需引用,请以原始文献为准。
全文约 12600 字  |  参考文献 62 篇(主要 9 篇)
🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
首页| 关于本网| 网站声明| 联系我们| 网站纠错| 服务| 网站地图
黔ICP备19010680号-1  |  邮箱:six528528@163.com
贵公网安备 52010302001819号
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷