TL;DR
春节假期开始, 好像很多人都在开始卷DeepSeek-R1的推理了. 渣B也被兄弟团队带着一起卷了一阵, 其实推理中还有很多约束, 比较认同的是章老师的一个观点: “推理框架很有可能就此走向两种极致分化的方向.“ 本文来做一个详细的阐述, 从一些乱七八糟的benchmark开始, 然后谈谈测试方法, 推理系统的各种约束, 推理框架的区别, 并行策略的区别,然后再解构一下DeepSeek的原厂方案.
1. 前情回顾 2. 推理性能指标概述 3. 推理系统性能约束 3.1 用户SLA的约束 3.2 内存的约束 4.约束带来的分叉 5. 私有化部署 5.1 基于SGLang 5.2 基于vLLM 5.3 并行策略选择 6. 平台部署 6.1 PD分离技术 6.2 Prefill阶段 6.3 Decode阶段 7. 未来优化的方向和对开源生态的建议
1. 前情回顾
比较现实的是两个极端, 一方面是各种平台的测评, 例如公众号“
CLUE中文语言理解测评基准
”的
《DeepSeek-R1 网页端稳定性首测:12家第三方平台真实测评》
另一方面是尤洋老师在微博的一个评论MaaS的商业模式和平台推理亏损, 这里提到了4台H800的总吞吐量
另一方面是各种私有化部署的需求, 例如小红书上最近经常刷到
还有章明星老师的KTransformer可以在单卡的4090 24GB上配合Intel CPU的AMX部署Q4的量化版本. 通过将Routed Expert放置在CPU上运行来降低内存的使用量.
还有直接ollama找一个1TB内存的CPU实例就开跑的方案.
然后Benchmark的定义上,一会儿20 Tokens/s, 一会儿又是几千Tokens/s的benchmark满天飞, 到底是怎么回事? 其实有很多认知的问题, 让渣B回忆起刚毕业入职工作的时候做运营商级的电话信令网关时, 天天测性能算Erlang模型的日子...
2. 推理性能指标概述
推理是一个在线业务, 因此对第一个Token出来的延迟(Time To First Token,TTFT)和后续token产生的延迟(Time Per output Token,TPOT)都会对用户体感产生影响.
通常会使用测试工具生成如下一个报告, 具体数据就不多说了.
影响这个报告的因素很多, 例如测试工具是采用vllm的benchmark_serving还是采用sglang.bench_serving, 常用的参数是按照多少Request per seconds(RPS)测试或者按照多少并发量进行测试, 但是DeepSeek-R1的推理Reasoning时间很长, 通常都会选择并发数进行约束.测试在一定的并发数量下的吞吐/延迟等指标. 测试命令如下所示:
### vLLM的bench测试 python3 ~/vllm/benchmarks/benchmark_serving.py --backend vllm \ --model ~/deepseek-R1 --port 8000 \ --dataset-name random \ --random-input 1234 \ --random-output 2345 \ --random-range-ratio 0.8 \ --dataset-path ~/ShareGPT_V3_unfiltered_cleaned_split.json \ --max-concurrency 16 \ --num-prompts 64 ### sglangM的bench测试 python3 -m sglang.bench_serving --backend vllm \ --model ~/deepseek-R1 --port 8000 \ --dataset-name=random --random-input=1234 \ --random-output=2345 \ --max-concurrency=64 \ --num-prompts=128 \ --random-range-ratio 0.9 \ --dataset-path ~/ShareGPT_V3_unfiltered_cleaned_split.json
除了并发数
max-concurrency
以外, 另两个比较重要的参数是input多少token和output多少token, 这也是非常影响测试结果的. DeepSeek-R1作为一个Reasoning模型, 输出Thinking阶段的token也挺多的, 所以要根据实际的业务需要来进行分析.
因为以前长期做运营商级的呼叫信令网关, 对于请求到达是否按照Poisson过程, 对于结果的影响也很大, 这是一个非常重要的点, sglang的bench例如并发128时, 就是128个请求一起发出去了, 然后大家一起Prefill, 然后一起decode,这样可能导致TTFT偏长. 而vllm的测试是如果设置并发时是按照Poisson过程请求的. 但是似乎做的也不太符合真实的情况.
3. 推理系统性能约束
主要的约束有几个方面:
3.1 用户SLA的约束
通常我们可以根据实际业务的需求获得平均输入Token数和输出Token数以及方差, 然后根据企业员工的数量或者承载用户的DAU计算出一个平均请求到达间隔, 然后根据一些SLA的约束, 例如TTFT首Token时间要小于4s, TPOT即用户感知的每秒token输出速度, 例如要大于20Token/S(TPS).然后再来估计用户平均对一个请求的整体持续时间, 通过Erlang模型建模.
但是很多时候性能和成本之间会有一些取舍, 例如是否在一个低成本方案中,放宽对TTFT和TPOT的要求, 慢一点但是足够便宜就好, 或者是另一方面例如袁老师的硅基流动, Pro版本就能够严格保证用户的SLA, 也就是夏Core讲的, 稳定保持TPOT > 20TPS、
但是为了保证API平台的SLA, 通常需要采用更复杂的并行策略, 降低延迟提高吞吐, 例如DeepSeek论文提到的EP320的 40台机器的集群方案.
3.2 内存的约束
对于较长的Context,KVCache对显存的占用也特别大, 虽然单机的H20显存也能放得下满血版的671B模型,但是剩余的显存也会约束到模型的并发能力. 通常有些提供API的厂家会配置一个截断, 例如最大长度就8192个Tokens. 通常在这种场景下为了提高并发, 最小配置都会用2台以上的H20, 或者一些MI300的实例, 国外还有一些会采用H200的实例.
4.约束带来的分叉
正如前一章节所属, 两个约束带来了分叉. 一方面用户希望低成本的私有化部署,带来了一些小型化部署的机会, 例如小红书上看到的, 200w如何私有化部署满血版. 另一方面是大规模的云平台提供服务的时候保障SLA.
这两者直接决定了部署上的区别:
私有化部署
: 2台4台并行小规模满足成本的需求, 而不太在意TTFT和TPOT的需求, 能够满足企业内并发需求即可,甚至是季宇老师提到的一个极端的情况,就只做一个并发时, 如何用最低成本的硬件实现大概10~20TPS.
平台部署
: 最小320卡到最大数千数万卡并行的需求, 这种需求下并发的请求数量, KVCache的用量和累计整个集群的TFTT和TPOT的约束都非常大, 因此需要在并行策略上进行更多的考虑, 例如EP并行还有PD分离等.
很多较小的提供商通常只有开源软件sglang和vllm的部署能力, 然后并行策略上只有非常局限的TP/PP选择, 因此只有2~4台机器并行一组的方式提供服务, 自然就会遇到一些成本过高,吞吐过低无法通过token收费挣钱的情况. 这也就是所谓的夹在中间非常难受的一个例子.
因此章明星老师讲的这两种部署带来的推理系统分叉将会成为一个必然趋势.
5. 私有化部署
通常的做法是买两台H20或者在云上租用2台H20构建一个最小部署集, 然后自建的方式来部署.
5.1 基于SGLang
基于Sglang的部署方式如下, 两台机器安装sglang
pip install sgl-kernel --force-reinstall --no-deps pip install "sglang[all]>=0.4.2.post3" --find-links https://flashinfer.ai/whl/cu124/torch2.5/flashinfer/
第一台机器执行时, nnodes=2, node-rank=0, dist-init-addr都是第一台机器的IP地址.
python3 -m sglang.launch_server \ --model-path ~/deepseek-R1/ \ --tp 16 --dist-init-addr 1.1.1.1:20000 \ --nnodes 2 --node-rank 0 \ --trust-remote-code --host 0.0.0.0 --port 8000
第二台机器执行时,--nnodes 2 --node-rank 1
python3 -m sglang.launch_server \ --model-path ~/deepseek-R1/ \ --tp 16 --dist-init-addr 1.1.1.1:20000 \ --nnodes 2 --node-rank 0 \ --trust-remote-code --host 0.0.0.0 --port 8000
需要注意的是,现阶段Sglang只支持TP并行, PP并行在未来几周可能会支持.
5.2 基于vLLM
vLLM需要基于Ray部署, 如下图所示:
首先需要安装Ray
pip3 install ray
然后第一台机器配置
ray start --head --dashboard-host 0.0.0.0
第二个机器根据第一个机器的提示输入加入集群
ray start --address=':6379'
然后检查集群状态
ray status ======== Autoscaler status: 2025-02-07 19:09:06.335568 ======== Node status --------------------------------------------------------------- Active: 1 node_50018fxxxxx 1 node_11cc6xxxxx Pending: (no pending nodes) Recent failures: (no failures) Resources --------------------------------------------------------------- Usage: 0.0/256.0 CPU 0.0/16.0 GPU 0B/1.59TiB memory 0B/372.53GiB object_store_memory Demands: (no resource demands)
然后两台机器都安装vllm, 注意需要安装最新版的vllm 0.7.2性能有很大提升.
pip3 install vllm
最后在第一台机器上开启服务即可, 然后需要根据容忍的最大输入和模型输出调整
max-num-batched-tokens
和
max-model-len
vllm serve ~/deepseek-R1 \ --tensor-parallel-size 16 \ --enable -reasoning \ --reasoning-parser deepseek_r1 \ --max-num-batched-tokens 8192 \ --max-model-len 16384 \ --enable -prefix-caching \ --trust-remote-code \ --enable -chunked-prefill \ --host 0.0.0.0
单个输入的测试脚本如下
#test.py from openai import OpenAI# Modify OpenAI's API key and API base to use vLLM's API server.