单用户没差别,一上并发就崩——本地推理引擎选型避坑指南
最后选了llama.cpp。
不是因为它比vLLM强。是我的场景根本用不上vLLM那一套。
翻车了。
去年开始在MacBook上跑本地模型,M2 Max,64G内存。一开始跟大多数人一样,装了Ollama,敲两行命令就能用,巴适得很。用了两周发现问题了——Ollama版本更新老是滞后,我想试试Qwen2.5的新量化格式,它那边还没跟上。咋整?直接搞llama.cpp吧。
说实话,第一次编译的时候我是懵的。
就两条命令:cmake -B build,然后cmake --build build --config Release -j 12。编译完一看,整个二进制文件才几十兆。几十兆。你想想vLLM的Docker镜像,动辄10G起步,光下载镜像的时间都够我编译十遍llama.cpp了。这玩意儿轻量得像开了挂。
然后开始折腾模型文件。llama.cpp用的是GGUF格式,跟HuggingFace上常见的safetensors不一样。一开始觉得这事儿挺烦的,还得用convert脚本转一遍。后来才明白——不对,应该说是顿悟——这恰恰是llama.cpp的设计哲学:零依赖。safetensors需要safetensors库读取,那个库又跟PyTorch绑在一起,PyTorch拖着一大堆东西。GGUF就一个文件,自己解析,干净利落。你品,你细品。
跑起来之后,发现一个有意思的现象。
单用户场景下,llama.cpp和vLLM的延迟差距几乎可以忽略。我测过Qwen2.5-7B的Q4_K_M量化版本,llama.cpp在M2 Max上大概每秒30到40个token,完全够用。但你要是拿它去做高并发API服务——讲真,那叫一个惨。
翻车得很彻底。
试过在llama.cpp上跑并发请求,三个客户端同时发prompt,吞吐量直接崩了。为啥?因为它没有vLLM那种Continuous Batching机制。vLLM能把多个请求动态拼成一个batch,GPU利用率拉满。llama.cpp呢?老老实实一个接一个处理,跟排队买奶茶似的。这差距,比我前任还靠谱——我是说vLLM比我前任靠谱。
这就是两条路线的根本分歧。
llama.cpp的出身就很野。它是从GGML发展来的,GGML是Georgi Gerganov纯手写的推理向量库。传统机器学习那套流程:PyTorch训练→Python推理→发现问题→优化迭代。llama.cpp的思路是反过来的——先脱离PyTorch那一整套繁重的依赖,用C++从零写推理引擎,只保留推理需要的部分,然后反哺到Python生态。llama-cpp-python就是这么来的。
你想想,写llama.cpp的时候,基础的矩阵向量得自己写,内存排布得掰着手指头算,permute这种算子用C++实现起来有多痛苦,做过的人都知道。模型加载得自己管理,编解码得自己处理,中文和emoji还老出问题。真不是一般人能干的。这玩意儿硬核到劝退。
绝了。
vLLM和SGLang走的是另一条路。它们站在巨人肩膀上——torch、numpy、sentencepiece、huggingface这一整套生态,只需要把精力放在怎么让系统跑得更快上,优化关键算子和通信就行。说白了,人家是站在巨人的肩膀上整活。
vLLM的核心创新是PagedAttention。说白了就是把KV缓存切成固定大小的块,让多个请求共享GPU内存而不浪费空间。再加上Continuous Batching,高并发场景下vLLM能把GPU榨得一滴不剩。Stripe迁移到vLLM之后,推理成本降了73%,用三分之一的GPU处理了每天5000万次API调用。这个数据是真的猛,猛到让人怀疑是不是在割韭菜——当然不是,是真的省。
SGLang更狠。它在vLLM基础上加了一层"推理编译器",搞了个RadixAttention。啥意思呢?多个请求共享开头提示时,SGLang会缓存前缀的计算结果并复用。比如用户A问"解释量子计算",用户B问"解释量子计算的应用",SGLang自动识别出"解释量子计算"这个公共前缀,直接复用KV缓存,只计算"的应用"部分。这操作,yyds。
在Agent和多轮对话场景下,SGLang的benchmark比vLLM还快。不过它社区小,生产环境大规模验证还不够,我暂时还不敢用在关键业务上。嗯,可能再观望半年吧。不是不行,就是有点虚。
说到这儿,你可能会问:那llama.cpp的价值到底在哪?
我一开始也没想明白。直到有一次出差,在高铁上拿一台无GPU的轻薄本跑了个7B模型,处理了几个文档分析任务。那台电脑就8G内存,模型量化到Q4_0之后只占4G多,跑起来虽然不快,但能用。
真的能用。
这就是llama.cpp的用武之地。它的设计原则就四条:纯C/C++实现、GGUF单文件格式、原生量化支持、mmap内存映射。零依赖,一个可执行文件加一个模型文件就能部署。边缘设备上,离线环境里,数据不能出本地的场景下,llama.cpp是唯一的选择。得嘞,这就是它的护城河。
而且它在Apple Silicon上的表现尤其好。M系列芯片有统一内存架构,CPU和GPU共享内存池,带宽能到400GB/s,再加上AMX协处理器的外积引擎,每周期能做大概1024次标量FMA。llama.cpp针对这些硬件做了深度优化,ARM NEON的SDOT指令、Apple AMX的专用kernel,性能提升好几倍。这玩意儿在Mac上跑得比兔子还快。
后来在H200上也做过对比测试。峰值负载下,vLLM的请求吞吐量是llama.cpp的35倍以上,总token吞吐量超过44倍。但切换到单用户场景,差距几乎消失。35倍和44倍——这数字我记了整整一周,太震撼了。
所以怎么选,其实很清楚。
选llama.cpp:消费级硬件上跑模型,数据不能出本地,自己用或小团队用,想第一时间试最新的开源模型,部署在离线或边缘环境。中不中?中!
选vLLM:对外提供API服务,手头有A100、H100这类企业级GPU,并发量大,对吞吐量和成本斤斤计较,需要Kubernetes原生部署。这玩意儿稳得像老狗。
选SGLang——不对,应该叫这个引擎——如果你有大量多轮对话或Agent场景,需要结构化输出,JSON Schema强制约束那种,前缀复用能带来明显收益。不过社区还在卷,再等等。
我现在的方案:本地开发和测试用llama.cpp,轻量、灵活、不挑硬件。生产环境部署用vLLM,稳定、高效、生态完善。SGLang还在观察,等社区再成熟一点,Agent场景多了可能会切过去。上周三下午我试了一下SGLang的最新版,还是有点小bug,不急。
说白了,这俩工具不是替代关系,是互补的。一个让大模型飞入寻常百姓家,一个让大模型撑起企业级的流量洪峰。挺好的这东西。
搞清楚自己在哪条路上,比纠结哪个"更好"有意义得多。
下一步打算在树莓派上试试llama.cpp的极限,跑个1.5B的小模型看看能不能做简单的语音助手。大概是闲得慌。但这就是折腾的乐趣,不是吗?
踩过的坑,最后都成了路。
读者评论 5