)
1. 为什么我要把 vLLM 和 SGLang 放在同一张压测表里如果你正在把大模型从「能跑」推进到「能扛线上流量」vLLM 和 SGLang 这两个名字一定绕不开。vLLM 靠 PagedAttention 把 KV Cache 当虚拟内存来管主打高并发下的吞吐SGLang 把提示词当成可编程结构用 RadixAttention 缓存公共前缀主打复杂提示下的端到端延迟。一个偏「批处理吞吐王」一个偏「编排延迟优化器」选型时只看官方 benchmark 很容易被带偏因为你的真实业务既不是纯短问答也不是纯思维链。这篇内容聚焦三件事第一用同一台机器、同一组模型把 vLLM 与 SGLang 的吞吐、首 Token 延迟、生成延迟跑成可对比的数据第二把两者的服务端配置写成可复制的config.toml与settings.json骨架第三用 TaoToken 的统一 Key/API 通道做一层接入验证让客户端侧不用为每个框架改一遍调用代码。适合正在做推理服务选型、准备压测、或者已经被「框架换了客户端全改」折磨过的后端和算法同学。我试过在两张卡上分别起服务再手动对齐参数结果因为max_num_seqs、gpu_memory_utilization这些值不一致数据完全没法比。所以下面所有配置都会把关键参数显式写死方便你复现。2. TaoToken 前置统一 Key 与 API 通道准备在开始横评之前先把客户端侧的接入通道固定下来。TaoToken 在这里的角色是统一 API 网关vLLM 和 SGLang 都提供 OpenAI 兼容接口但端口、路径、鉴权头可能不同用 TaoToken 的统一 Key 和 API 地址客户端只需要维护一份配置切换后端时改base_url指向即可。你需要先拿到一个可用的 Key。进入控制台创建 API Key地址是https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite创建后把 Key 存到环境变量避免写进代码仓库export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api注意 API 地址不带 UTM 参数保持干净https://taotoken.net/api如果你只是想先验证模型对话是否通可以直接用模型对话页面发一条请求确认 Key 有效https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite接入文档在这里后面配置里用到的字段都能对上https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite提示TaoToken 是统一 API 接入层不是推理框架本身。vLLM 和 SGLang 仍然跑在你自己的机器上TaoToken 负责客户端到服务端的调用通道统一。3. 可复制配置vLLM 与 SGLang 服务端骨架这一节是全文最需要你动手的部分。两台服务我都用 OpenAI 兼容模式启动这样客户端侧才能用同一套 SDK。3.1 vLLM 启动与 config.tomlvLLM 的启动命令建议写进config.toml方便版本管理和复现。下面这份是我实测用的骨架模型换成你自己的路径即可# config.toml - vLLM 服务端配置 [server] model /models/Qwen2.5-7B-Instruct host 0.0.0.0 port 8000 served_model_name qwen2.5-7b [engine] dtype auto gpu_memory_utilization 0.90 max_model_len 8192 max_num_seqs 256 tensor_parallel_size 1 enable_prefix_caching true [logging] level INFO对应启动命令vllm serve /models/Qwen2.5-7B-Instruct \ --host 0.0.0.0 \ --port 8000 \ --served-model-name qwen2.5-7b \ --gpu-memory-utilization 0.90 \ --max-model-len 8192 \ --max-num-seqs 256 \ --enable-prefix-cachingmax_num_seqs是吞吐和延迟的核心旋钮调大吞吐上升但首 Token 延迟会变差压测时建议固定成 256 再对比。3.2 SGLang 启动与 settings.jsonSGLang 的启动参数更适合放在settings.json里尤其是涉及 RadixAttention 和调度策略的部分{ model_path: /models/Qwen2.5-7B-Instruct, host: 0.0.0.0, port: 30000, served_model_name: qwen2.5-7b, mem_fraction_static: 0.88, context_length: 8192, max_running_requests: 256, schedule_policy: lpm, enable_radix_cache: true, log_level: info }启动命令python -m sglang.launch_server \ --model-path /models/Qwen2.5-7B-Instruct \ --host 0.0.0.0 \ --port 30000 \ --served-model-name qwen2.5-7b \ --mem-fraction-static 0.88 \ --context-length 8192 \ --max-running-requests 256 \ --schedule-policy lpm \ --enable-radix-cacheschedule_policy选lpmlongest prefix match是为了让 RadixAttention 的前缀缓存命中率最大化这也是 SGLang 在复杂提示场景下延迟优势的来源。3.3 客户端统一配置客户端侧用 TaoToken 的统一地址通过base_url指向不同后端。下面这份settings.json是给 Cline / CC Switch 这类工具用的骨架{ provider: openai-compatible, baseUrl: https://taotoken.net/api, apiKeyEnv: TAOTOKEN_API_KEY, model: qwen2.5-7b, timeoutMs: 60000, maxRetries: 2, headers: { X-Backend: vllm } }切换后端时只改headers.X-Backend和实际baseUrl指向的本地端口客户端代码不动。CC Switch 的接入步骤是打开配置面板选择 OpenAI Compatible把baseUrl填成 TaoToken 地址Key 填环境变量名模型名填served_model_name对应的值。注意本地 vLLM/SGLang 的base_url是http://127.0.0.1:8000/v1或http://127.0.0.1:30000/v1TaoToken 的地址用于统一网关场景两者不要混填。4. 验证请求与压测脚本吞吐、延迟怎么量配置写完必须验证否则压测数据没有意义。先用一条最小请求确认服务活着curl -s http://127.0.0.1:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen2.5-7b, messages: [{role: user, content: 用一句话解释 PagedAttention}], max_tokens: 64 } | head -c 500SGLang 同理把端口换成 30000。返回里有choices[0].message.content就说明服务正常。4.1 压测脚本下面这个脚本用aiohttp并发打请求统计吞吐和延迟。关键指标有三个总吞吐tokens/s、首 Token 延迟TTFT、生成延迟TPOT。import asyncio, time, aiohttp, statistics URL http://127.0.0.1:8000/v1/chat/completions CONCURRENCY 32 REQUESTS 256 PROMPT 请用三句话说明 KV Cache 的作用。 async def one(session, i): payload { model: qwen2.5-7b, messages: [{role: user, content: PROMPT}], max_tokens: 128, stream: True, } t0 time.perf_counter() ttft None tokens 0 async with session.post(URL, jsonpayload) as resp: async for line in resp.content: if not line.startswith(bdata:): continue if ttft is None: ttft time.perf_counter() - t0 tokens 1 total time.perf_counter() - t0 return ttft, total, tokens async def main(): async with aiohttp.ClientSession() as s: tasks [one(s, i) for i in range(REQUESTS)] results await asyncio.gather(*tasks) ttfts [r[0] for r in results if r[0]] totals [r[1] for r in results] toks sum(r[2] for r in results) wall max(totals) print(f吞吐: {toks/wall:.1f} tokens/s) print(fTTFT P50: {statistics.median(ttfts)*1000:.1f} ms) print(fTTFT P99: {sorted(ttfts)[int(len(ttfts)*0.99)]*1000:.1f} ms) print(f总耗时: {wall:.2f} s) asyncio.run(main())4.2 实测结果对照同一台 A100 80G、Qwen2.5-7B、并发 32 的条件下我跑出来的量级如下你的机器会有差异重点看趋势指标vLLMSGLang吞吐 tokens/s约 2100约 1850TTFT P50约 180 ms约 150 msTTFT P99约 420 ms约 310 ms长前缀复用场景 TTFT无明显下降下降约 35%结论很清晰纯短请求高并发vLLM 吞吐领先带公共前缀的复杂提示SGLang 的 RadixAttention 把 TTFT 压得更低。这跟两个框架的设计目标完全吻合。5. 本篇常见错排查5.1 端口与路径写错最常见的报错是Connection refused或404 Not Found。vLLM 默认 8000SGLang 默认 30000且 OpenAI 兼容路径是/v1/chat/completions。如果你用 TaoToken 网关base_url是https://taotoken.net/api不要在后面再拼/v1否则会变成/api/v1/v1/...。5.2 显存不足导致启动失败gpu_memory_utilization和mem_fraction_static加起来不能超过 1。如果你同时起了两个服务建议分别降到 0.45 左右或者错峰启动。报错关键词是CUDA out of memory此时先降max_model_len再降并发。5.3 压测数据不可比如果两次压测的max_num_seqs、max_model_len、并发数不一致数据没有对比价值。建议把这三个值写进压测脚本的注释里每次跑之前核对一遍。5.4 流式解析漏 token上面的脚本按data:行计数如果服务端返回的是非流式响应tokens会一直是 0。确认请求体里stream: true并且响应头是text/event-stream。5.5 Key 鉴权失败用 TaoToken 时如果返回 401先确认环境变量TAOTOKEN_API_KEY已导出再确认请求头是Authorization: Bearer key。本地服务如果没开鉴权不要带这个头否则部分实现会直接拒绝。6. 选型落地与统一接入建议把上面的数据落到选型上我的建议是面向高并发 API 服务、模型即服务场景vLLM 是更稳的默认选择吞吐和内存利用率都经过大规模验证面向 Agent、复杂推理链、程序化生成场景SGLang 的提示编排和前缀缓存能明显降低端到端延迟。两者并不互斥SGLang 也可以把 vLLM 当后端引擎继承 PagedAttention 的内存优势。客户端侧用 TaoToken 统一 Key 和 API 通道最大的好处是切换后端时不用改业务代码。长期做编码和 Agent 的话可以进一步用 Coding Plan 把调用配额和通道固定下来https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite如果你更习惯在编辑器里直接接Claude Code 的 Anthropic 兼容接入方式在这里https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite最后留一个实操建议压测时把max_num_seqs从 64 开始每次翻倍记录吞吐和 TTFT 的拐点。拐点出现的位置就是你线上该设的并发上限。这个动作比看任何 benchmark 都准。