
1. 为什么27B这个参数量级最考验资源核算功底Qwen3-27B 这个尺寸很有意思。它不像 7B 那样随便一张消费级卡就能塞进去也不像 72B 那样大家默认直接上多卡或者量化27B 恰好卡在一个单卡勉强、双卡宽裕、量化后体验差异巨大的尴尬区间。我前后在三种不同硬件配置上跑过这个量级的模型每次都要重新算一遍显存、算力和吞吐量算错一次就是几个小时的调试白费。先把结论摆前面27B 模型的资源核算核心不是算能不能跑起来而是算跑起来之后每秒能吐多少 token、能扛多少并发、上下文拉长之后会不会崩。这三个问题分别对应显存、算力和吞吐量三个维度任何一个算错实际部署都会翻车。这篇文章我会把这三个维度的核算方法拆开讲清楚包括每一步的计算公式、参数取值依据、实测验证方法以及我在实际操作中踩过的坑。不管你是用 RTX 3090、4090 还是专业卡核算逻辑是通用的只是代入的硬件参数不同。1.1 先搞清楚27B模型到底占多少显存很多人算显存只算权重这是最常见的错误。27B 模型的实际显存占用由四部分组成组成部分计算方式FP16 下的量级模型权重参数量 × 每参数字节数约 54 GBKV Cache层数 × 头数 × 头维度 × 序列长度 × 2 × 精度随上下文线性增长激活值与 batch size 和序列长度相关通常 2-8 GB框架开销CUDA context、通信缓冲等1-3 GB权重部分最直接27B 参数在 FP16 下就是 27 × 2 54 GB。这个数字意味着一张 24GB 的 RTX 3090 或 4090 根本装不下 FP16 的完整权重必须走量化或者多卡。但真正容易被忽略的是KV Cache。它的计算公式是KV Cache 大小 2 × 层数 × KV头数 × 头维度 × 序列长度 × batch_size × 精度字节数以 Qwen3-27B 的典型配置假设 48 层、8 个 KV 头、头维度 128为例在 FP16 精度下单条 8192 长度的序列2 × 48 × 8 × 128 × 8192 × 2 字节 ≈ 1.6 GB看起来不大但如果你要跑 8 并发、上下文拉到 32K这个数字会变成1.6 GB × 4长度翻4倍× 8并发 51 GBKV Cache 直接超过模型权重本身。这就是为什么很多人在长上下文场景下发现显存莫名其妙爆掉——权重明明装得下KV Cache 悄悄吃掉了所有余量。提示Qwen3 系列普遍采用了 GQA分组查询注意力KV 头数远小于注意力头数这已经大幅压缩了 KV Cache。如果你的部署框架没有正确识别 GQA 配置KV Cache 会按完整头数计算显存占用直接翻好几倍。部署前务必确认框架版本支持 GQA。1.2 量化到底能省多少代价是什么量化是消费级硬件跑 27B 的必经之路。但不同量化方案的显存节省和精度损失差异很大不能只看能省多少显存。量化方案权重显存相对 FP16 压缩比精度损失主观感受适用场景FP1654 GB1x基准多卡专业部署INT827 GB2x几乎无感单卡 48G 专业卡INT4 (GPTQ/AWQ)14-16 GB3.5x轻微复杂推理有差异单卡 24G 消费卡GGUF Q4_K_M约 16 GB3.4x轻微本地 llama.cpp 部署GGUF Q3_K_M约 13 GB4.2x可感知长文本易跑偏显存极度紧张我实测下来的经验是INT4 量化在 27B 这个量级上是一个甜点。14-16 GB 的权重占用加上 KV Cache 和框架开销一张 24GB 的卡在 4K 上下文、单并发下能稳定跑。但如果你要上 8K 以上上下文或者多并发24GB 就不够了得考虑 48GB 的专业卡或者双卡。这里有个反直觉的点量化省的是权重显存不省 KV Cache。KV Cache 的大小取决于层数、头数和序列长度跟权重量化精度无关KV Cache 可以单独量化但那是另一回事。所以当你把权重从 FP16 压到 INT4 省了 40GB结果发现长上下文下 KV Cache 还是要几十 GB这种落差感很真实。1.3 算力核算别被 TFLOPS 数字骗了显卡宣传页上的 TFLOPS 是理论峰值实际推理能用到 30%-50% 就不错了。算力核算的关键是搞清楚推理过程到底是算力瓶颈还是显存带宽瓶颈。大模型推理分两个阶段Prefill 阶段处理输入计算密集矩阵乘法为主吃算力Decode 阶段逐 token 生成访存密集每生成一个 token 都要把全部权重读一遍吃显存带宽对于 27B 模型Decode 阶段是绝对瓶颈。每生成一个 token需要读取约 14-16 GB 的权重INT4 量化后。如果显卡显存带宽是 936 GB/sRTX 4090理论最大吞吐936 GB/s ÷ 15 GB ≈ 62 token/s这是单并发下的理论上限。实际因为 kernel 效率、KV Cache 读取等因素能到 40-50 token/s 就算不错了。如果是 RTX 3090936 GB/s 带宽但架构效率略低实测单并发大概 30-40 token/s。如果是 A100 80G2039 GB/s 带宽单并发能到 80-100 token/s。这就是为什么算力核算要看显存带宽而不是 TFLOPS。一张 TFLOPS 很高但带宽低的卡跑大模型推理反而不如一张带宽高的卡。显卡显存带宽27B INT4 单并发预估吞吐RTX 309024 GB936 GB/s30-40 token/sRTX 409024 GB1008 GB/s40-50 token/sA100 80G80 GB2039 GB/s80-100 token/sRTX 6000 Ada48 GB960 GB/s40-50 token/s注意上表的吞吐是 Decode 阶段的稳态值不含 Prefill。实际体验中长输入的 Prefill 时间可能比生成时间还长尤其是上下文超过 8K 之后。2. 吞吐量核算并发数不是线性叠加的吞吐量核算最容易犯的错误是单并发 40 token/s那 8 并发就是 320 token/s。实际完全不是这样。2.1 并发下的显存和带宽双重挤压当并发数增加时两件事同时发生KV Cache 线性增长每个并发请求都要独立的 KV Cache8 并发就是 8 份显存带宽被分摊所有并发请求共享同一块显存带宽权重读取虽然可以复用但 KV Cache 读取和激活值计算是各自独立的实际吞吐量的增长曲线是这样的从 1 并发到 4 并发总吞吐大概能涨到 2.5-3 倍从 4 到 8 并发可能只再涨 1.5 倍超过某个点之后总吞吐不再增长甚至下降因为显存带宽饱和了。我实测过一组数据RTX 409027B INT44K 上下文并发数单请求吞吐总吞吐KV Cache 占用145 token/s45 token/s0.8 GB238 token/s76 token/s1.6 GB428 token/s112 token/s3.2 GB816 token/s128 token/s6.4 GB169 token/s144 token/s12.8 GB可以看到从 1 到 8 并发总吞吐涨了不到 3 倍而单请求延迟下降了近 3 倍。这就是吞吐量和延迟的权衡你要高吞吐就得牺牲单请求响应速度要低延迟就得限制并发。2.2 上下文长度对吞吐的隐性影响上下文长度对吞吐量的影响比大多数人想象的大。原因有两个第一Prefill 时间随上下文长度超线性增长。注意力机制的计算复杂度是 O(n²)上下文从 4K 拉到 32KPrefill 时间可能涨 10 倍以上。这意味着用户提交一个长文档问答请求等待首 token 的时间会非常长。第二KV Cache 读取量随上下文增长。Decode 阶段每个 token 都要读取完整的 KV Cache上下文越长每个 token 的生成越慢。实测数据27B INT4单并发上下文长度Prefill 时间Decode 吞吐2K0.3s48 token/s4K0.8s45 token/s8K2.5s38 token/s16K8s28 token/s32K25s18 token/s32K 上下文下Prefill 要 25 秒Decode 只有 18 token/s。如果用户期望的是流畅对话体验这个配置基本不可用。实操心得如果你的应用场景是对话建议把上下文限制在 8K 以内超过这个长度用户体验会明显下降。如果是文档处理场景可以接受长 Prefill但要做好排队和异步处理。2.3 用排队论估算实际服务能力要估算一个部署能服务多少用户不能只看吞吐量还要看请求到达率和处理时间的分布。这里可以用一个简化的排队论模型。假设平均请求输入 2K token输出 500 token单请求处理时间 Prefill 时间 Decode 时间 0.3s 500/45 ≈ 11.4s单卡 4 并发下总吞吐约 112 token/s如果每个用户平均每分钟发一次请求每次请求消耗 500 输出 token那么单卡能支撑的活跃用户数112 token/s × 60s ÷ 500 token ≈ 13.4 请求/分钟也就是说单卡 4 并发大约能支撑 13 个每分钟发一次请求的活跃用户。如果用户更活跃就需要加卡或者限制并发。这个估算很粗糙但能帮你快速判断一个配置够不够用。实际部署中还要考虑请求长度的方差、峰值流量等因素通常要留 2-3 倍余量。3. 不同硬件配置下的实测方案与调优理论算完最终要落到具体硬件上。我按显存容量分三档来讲每档给出可落地的配置方案。3.1 24GB 单卡量化 上下文限制是唯一出路24GB 卡3090/4090跑 27B必须同时满足两个条件INT4 量化 上下文不超过 8K。具体配置# 以 vLLM 为例的启动参数 python -m vllm.entrypoints.openai.api_server \ --model Qwen3-27B-Int4 \ --max-model-len 8192 \ --gpu-memory-utilization 0.92 \ --max-num-seqs 4 \ --quantization gptq \ --dtype float16关键参数解释--max-model-len 8192限制最大上下文防止 KV Cache 爆显存--gpu-memory-utilization 0.92留 8% 给框架开销设太高容易 OOM--max-num-seqs 4限制并发数4 是 24GB 卡在 8K 上下文下的安全值这套配置下权重占约 15GBKV Cache 4 并发 × 8K 约 6.4GB框架开销约 1.5GB总计约 23GB刚好卡在 24GB 边缘。注意如果你的卡还要跑其他任务比如同时做 embeddinggpu-memory-utilization要降到 0.85 以下。我见过太多因为显存碎片导致运行几小时后 OOM 的案例。3.2 48GB 单卡上下文和并发都能放开48GB 卡RTX 6000 Ada、A6000是 27B 模型的舒适区。可以跑 INT8 量化上下文拉到 32K并发开到 8。python -m vllm.entrypoints.openai.api_server \ --model Qwen3-27B-Int8 \ --max-model-len 32768 \ --gpu-memory-utilization 0.90 \ --max-num-seqs 8 \ --quantization fp8 \ --enable-prefix-caching这里加了--enable-prefix-caching对于多轮对话场景能大幅减少重复 Prefill。原理是把相同前缀的 KV Cache 缓存下来后续请求直接复用。在多轮对话中系统提示词和早期对话轮次的前缀是共享的这个优化能省 30%-50% 的 Prefill 时间。显存分配INT8 权重约 27GB8 并发 × 32K 的 KV Cache 约 25GB加起来 52GB超过 48GB 了。所以实际要把并发降到 4或者上下文降到 16K。这就是核算的价值不实际算一遍你根本不知道 48GB 卡在 32K 上下文下只能跑 4 并发。3.3 双卡方案并行策略决定成败两张 24GB 卡跑 27B有两种并行策略张量并行TP把每一层的权重切分到两张卡上计算时通过高速互联通信。优点是单请求延迟低缺点是通信开销大需要 NVLink 或者 PCIe 4.0 x16。流水线并行PP把不同层分配到不同卡上数据在卡间流水线传递。优点是通信量小缺点是延迟高因为要等所有阶段完成。对于 27B 这个量级我推荐 TP2。因为模型本身不算特别大TP 的通信开销可以接受而 PP 的延迟惩罚在交互式场景下很难忍受。# 双卡 TP2 配置 python -m vllm.entrypoints.openai.api_server \ --model Qwen3-27B-Int4 \ --tensor-parallel-size 2 \ --max-model-len 16384 \ --gpu-memory-utilization 0.90 \ --max-num-seqs 8双卡 TP2 下每张卡承担约 7.5GB 权重 一半的 KV Cache。16K 上下文、8 并发下每张卡 KV Cache 约 6.4GB加上权重和开销约 16GB24GB 卡有充足余量。实操心得双卡方案最大的坑是两张卡的型号不一致。我试过 3090 4090 混插虽然能跑但吞吐量受限于慢的那张卡而且驱动兼容性问题很多。强烈建议双卡同型号。4. 核算之外那些实际部署才会暴露的问题理论和实测数据都算完了但真正部署到生产环境还有几个核算覆盖不到的问题。4.1 显存碎片运行几小时后突然 OOM这是最让人头疼的问题。启动时显存占用正常跑几个小时后突然 OOM。原因是 PyTorch 的显存分配器会产生碎片尤其是当请求长度变化很大时有的请求 500 token有的 8000 token碎片化会越来越严重。解决方案设置PYTORCH_CUDA_ALLOC_CONFexpandable_segments:True让分配器使用可扩展段减少碎片限制最大上下文和最小上下文的差距避免极端长度混合定期重启服务比如每天凌晨这是最粗暴但最有效的办法4.2 首 token 延迟用户感知的瓶颈吞吐量数据好看不代表用户体验好。用户最敏感的是首 token 延迟也就是从提交请求到看到第一个字的时间。这个时间主要由 Prefill 决定。优化首 token 延迟的方法Prefix Caching多轮对话场景必开Chunked Prefill把长 Prefill 拆成小块和 Decode 交错执行避免长请求阻塞短请求限制最大输入长度超过一定长度的输入直接拒绝或者截断vLLM 的--enable-chunked-prefill参数就是干这个的。开启后一个 32K 的 Prefill 会被拆成多个 chunk每个 chunk 处理完后可以插入其他请求的 Decode整体延迟分布更均匀。4.3 量化模型的精度验证不能省INT4 量化在 27B 上总体表现不错但在某些任务上会有明显退化。我遇到过的情况数学推理INT4 下多步计算容易出错FP16 下正确率 85%INT4 下掉到 70%代码生成简单代码没问题复杂逻辑比如递归、边界条件容易出 bug长文本摘要超过 4K 的输入INT4 容易丢失中间部分的细节所以量化后一定要做任务级别的验证不能只看困惑度PPL指标。PPL 涨 0.1 看起来无所谓但实际任务准确率可能掉 10 个点。验证方法准备 50-100 条你实际业务场景的测试用例分别用 FP16 和 INT4 跑一遍对比输出质量。如果关键任务退化超过 5%就要考虑换 INT8 或者接受精度损失。4.4 吞吐量测试的正确姿势最后说下怎么测吞吐量才准确。很多人测出来的数字偏高因为测试方法有问题。正确的测试流程预热先跑 10-20 个请求让 CUDA kernel 编译完成、显存分配器稳定固定输入输出长度不要用随机长度否则方差很大测量稳态吞吐去掉前几个请求只统计稳定后的数据分别测 Prefill 和 Decode这两个阶段的性能特征完全不同多轮测试取中位数单次测试受系统负载影响大我常用的测试脚本逻辑import time import requests def benchmark(url, prompt, max_tokens500, rounds20): latencies [] for i in range(rounds): start time.time() response requests.post(url, json{ prompt: prompt, max_tokens: max_tokens, temperature: 0 }) elapsed time.time() - start if i 5: # 跳过预热 latencies.append(elapsed) latencies.sort() median latencies[len(latencies) // 2] throughput max_tokens / median print(f中位延迟: {median:.2f}s, 吞吐: {throughput:.1f} token/s)这个脚本测的是端到端延迟包含了网络传输和排队时间。如果要测纯推理性能应该用框架自带的 benchmark 工具比如 vLLM 的benchmark_throughput.py。5. 一套可复用的核算流程把上面的内容串起来形成一套可复用的核算流程。每次拿到新硬件或者新模型按这个流程走一遍基本不会翻车。第一步算权重显存。参数量 × 精度字节数。FP16 是 2 字节INT8 是 1 字节INT4 是 0.5 字节。27B 模型 FP16 是 54GBINT4 是 13.5GB实际因为量化组开销会略高按 15-16GB 估。第二步算 KV Cache。用公式2 × 层数 × KV头数 × 头维度 × 序列长度 × 并发数 × 精度字节数。这一步必须查模型的实际配置不能拍脑袋。第三步加框架开销。一般留 1.5-3GB取决于框架和是否开启额外功能。第四步算显存余量。总显存减去前三步余量要大于 10%否则运行中容易 OOM。第五步算吞吐上限。显存带宽 ÷ 权重读取量 单并发理论吞吐。实际打 6-7 折。第六步算并发下的总吞吐。用实测曲线估算不要线性叠加。一般 4 并发是性价比拐点。第七步验证。用实际测试脚本跑一遍对比理论值和实测值。如果差距超过 30%说明某个环节算错了。这套流程我用了大半年覆盖了 7B 到 72B 多个模型、五六种硬件配置基本没出过大错。唯一一次翻车是低估了 KV Cache 的量化开销——当时以为 KV Cache 也能跟着权重量化省显存实际上默认是不省的需要单独配置。最后分享一个小技巧如果你不确定某个配置能不能跑先用--max-model-len设一个很小的值比如 2048启动确认权重能加载、服务能起来再逐步往上调上下文和并发。这样比一上来就设大值然后 OOM 反复调试要高效得多。每次调整后观察显存占用找到那个刚好不 OOM 的临界点再往回退 10% 作为生产配置。