
大模型评测体系的性能维度吞吐、延迟、并发与资源效率的量化方法一、当评测不再只看榜单分数——大模型选型的隐性性能成本技术团队在选型大模型时往往陷入一个常见的误区只关注 MMLU、C-Eval 等基准榜单分数却忽略了模型在真实生产环境中的性能表现。当我们把 LLM 作为一个在线推理服务部署时它和传统微服务一样面临着吞吐、延迟、并发的三维约束。不同模型在相同准确度下的推理成本可能相差 5 至 10 倍这意味着如果将 MMLU 分数作为唯一选型标准月度 GPU 账单可能会超出预算 3 倍。某次技术选型评审中团队需要在 Qwen2.5-72B、DeepSeek-V2 和 Llama-3.1-70B 三个候选模型中选择一个接入智能客服系统。三个模型在业务评测集上的 F1 分数分别为 91.2%、90.8% 和 91.5%差异在统计误差范围内。但在相同并发100 QPS和相同硬件8×A100的压测环境下三者的首 Token 延迟分别为 180ms、320ms 和 410msToken 生成速率分别为 85 t/s、62 t/s 和 48 t/s。最终选型结果并非基于那 0.7% 的精度差而是基于近 3 倍的吞吐差异。这个案例揭示了一个关键事实大模型的性能评测需要从准确度单维度扩展到多维度体系将吞吐、延迟、并发和资源效率作为与准确度同等重要的一级指标。只有将性能约束纳入评测框架选型决策才是完整的。二、四维性能指标体系——从单点指标到系统视角的评测框架吞吐维度的核心指标是每秒生成的 Token 数。它直接决定了在给定 GPU 资源下能承载的业务并发量。单请求吞吐受 Prompt 长度和输出长度共同影响短 Prompt512 Token下吞吐高长 Context8K Token由于 KV Cache 压力吞吐会显著下降。批处理吞吐衡量并发推理场景下的吞吐总量受 Continuous Batching 策略影响极大。延迟维度需要拆解为三个细粒度指标。首 Token 延迟Time to First TokenTTFT指用户发送请求到收到第一个 Token 的时间包含网络传输、请求排队、Prompt 预填充和第一个 Token 生成的全过程。单 Token 生成延迟Time per Output TokenTPOT指自回归生成阶段每个 Token 的平均耗时主要取决于计算带宽。端到端延迟是 TTFT TPOT × 输出 Token 数是用户体验的直接感知值。并发维度不是简单地统计同时处理的请求数而是构建延迟-并发曲线。在给定的延迟 SLO 下如 P99 TTFT 500ms通过递增并发数找到服务能承载的上限。这个曲线的拐点就是该模型在特定硬件上的最佳并发水位。资源效率维度关注单位资源的产出效率。Token/GPU-h 衡量每 GPU 小时能生成的 Token 数直接关联成本。显存带宽利用率反映模型是否充分使用了 GPU 的 HBM 带宽对于显存带宽受限的大模型推理这个指标往往比浮点利用率更重要。MFUModel FLOPs Utilization衡量实际计算效率与理论峰值的比值。三、基于 vLLM 与自动化测试框架的性能压测代码——生产级的评测流水线以下代码构建了一个可复现的模型性能评测流水线基于 vLLM 推理引擎 Locust 压测框架支持四维指标的自动化采集。import okhttp3.*; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.time.Duration; import java.time.Instant; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; /** * 大模型性能评测引擎——采集四维性能指标 */ public class LLMPerformanceEvaluator { private static final String VLLM_ENDPOINT http://localhost:8000/v1/completions; private static final OkHttpClient HTTP_CLIENT new OkHttpClient.Builder() .connectTimeout(Duration.ofSeconds(30)) .readTimeout(Duration.ofSeconds(120)) .build(); private static final Gson GSON new Gson(); /** * 单次推理请求——返回首Token延迟、端到端延迟、生成Token数 */ static class InferenceMetrics { long ttftMs; // 首Token延迟 long totalMs; // 端到端延迟 int tokenCount; // 生成Token数 boolean success; String errorMessage; double tokensPerSecond() { return totalMs 0 ? (tokenCount * 1000.0) / totalMs : 0; } } /** * 评测用例配置 */ static class EvalConfig { int warmupRounds 10; int testRounds 100; int maxConcurrency 32; int maxTokens 512; String prompt; String modelName default; } private final EvalConfig config; private final ListInferenceMetrics metricsList new CopyOnWriteArrayList(); private final ConcurrentLinkedQueueLong ttftQueue new ConcurrentLinkedQueue(); private final AtomicLong totalTokensGenerated new AtomicLong(); private final AtomicLong totalTimeMs new AtomicLong(); private final String outputCsvPath; public LLMPerformanceEvaluator(EvalConfig config, String outputCsvPath) { this.config config; this.outputCsvPath outputCsvPath; } /** * 执行完整评测流程预热 → 单并发基准 → 递增并发压测 */ public void runFullEvaluation() throws Exception { // 第一阶段预热 System.out.println( 预热阶段 ); runConcurrencyTest(1, config.warmupRounds, true); // 第二阶段单并发基准 System.out.println( 单并发基准测试 ); metricsList.clear(); ttftQueue.clear(); totalTokensGenerated.set(0); totalTimeMs.set(0); runConcurrencyTest(1, config.testRounds, false); printSingleRequestStats(); // 第三阶段并发递增测试 int[] concurrencyLevels {1, 4, 8, 16, 32}; try (PrintWriter csvWriter new PrintWriter( new FileWriter(outputCsvPath))) { csvWriter.println(并发数,TTFT_P50,TTFT_P99,吞吐(token/s),成功率); for (int concurrency : concurrencyLevels) { if (concurrency config.maxConcurrency) break; System.out.println( 并发测试: concurrency ); metricsList.clear(); ttftQueue.clear(); totalTokensGenerated.set(0); totalTimeMs.set(0); runConcurrencyTest(concurrency, config.testRounds, false); double p50 calculatePercentile(new ArrayList(ttftQueue), 50); double p99 calculatePercentile(new ArrayList(ttftQueue), 99); double throughput totalTokensGenerated.get() * 1000.0 / Math.max(totalTimeMs.get(), 1); long successCount metricsList.stream().filter(m - m.success).count(); double successRate successCount * 100.0 / metricsList.size(); csvWriter.printf(%d,%.2f,%.2f,%.2f,%.2f%%%n, concurrency, p50, p99, throughput, successRate); System.out.printf(并发%d, P50%.2fms, P99%.2fms, 吞吐%.2f token/s, 成功率%.2f%%%n, concurrency, p50, p99, throughput, successRate); } } } private void runConcurrencyTest(int concurrency, int rounds, boolean isWarmup) throws Exception { ExecutorService executor Executors.newFixedThreadPool(concurrency); CountDownLatch latch new CountDownLatch(rounds); Instant start Instant.now(); for (int i 0; i rounds; i) { executor.submit(() - { try { InferenceMetrics metrics sendInferenceRequest(); if (!isWarmup) { metricsList.add(metrics); if (metrics.success) { ttftQueue.add(metrics.ttftMs); totalTokensGenerated.addAndGet(metrics.tokenCount); } } } catch (Exception e) { if (!isWarmup) { InferenceMetrics failMetrics new InferenceMetrics(); failMetrics.success false; failMetrics.errorMessage e.getMessage(); metricsList.add(failMetrics); } } finally { latch.countDown(); } }); } latch.await(5, TimeUnit.MINUTES); executor.shutdownNow(); if (!isWarmup) { totalTimeMs.addAndGet(Duration.between(start, Instant.now()).toMillis()); } } private InferenceMetrics sendInferenceRequest() throws IOException { JsonObject body new JsonObject(); body.addProperty(model, config.modelName); body.addProperty(prompt, config.prompt); body.addProperty(max_tokens, config.maxTokens); body.addProperty(stream, false); Request request new Request.Builder() .url(VLLM_ENDPOINT) .post(RequestBody.create( GSON.toJson(body), MediaType.parse(application/json))) .build(); Instant requestStart Instant.now(); try (Response response HTTP_CLIENT.newCall(request).execute()) { if (!response.isSuccessful()) { InferenceMetrics failMetrics new InferenceMetrics(); failMetrics.success false; failMetrics.errorMessage HTTP response.code(); return failMetrics; } String respBody response.body() ! null ? response.body().string() : ; JsonObject respJson GSON.fromJson(respBody, JsonObject.class); // 从响应中提取性能数据 InferenceMetrics metrics new InferenceMetrics(); metrics.success true; metrics.totalMs Duration.between(requestStart, Instant.now()).toMillis(); metrics.tokenCount respJson.getAsJsonObject(usage) .get(completion_tokens).getAsInt(); // TTFT 的精确获取需要 streaming 模式简化估算 metrics.ttftMs (long) (metrics.totalMs * 0.15); return metrics; } catch (IOException e) { throw new IOException(推理请求失败: config.modelName, e); } } private void printSingleRequestStats() { LongSummaryStatistics ttftStats ttftQueue.stream() .mapToLong(Long::longValue).summaryStatistics(); LongSummaryStatistics totalStats metricsList.stream() .filter(m - m.success) .mapToLong(m - m.totalMs).summaryStatistics(); double throughput totalTokensGenerated.get() * 1000.0 / Math.max(totalTimeMs.get(), 1); System.out.printf(单并发基准: TTFT avg%.2fms, E2E avg%.2fms, 吞吐%.2f token/s%n, ttftStats.getAverage(), totalStats.getAverage(), throughput); } private double calculatePercentile(ListLong sortedValues, int percentile) { if (sortedValues.isEmpty()) return 0; Collections.sort(sortedValues); int index (int) Math.ceil(percentile / 100.0 * sortedValues.size()) - 1; index Math.max(0, Math.min(index, sortedValues.size() - 1)); return sortedValues.get(index); } public static void main(String[] args) throws Exception { EvalConfig config new EvalConfig(); config.prompt 请详细介绍 Java 虚拟机的垃圾回收机制 包括 Serial、Parallel、CMS、G1 和 ZGC 的特点与适用场景。; config.maxTokens 512; config.modelName qwen2.5-72b-instruct; config.warmupRounds 5; config.testRounds 50; config.maxConcurrency 32; LLMPerformanceEvaluator evaluator new LLMPerformanceEvaluator( config, llm_perf_results.csv); evaluator.runFullEvaluation(); } }上述代码有几个工程考量。vLLM 的非流式请求无法精确获取 TTFT精确的 TTFT 测量需要启用 streaming 模式并逐 Token 解析 SSE 事件。递增并发测试从 1 开始等比增长而非线性增长因为延迟-并发曲线通常在某个拐点后急剧恶化等比采样能更好地捕捉这个拐点。测试结果写入 CSV 文件而非仅在日志输出是为了支持后续的历史趋势对比和不同模型间的横向度量。四、评测体系中的关键陷阱——避免数据漂亮但上线翻车评测体系中存在几个常见的误导性陷阱。第一个陷阱是使用固定的短 Prompt 进行基准测试。如果 POC 阶段用 200 Token 的 Prompt 测得吞吐为 80 token/s但生产环境的实际 Prompt 平均长度为 3K Token则实际吞吐可能掉到 30 token/s。评测 Prompt 的长度分布必须与生产流量对齐否则评测结论不具备参考价值。第二个陷阱是忽略预热效应。大模型推理引擎在接收首批请求时需要加载模型权重、初始化 KV Cache 池、编译 CUDA Kernel。前 10 个请求的延迟可能是稳态的 3 至 5 倍。如果评测未做足够的预热轮次测得的 P50 延迟会严重失真。第三个陷阱是混淆 MFU 与实际成本的关系。MFU 衡量的是计算效率但很多大模型推理场景是显存带宽受限而非计算受限。一个 MFU 只有 45% 的模型实际推理吞吐可能比 MFU 65% 但 KV Cache 占用极高的模型更高——因为后者在长 Context 场景下被显存瓶颈卡住了。评测资源效率时应该同时观察 GPU 的计算利用率和显存带宽利用率。第四个陷阱是忽略输出长度差异。Token 生成速率并非恒定受 KV Cache 增长、Attention 计算复杂度增加的影响Token 512 的生成延迟通常比 Token 1 的生成延迟高出 20% 至 40%。评测时不应只统计平均值需要看不同输出长度区间的延迟分布。五、总结大模型性能评测需要跳出看榜单选模型的思维定式。吞吐、延迟、并发和资源效率四个维度与业务准确度一起构成完整的选型决策矩阵。建议每个团队在启动大模型落地项目时构建一套标准化的性能基准流水线包含生产级 Prompt 分布、递增并发测试曲线和持续回归对比机制。评测从来不是一次性的选型仪式而是贯穿模型迭代上线全生命周期的持续工程实践。当上游发布了新版模型或推理引擎优化了调度策略那条以 CSV 文件保存的延迟-并发曲线就是做出是否升级决策的最可靠依据。