01 “有vLLM了为啥不用?”:一场关于“轮子”的哲学辩论
两年前,我在芯片公司的GPU软件团队提出要自研推理引擎时(非LLM专用),和领导的远程语音瞬间沉默,仿佛网络掉线一般。在沉默之后,领导缓缓的问出了:“PyTorch不是能跑吗?vLLM开源了为啥不用?LLAMA依赖少可修改为什么不行?我们的核心产出是啥?”
我深吸一口气,祭出灵魂连击:
1. PyTorch是瑞士军刀,推理引擎是手术刀
前者为训练而生,带着自动微分、动态图等“豪华负重”上场推理——就像穿着潜水服跑马拉松。推理需要的是极致的内存复用、算子融合、流水线调度,这些在训练框架里都是“非刚需”。我们和PyTorch是互补,不是有他无我的互斥。
2. vLLM很香,但它是给NVIDIA卡定制的西装
而我们的芯片有自定义指令集、特殊的片上缓存、甚至内存带宽瓶颈。硬套vLLM?相当于给哈士奇穿高跟鞋——理论上能走,实际上每一步都在挑战物理法则。更要命的是你要跑vLLM,首先你得跑起来PyTorch。
3. 我们不只是做LLM,还有CNN/ASR/TTS,还有边缘设备
你试试在某些嵌入式盒子上装个PyTorch?安装包还没解压完,设备已经进入冬眠模式。
4. 自有软件栈是必须的
我们有芯片的硅前硅后验证,我们需要对齐其它厂商的软件生态建设,比如TensorRT。
领导沉吟三秒:“好的,现在正好有一个短暂的交付真空期,你们做一下调研。然后准备去总部那边开会讨论这个事情”
——于是,我和另外两位勇士(以及GitLab里那个空得能听见回声的仓库)开启了“从零造火箭”之旅。
02 从ResNet到ChatGLM:当模型体积×100,调试难度×10000
前期顺得像开了挂:建仓、写基础算子、三天跑完OP、两周跑通ResNet。CNN时代那熟悉的感觉,让大家都觉得一切将继续顺利推进,团队士气高涨,仿佛马上就能敲钟上市。
直到我们把目标换成 ChatGLM2-6B(60亿参数,10GB+显存开销) 。
那一刻我悟了:LLM的残酷真相,是模型体积×100,调试难度×10000。 这不仅仅是在硬件上跑起来,更涉及 人工智能 模型的深度理解与适配。
信仰崩塌现场实录:
-
RoPE(旋转位置编码):论文说“很简单”,工程说“你礼貌吗?”
位置编码本该是“给token标序号”,但RoPE偏要搞文艺复兴——用旋转矩阵把位置信息“拧”进向量里。更绝的是ChatGLM2:一半分奇偶,一半直接复制,这一“创新”搞的人头大。
-
内存布局(Layout):当“神奇格式”遇上现代LLM模型
FP16权重该按什么顺序排?NCHW?NHWC?还是厂商自定义的sbf(没错,就是seq,batch,feature)?一个transpose写错,模型不报错,只是默默输出“人类无法理解的乱码”——也许这就是一种“电子木鱼”,敲一下,嗡嗡响,但听不懂。
-
子图匹配:当优化变成恐怖片
为提升性能,我们把多个小算子融合成大算子。结果匹配一个RoPE子图,子图匹配算法跑了2小时!传统的DFS/BFS匹配相当于用深度优先搜索遍历整个互联网找“知乎”俩字,这性能就和总Op成反比了。
转机发生在某个午休前。吃完午饭回来,本着试一试的想法,我改完Rope的kernel代码,按下F5——
屏幕缓缓滚动:
我是一个名为 ChatGLM2-6B 的人工智能助手,是基于清华大学 KEG 实验室和智普 AI 公司于 2023 年共同训练的语言模型开发的...
那一刻,我截图的手在抖。
不是因为技术多牛,而是终于证明了“自研可行”。
(后来吐槽:如果当时选择LLAMA/Qwen这种干净统一的结构,也许事情的进展会顺利很多。ChatGLM后来也发现自己不合群,改为了主流方式)
03 多卡并行:当DP/PP/TP/EP从PPT走进ICU
单卡跑通只是幼儿园毕业。真正的地狱模式是多卡并行——这里必须展开说说那些“听起来高大上,做起来想删库跑路”的缩写:
| 缩写 |
全称 |
人话解释 |
工程现场 |
| DP |
Data Parallel |
同一份模型,不同卡喂不同数据 |
如何自动切分任务,保持任务的均衡 |
| PP |
Pipeline Parallel |
模型切层,卡1跑前10层→卡2跑后10层 |
气泡问题:单任务时卡2干等卡1,GPU利用率暴跌,像流水线卡壳 |
| TP |
Tensor Parallel |
一个矩阵乘法拆到多卡并行算 |
通信密集:每层都要AllReduce,带宽杀手 |
| EP |
Expert Parallel |
MoE模型专属,不同卡负责不同专家 |
负载不均:热门专家卡爆了,冷门专家在摸鱼,像网红店排队 |
网上教程总说“混合并行很简单”,但当你把TP+PP+EP叠满,会发现:单OP并行是教科书,模型级并行是修罗场。
比如TP要求权重按行列切分,PP要求按层切分,两者叠加时——权重切分维度直接打架!走出教科书的单OP切分后,你还将面对模型整体的混合切分问题。
最终你将会明白:并行策略不是简单的数学上的等价拆分,而是一个复杂化的系统工程。 得考虑通信拓扑、卡间带宽、PCIe插槽的物理位置——是的,插槽离得远,通信延迟真的会高一些。甚至多卡的数据加载,传输,同步等等一系列问题需要你去解决。这考验着对 智能 & 数据 & 云 计算范式的深刻理解。
04 CNN时代 vs LLM时代:那些被刷新的“常识”
在CNN时代摸爬滚打好几年,做过CNN推理引擎,NPU编译工具,量化等等,我以为自己已经会了很多。直到LLM教我重新做人:
1. 图优化时间:从“秒级”到“小时级”
- CNN模型图结构规整,总OP量不大,子图匹配像拼乐高。
- LLM模型算子稀碎(一个Attention拆成Q/K/V投影+RoPE+Mask+Softmax…),匹配时组合耗时爆炸。没优化的匹配算法,跑一个RoPE匹配能等到你孩子上小学。
2. 量化:int4的“比特魔术”
- CNN时代,我们笑谈“int8量化掉点1%算失败”。
- LLM时代,int4+Group Quantization(如每128个权重一组独立量化)成了标配。
更魔幻的是:4bit数据怎么存?怎么用?
如果你以为是“高 4 位 / 低 4 位”那么恭喜你,在解出来后会发现数据怎么好像不对了。这时候就该说,不对?那就是对咯。
解决了数据存储,如何实现高效的int4 gemm可能又是另外一个可以专门写一篇文章的深坑了。
3. KV Cache:LLM推理的“命门”
自回归生成时,每步都要缓存历史token的Key/Value(这就是KV Cache)。
10GB+模型跑长文本?多对话?非对齐?KV Cache能吃掉80%显存。
于是就有了经典的:
- Paged Attention: 借鉴操作系统分页,把KV Cache切成块动态管理(vLLM的核心创新)。
- V Attention: 直接借用硬件和驱动的MMU去做事情,参考Cuda的VMM系列行为。
个人偏见:能上V Attention就别用Paged Attention。
前者是重构内存哲学,后者是天然的实现——当然,前提是你的硬件和驱动支持你需要的能力。
4. 那些“不起眼却致命”的细节
- 多卡显存回收: 单卡时一手Time Line + Memory Pool就一劳永逸;多卡时,卡1释放的显存卡2可能正引用——这玩意儿调试起来,堪比在雷区跳踢踏舞。多卡如何同步,谁来释放VMM的管理资源,释放的资源可以进入Memory Pool吗?进入后可以成为普通的显存被继续使用吗?
- Condition/Loop支持: 静态图框架若不支持控制流,ASR/TTS场景直接废掉。想象一下:语音识别时“根据置信度决定是否重识别”,没Condition?你打算怎么做?问就是不做,换一个能跑的模型。
- 多用户态竞争: 100个用户同时请求,推理引擎若只做图优化,会像高速公路只修收费站——车流堵死在入口。真正的优化在运行时调度:如何让不同请求能高效的跑起来,这并不比写一个子图优化简单。
05 终极拷问:LLM时代,自研推理引擎还有意义吗?
每当行业唱衰“推理框架已成红海,适配vLLM/SGLang就行”,我总会想起那个跑通ChatGLM的午后。
自研的意义,从来不是“重复造轮子”,而是“造适配自家赛道的轮子”。
- NVIDIA有TensorRT-LLM,因为它的卡有特殊Tensor Core;
- 华为有MindIE,因为昇腾芯片的达芬奇架构需要定制调度;
- 我们做自研引擎,是因为硬件差异化是护城河,而软件是放大器。
但也要清醒:自研≠闭门造车。 我们也大量学习和借鉴业界的 开源实战 经验。
我们大量学习已有的经验,Continuous Batching(动态批处理)、借鉴FlashAttention的IO感知算法、借鉴Chunk Prefill来降低TTFT指标。
真正的工程智慧是 “站在巨人肩膀上,给巨人装涡轮增压”。
06 在浪潮中保持初心:坚持与调整的辩证法
这两年最大的感悟,是两句话的微妙平衡:
- 坚持自己认为正确的事,即使收获周期很长
- 学会在变化中调整目标,长久目标只能作为参考
看似矛盾,实则是工程师的生存哲学:像RoPE一样——位置编码随序列长度旋转(适应变化),但旋转公式本身恒定(坚守本质)。
从草稿纸上的胡乱涂鸦,到芯片发布会上真实的跑出多卡DeepSeek V3全参模型。经历过这两年,终于可以吹自己 “会做推理引擎了” 。
回想起来,从项目立项的踌躇满志,到第一个LLM模型跑通,再到发布会上的多卡满血DeepSeek V3实时模型演示。经历了从 "体面厂" 离开后的第一次深夜2点下班,当在台风的极限催促下登上离开发布会城市的飞机时,我虽然已连续几天没休息好却仍然不觉得累。
这两年的路走起来并不是那么顺畅,但是现在想说还好坚持下来了。
07 尾声:给后来者的一句话
如今,当有人问我“推理引擎怎么做”,我会先反问:
- 你准备好和成百上千GB的内存泄漏搏斗了吗?
- 准备好被RoPE的sin/cos函数逼疯了吗?
- 准备好在多卡通信延迟里一根一根找头发了吗?
如果对方眼睛发亮说“准备好了”——恭喜,又一个 “推理引擎民工” 诞生了。
这条路尽头也许没有鲜花和掌声,但一定会有某个时刻,你的屏幕上跳出一行字:
“你好,我是你亲手造出来的智能。”
那一刻,所有的debug、所有的会议扯皮、所有的“有vLLM为啥不用”,都值了。
附:术语轻量指南(给好奇的非工程师朋友)
- KV Cache: LLM生成文本时,为避免重复计算历史token,把中间结果缓存起来——相当于“记住刚才聊了啥”
- FlashAttention: 通过重计算+IO优化,减少显存读写,让Attention计算更快
- Continuous Batching: 动态合并不同长度的请求,让GPU始终“有活干”,吞吐量提升
- Group Quantization: 每N(如128)个权重一组独立量化,比全局量化精度高得多
- RoPE: 用旋转矩阵给token注入位置信息,让模型理解“第一个词”和“最后一个词”的区别