全精度无量化278 tok/s?DeepSeek V4 Flash推理性能复现与验证指南 看到 DeepSeek V4 Flash at 278 tok/s, full precision, no quantization 这样一条性能描述时很多人的第一反应是速度快第二反应是想在自己的环境里复现一遍。但问题是tok/s 这个数字并不是模型与生俱来的属性而是模型权重、精度格式、推理引擎、GPU 显存带宽、并发数、上下文长度、KV cache 策略共同作用的结果。278 tok/s 只有在测试条件明确时才有工程意义什么 GPU、什么精度、单请求还是多并发、输入多长、输出多长、研究的是首 token 延迟还是持续生成速度。如果把这些问题忽略掉「full precision、no quantization」的标签再好看也只是宣传话术不是可复现的测试结论。这里要解决的问题是如何拆解这条性能描述如何在自己的机器上复现全精度无量化推理以及如何判断别人给出的 tok/s 到底可不可信。1. 先拆解性能描述tok/s、full precision、no quantization 分别指什么1.1 tok/s 是生成吞吐不是用户感知的「快」在大语言模型推理场景中文本先被切分成 token模型每次生成一个 token再把这个 token 拼进上下文继续预测下一个。tok/s 全称 tokens per second表示模型每秒钟能够生成的 token 数量。它衡量的是 decode 阶段的持续输出速度是一个可以直接观测、可以对比的数值。但用户感知到的「快慢」实际由两部分组成从提交请求到模型吐出第一个 token 的时间以及从第一个 token 到最后一个 token 的滚动生成时间。前者叫 TTFT也就是首 token 延迟主要由 prefill 阶段决定后者才对应 tok/s。一个系统可能 TTFT 很快但后续生成很慢也可能生成速度很快但首 token 等了三秒钟。只看 tok/s 并不足以判断服务体验。所以在评估任何 tok/s 数据之前先确认统计口径指标统计方式主要受什么影响对用户体验的影响TTFT请求发出到第一个 token 开始返回prefill 计算量、输入长度、排队时间决定「第一反应快不快」generation tok/s从首个 token 之后开始统计计算生成阶段平均速度显存带宽、权重大小、batch 策略决定「输出滚动快不快」end-to-end tok/s全请求耗时换算TTFT、生成速度、网络传输决定整体等待时间throughput多并发请求下单位时间完成的总 token 数batch 策略、KV cache 容量、显存决定服务容量一条「278 tok/s」如果只写了生成速度没有写 TTFT 和测试输入输出长度它只能说明模型在某个阶段的滚动输出能力不能代表完整响应性能。1.2 full precision 在推理语境里通常指 FP16/BF16「full precision」直译是全精度。但大模型推理里这个词容易产生误解。如果指 FP32那绝大多数推理场景都不会用因为显存占用和带宽成本太高推理速度会慢到一个基本不可用的状态。社区讨论里说的 full precision 或 no quantization通常指权重按 FP16 或 BF16 加载不降级到 INT8、INT4、FP8 等低精度格式。精度直接影响三个工程指标权重文件大小FP32 约为每参数 4 字节FP16/BF16 约为每参数 2 字节INT8 约为每参数 1 字节INT4 约为每参数 0.5 字节。显存占用权重占用的显存随精度字节数线性变化权重越小剩余显存越多能容纳的 KV cache 和并发 batch 也越多。显存带宽读取量decode 阶段每生成一个 token 都要重新读取全部权重权重越紧凑读取耗时越短tok/s 越高。所以当一条性能描述特别强调「full precision」时它其实是在说明速度不是靠降低权重精度换来的权重加载和运算的数值格式保持在一个较高的精度水平。1.3 no quantization 是一个负向定义强调没有为速度牺牲精度量化quantization在推理里是一种压缩手段把权重从 2 字节压到 1 字节甚至 0.5 字节从而降低显存占用并提升速度。量化做得好时多数场景肉眼几乎看不出输出差异但量化对数值范围和分布是敏感的若校准集选得不好模型的逻辑推理、长文本生成、指令遵循能力可能出现退化。「no quantization」是一个负向定义它排除了「先用 INT4 把模型压缩到显存足够小再测出一个漂亮 tok/s」的路径。也就是说这条性能数据不是通过压缩权重获得的而是由硬件、推理引擎和其他优化手段推动的。不过要注意不量化不代表没有其他优化。连续批处理、PageAttention、CUDA Graph、前缀缓存、投机解码等技术都会显著影响 tok/s。即使同样是全精度无量化不同推理引擎、不同参数配置下跑出来的速度可能相差很大。2. 同一个 278 tok/s背后的硬件与运行时条件可能完全不同2.1 显存带宽决定 decode 阶段的理论速度上限decode 阶段是典型的访存密集任务。每轮生成只计算一个 token但需要从显存里读取全部权重读取权重的时间远远大于矩阵计算时间。因此单请求单 batch 场景下最高生成速度可以粗略用下面的关系估算最高可预期 tok/s ≈ 显存有效带宽 / 权重字节数举例来说明这个公式的用法。假设某个模型的 BF16 权重为 120GB某块 GPU 的实际读取带宽约为 3.3TB/s那么单请求 decode 理论上限大约是 27 tok/s 量级。实际因为调度、计算、缓存命中率等原因会比这个上限更低。反过来如果你看到有人宣称在单请求场景下测出 278 tok/s就可以用他的 GPU 带宽反推模型权重大致在几十 GB 减一两个数量级才比较合理然后判断测试环境是否真实。这里有一个很实用的复现检查逻辑速度数字和权重大小、显存带宽必须自洽。如果模型很大、GPU 带宽普通理论上限就摆在那里任何宣称值超过上限都要追问统计口径。2.2 batch、动态批处理和单请求口径的差异tok/s 有两种截然不同的统计方式。一种是单请求生成速度只衡量一个请求在 GPU 上的持续输出另一种是系统总吞吐把多个并发请求合并进一个 batch统计单位时间完成的总 token 数。大模型推理服务普遍使用连续批处理。推理引擎每步检查当前 batch 里哪些请求已经结束、哪些请求可以插入然后动态重组 batch。并发提高后GPU 单位时间产出的总 token 数会明显上升但单个请求分到的生成速度可能基本不变也可能略降。因为 batch 中每个 step 需要同时处理多个请求的解码计算和读取总量增加了但单个请求的权重读取成本是共享的。因此听到「278 tok/s」时必须确认是单请求速度还是并发总吞吐并发数是多少每个请求的输出长度是否一致batch 上限是否被显存限制。只有把这些条件全部固定性能数据才具备横向比较的基础。2.3 上下文长度和 KV cache 对速度的影响模型生成过程中会把历史 token 对应的 Key 和 Value 缓存到显存形成 KV cache。输入越长、已经生成的 token 越多KV cache 占用越大。同一块 GPU 上如果把 max_model_len 从 4096 提升到 32768KV cache 可能占用几十倍显存可容纳的并发 batch 数量会明显下降。复现性能测试时上下文长度必须固定。推荐方案是规定「输入约 1000 token输出约 1000 token」然后统计生成阶段 tok/s。如果输入太短例如只有 10 个 tokenprefill 计算量小KV cache 尚未增长测出来的速度会偏向乐观如果输出很长随着上下文不断累积虽然显存够用调度和内存访问模式也可能发生变化。注意性能复现不是把模型启动起来就结束。输入长度、输出长度、并发数任何一个变化tok/s 都可能变化 30% 以上。3. 用 vLLM 复现一次全精度、无量化推理并实测 tok/s3.1 环境准备先确认 GPU、驱动和显存余量完整复现的第一步是确认目标机器的 GPU 情况。执行nvidia-smi重点看四类信息GPU 型号和显存总量当前显存占用是否有残留的推理进程驱动版本功耗上限和当前温度。再确认 CUDA 编译环境nvcc --version如果使用 PyTorch 系的推理引擎还需要确认 Python、PyTorch、CUDA 三者版本匹配。推荐先在一个几 GB 的小模型上跑通全流程再加载目标大模型。不要一开始就把最大模型塞进显存参数配错会在加载阶段直接 OOM反而排查起来更麻烦。3.2 安装 vLLM以 vLLM 作为推理引擎示例常规安装命令pip install vllm生产环境建议使用虚拟环境或容器并固定 vLLM 版本。不同版本的 vLLM 对 CUDA 版本、Python 版本、驱动版本要求不同安装前要先看对应版本的官方文档。如果从源码安装还需要编译环境耗时更长遇到编译错误的可能性也更大建议优先走预编译包。3.3 启动全精度、无量化的 OpenAI 兼容服务假设模型权重已经放到 /data/models/deepseek-v4-flash 目录用 vLLM 启动一个 OpenAI 兼容的服务python -m vllm.entrypoints.openai.api_server \ --model /data/models/deepseek-v4-flash \ --dtype bfloat16 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --port 8000参数含义--model模型权重目录或仓库名--dtype bfloat16明确按 BF16 加载权重这是「full precision」的关键配置--max-model-len 8192限制最大上下文长度避免 KV cache 无限膨胀--gpu-memory-utilization 0.9最多使用 90% 显存给 CUDA context 和系统留出余量--port 8000HTTP 服务监听端口。关于量化声明vLLM 的部分版本支持--quantization none显式关闭量化加载。如果当前版本支持可以直接加上如果参数不存在就保持默认加载方式并确认模型目录中只有非量化权重。不要同时在一个目录里混合存放 AWQ、GPTQ、FP8 版本和原始版本这样容易加载错文件。服务启动后日志里会显示加载精度、显存分配、模型路径等信息。看到dtypetorch.bfloat16、quantizationNone之类的日志才说明当前这个进程确实是全精度加载。3.4 用流式请求测量首 token 延迟和生成 tok/s启动服务后在另一个终端运行测量脚本。脚本通过流式接口接收返回的 token并记录 TTFT、生成阶段耗时和精确 token 数import json import time import requests from transformers import AutoTokenizer model_dir /data/models/deepseek-v4-flash url http://localhost:8000/v1/completions tokenizer AutoTokenizer.from_pretrained(model_dir) payload { model: model_dir, prompt: 请解释什么是连续批处理并给出一个使用场景。, max_tokens: 256, temperature: 0.7, stream: True, } start time.time() first_token_at None pieces [] with requests.post(url, jsonpayload, streamTrue) as resp: for line in resp.iter_lines(): if not line: continue line line.decode(utf-8) if line.startswith(data: ): data line[6:] if data [DONE]: break obj json.loads(data) choices obj.get(choices, []) if not choices: continue delta choices[0].get(delta, {}) content delta.get(content, ) if content: if first_token_at is None: first_token_at time.time() pieces.append(content) end time.time() generated_text .join(pieces) total_time end - start ttft (first_token_at - start) if first_token_at is not None else total_time generate_time (end - first_token_at) if first_token_at is not None else total_time token_num len(tokenizer.encode(generated_text)) speed token_num / generate_time if generate_time 0 else 0 print(f生成文本前 80 字符: {generated_text[:80]}) print(ftoken 数: {token_num}) print(fTTFT 首 token 延迟: {ttft:.3f} s) print(f生成阶段耗时: {generate_time:.3f} s) print(f生成速度: {speed:.2f} tok/s) print(f总耗时: {total_time:.3f} s)脚本里有一个容易踩坑的地方不能用len(generated_text)替代 token 数因为字符数和 token 数不是一回事。一个英文单词可能是一个 token一个中文字符也可能是 0.5 到 2 个 token。严谨做法是用模型配套的 tokenizer 对完整生成文本重新编码再统计 token 数量。完整测量建议连续执行三轮以上去掉最高值和最低值后取中位数。测试过程中不要在同一个 GPU 上跑其他大任务也不要手动开浏览器频繁请求同一个服务否则结果会被干扰。3.5 如何确认确实「没有量化」测量 tok/s 前先确认权重文件确实是非量化版本。最直接的方法是查看模型目录ls -lh /data/models/deepseek-v4-flash如果目录中存在quantize_config.json、*.gptq.safetensors、*.awq.safetensors、*fp8*等文件说明权重是量化格式。正常的非量化权重目录应该是一组完整的 SaferTensors 分片文件比如model-00001-of-0000X.safetensors以及config.json、tokenizer.json、tokenizer_config.json等文件。第二个判断依据是启动日志。多数推理引擎会打印加载精度和量化方式。日志里如果出现GPTQ、AWQ、FP8、W8A8等关键字就不是全精度加载。如果只有dtypetorch.bfloat16或quantizationNone说明符合「full precision, no quantization」的条件。4. 验证一条性能数据是否可信的四个步骤4.1 确认统计口径是否可以复现无论是官方宣传、社区测评还是某位博主的测试第一步都是还原统计口径。一个可复现的性能描述至少应该包含模型名称和权重版本以及精度格式GPU 型号、数量、显存大小推理引擎名称和版本并发数、单请求 max_tokens、输入输出长度tok/s 是单请求生成速度还是并发总吞吐。缺任何一项数字都只能当一个参考印象不能作为部署决策依据。实际项目中如果同事转述一个 tok/s 数据最好直接索要完整测试命令和脚本而不是记下一个没有上下文的数字。4.2 用权重字节数反推带宽上限前面提到过粗估公式权重字节数 参数量 * 2BF16 理论上限 GPU 有效带宽 / 权重字节数当宣称速度明显高于理论上限时有几种常见解释宣称条件合理判断需要追问单请求 278 tok/s模型权重约 3GB合理接近一次读完整份权重的速度GPU 是否高端、权重是否确实 3GB单请求 278 tok/s模型权重约 140GB不太可能是否开了投机解码、是否并发统计并发 32 场景总吞吐 278 tok/s很正常单用户实际感知速度和 queue 延迟怎样这里要特别注意这条反推只是粗估不是精确公式。如果推理引擎启用了投机解码模型会在一次 step 中生成多个候选 token实际输出 token 数可能大于普通 decode 的步数tok/s 看起来会明显更高。4.3 检查上下文长度、KV cache 和并发的影响上下文长度改变时KV cache 占用变化很大。同样一块显卡max_model_len 从 4096 提升到 32768KV cache 占用成倍增长能容纳的并发 batch 数量下降系统的总吞吐可能明显下滑。并发数改变时单请求延迟和系统吞吐的走势也可能相反。并发从 1 提升到 16总吞吐通常上升但单个请求的 TTFT 可能恶化因为请求可能在队列里等待前一个长任务结束。因此横向对比两组数据时如果上下文长度、并发数、输出长度不同tok/s 不能直接做减法。4.4 用同样的 prompt 和参数做多轮实测最终判断要靠自己的机器复现。固定输入 prompt、固定 max_tokens、固定并发连续跑三到五轮取中位数。如果自己复现的结果和宣称值偏差超过 20%按优先级检查是否用了不同精度或量化格式是否启用了投机解码、前缀缓存、CUDA Graph是否 GPU 被其他进程占满是否推理引擎版本不同是否 max_model_len、gpu-memory-utilization 设置不同。多数 tok/s 争议都不是模型本身的问题而是测试配置和统计方式不一致。5. 本地实测速度上不去时按这条链路排查5.1 全精度模型加载时直接 OOM现象启动服务后报 CUDA out of memory进程退出。可能原因权重本身很大再加上 KV cache 和 CUDA context 超过显存max_model_len 设置过大KV cache 占掉太多显存gpu-memory-utilization 设置过高留给 CUDA context 的空间不足当前 GPU 上还有其他推理服务或训练任务。处理顺序nvidia-smi先确认没有残留进程占用显存。然后逐步调低--max-model-len例如从 32768 降到 8192再把--gpu-memory-utilization从 0.9 降到 0.8。如果还是 OOM最后考虑换更大显存的 GPU或者使用多卡并行。多卡启动可以在命令里加上--tensor-parallel-size 2此时模型权重会切分到两张 GPU 上单卡显存压力下降但要确认机器的卡间通信带宽和驱动环境支持。5.2 日志显示加载成功但生成速度明显低于宣称值现象模型能跑但实测 tok/s 比宣称值低不少。可能原因宣称值用了 INT4 或 FP8本地用了全精度宣称值开了投机解码本地没有开并发数不同batch 填充率不同GPU 被其他任务占用或温度、功耗墙导致降频。检查命令nvidia-smi查看 GPU 利用率、显存使用、功耗和温度。再持续观察watch -n 1 nvidia-smi如果 GPU 利用率很低但速度也低先怀疑 Python 侧流式处理、tokenizer 统计方式或请求排队如果利用率很高但速度不理想再去检查权重文件大小和推理引擎版本因为这些直接决定单步读取量。5.3 首 token 延迟很高后续生成还算快现象TTFT 要等好几秒但开始输出后 tok/s 正常。可能原因输入 prompt 太长prefill 阶段计算量很大请求排队前面有长输出请求没有结束没有开启前缀缓存相同前缀的重复请求也要重新做 prefillmax_model_len 很大推理引擎初始化时分配了大量 CUDA Graph 空间。处理建议测试时缩短输入 prompt查看服务日志中 prefill 阶段的耗时统计必要时开启前缀缓存或限制单请求 max_tokens。5.4 从现象到根因的排查顺序表顺序检查内容命令或方式关键点1测量脚本和统计方式是否正确检查 token 统计、时间节点排除「测错了」2GPU 是否空闲nvidia-smi排除其他进程干扰3加载的是否量化权重查看模型目录和启动日志排除精度差异4KV cache 是否占用过大检查 max_model_len 设置排除容量挤压5是否开启投机解码、前缀缓存查看启动参数排除优化差异6推理引擎版本vllm --version排除内核实现差异7硬件带宽是否符合预期对比权重字节数和宣称速度排除物理上限矛盾6. 全精度与量化为什么有人坚持不量化有人必须量化6.1 量化提速的三个来源量化加速的核心原因是减少显存带宽读取量。decode 阶段每生成一个 token 都要读取权重权重从 2 字节压缩到 1 字节单次读取时间接近减半。这是最主要的速度来源。第二个来源是显存占用下降。权重变小后省下来的显存可以分配给 KV cache 和更大的 batch。batch 增大后GPU 单步处理的 token 数变多系统总吞吐上升。第三个来源是部分硬件对低比特矩阵运算有专门指令。例如 INT4 或 INT8 的矩阵乘法在一些 GPU 上可以走专用路径计算单元效率更高。但这一点依赖硬件型号和推理引擎是否针对该格式做了优化。量化也不是没有代价。INT4 对权重数值范围和分布要求更高如果校准集选得不好模型可能在某些问题上输出不稳定。FP8 压缩率比 INT4 低但精度损失往往更小是质量和性能之间的折中选择。6.2 全精度的不可替代场景全精度最大的价值是稳定和可解释。模型行为出现异常时全精度可以作为基线如果全精度下模型回答正常量化后出现乱码或逻辑跳变就可以判断问题出在量化校准或量化参数上。反过来如果一开始就直接量化出了问题很难定位是权重问题还是量化误差问题。开发调试阶段建议始终使用全精度。调 prompt、调参数时先确认模型在全精度下的行为基线再决定是否量化。否则一个「某次输出特别差」的问题可能让你在代码逻辑和模型权重之间反复排查浪费大量时间。6.3 按场景选型而不是按热闹选型使用场景部署建议原因开发调试、问题排查全精度便于定位模型行为问题显存非常紧张低比特量化先保证能加载运行输出质量、数学推理敏感全精度或 FP8减少精度损失高并发线上服务量化 大 batch优先提升整体吞吐性能优化前先全精度测基线量化收益才有对比基础实际项目里不要一上来就追低比特量化。如果全精度在当前硬件上显存够用、速度可接受保持全精度反而是最省事的方案。只有当显存成为瓶颈或并发吞吐确实不够时才引入量化并按质量评测结果决定是否回退。7. 参数设置和模型对比以 DeepSeek V4 Flash 与同类模型为例7.1 对比前先把参数设置统一社区里讨论 DeepSeek V4 Flash 和 GLM-5.3-Flash 谁更快时最常出现的问题是测试条件不统一。下列参数只要有一个不同tok/s 就不具备可比性dtype一个用 BF16一个用 INT8速度差异可能超过一倍max_model_len一个 8192一个 32768KV cache 分配完全不同gpu_memory_utilization一个 0.95一个 0.8能容纳的 batch 上限不同tensor_parallel_size单卡和多卡访存模型完全不同是否开启流式返回是否开启前缀缓存是否开启投机解码。所以在看任何「XX 比 XX 快」的结论时先要求对方给出完整启动命令和参数设置。没有参数配置的对比属于体验描述不能当成工程结论直接复用。7.2 固定精度、固定 prompt、固定并发的对比方法推荐的最小对比方案如下两个模型都使用全精度 BF16 加载使用同一批 prompt长度控制在 500 到 1500 token 之间每个模型各测 5 轮取 TTFT、单请求生成速度、总吞吐的中位数记录峰值显存生成文本保存下来按指令完成度、逻辑性、稳定性做质量抽样评估。这样做的好处是能区分「模型结构差异带来的性能差异」和「部署配置差异带来的性能差」。如果 A 模型用了 INT4 而 B 模型用全精度测出的速度快慢根本不能说明模型本身优劣。7.3 tok/s 之外还需要看的指标tok/s 是重要指标但不是唯一指标。实际选型至少还要看三个方面显存占用同样性能下显存更省意味着能开更高并发或部署更灵活输出质量对格式要求严格的任务速度再快输出格式稳定才是前提长上下文稳定性并发高、上下文长时是否出现 OOM、延迟尖刺或输出异常。一套完整的模型评测应该把速度、质量、稳定性放在一起看。单看某一天的 tok/s 排名来定生产方案风险很高。8. 实践建议与可复用清单8.1 可复现性能描述的最小信息清单以后看到任何 tok/s 数据先检查信息是否完整。最少应包含模型名称和权重版本精度和量化方式GPU 型号、数量、显存容量推理引擎名称与版本并发数与 batch 策略输入长度、输出长度、max_model_lentok/s 的统计口径是否启用投机解码、前缀缓存、CUDA Graph。如果对方给出的信息少于五项这个数字只能当参考不能直接用于容量规划。8.2 全精度部署前检查清单在准备全精度无量化部署时可以按下面列表逐项确认[ ] 磁盘空间足够保存原始权重文件[ ] 显存能放下权重、KV cache 和 CUDA context[ ] 驱动、CUDA、Python、推理引擎版本互相兼容[ ] 模型目录中只有非量化权重没有混入 GPTQ、AWQ、FP8 文件[ ] 启动参数显式设置--dtype bfloat16或等价的精度配置[ ] max_model_len 根据实际业务长度设定而不是盲目取最大值[ ] gpu-memory-utilization 保留足够余量[ ] 准备 tokenizer 用于精确统计 token 数[ ] 测试时固定输入输出长度和并发数[ ] 至少跑三轮取中位数而不是最大值。8.3 给新手的练习路径想真正建立对 tok/s 的判断力建议按下面顺序练一遍找一个几 GB 的公开小模型先跑通 vLLM 全精度加载用流式接口和 tokenizer 准确测量 TTFT 与生成速度把同一模型用 INT4 或 FP8 格式加载重新测速度、显存和输出质量调整 max_model_len 和并发数观察速度指标如何变化记录所有条件形成一份自己的测试模板。跑完这五步之后再看到「278 tok/s、full precision、no quantization」这样的描述你会自然先去想测试环境而不是直接相信它是一个可以无条件复制的性能结果。性能数据最终只有在自己的硬件、自己的参数设置下重新测一遍才能变成工程部署的可靠依据。