跳到主要内容

评分方法论

本文档定义 Tier6+Model 平台的部署方案评分体系,用于对 LLM 推理部署方案进行多维度量化评估。

行业参考与定位

现有行业基准

MLPerf Inference

MLPerf 是目前 LLM 推理领域最权威的基准测试。其核心思路是:在 P99 延迟约束(SLO)下最大化 QPS(Queries Per Second)。评测分为 Offline(纯吞吐)和 Server(延迟约束下吞吐)两个场景,不产生单一综合评分。

Goodput (有效吞吐)

学术界(如 Distserve、Splitwise 等论文)提出 Goodput 概念:只计入满足 SLO 的请求吞吐。一个方案即使总 QPS 很高,但大量请求超时,则 Goodput 很低。这体现了"延迟约束优先"的思想。

Roofline 模型

Roofline 模型将工作负载按算术强度(FLOPs/Byte)分为计算密集型和访存密集型。对于 LLM 推理:

  • Prefill 阶段:大矩阵乘法,算术强度高,属于计算密集型 -> MFU 是关键指标
  • Decode 阶段:逐 token 生成,每次读取大量 KV Cache,属于访存密集型 -> MBU 是关键指标

这是理解效率评分设计的基础。

为什么没有通用标准

LLM 推理部署没有单一的"最优评分标准",原因如下:

  1. 场景差异大:实时对话要求低延迟(TTFT < 200ms),批量处理只关心吞吐,成本敏感场景优先看 $/TPS
  2. 指标之间存在本质矛盾:增大 batch size 提升吞吐但牺牲延迟,增加 TP 降低延迟但降低芯片效率
  3. 硬件差异:不同芯片的计算/访存/通信能力比例不同,"好方案"的标准因硬件而异

本系统定位

Tier6+Model 是一个部署方案探索与对比工具,评分体系的目标是:

  • 提供 0-100 的归一化分数,便于方案之间快速比较
  • 多维度评估,避免单一指标掩盖短板
  • 使用几何平均惩罚极端短板,引导用户关注瓶颈
  • SLO 参数可配置,适配不同业务场景

推荐评分体系

六维度评分

删除原有的 balance_score(原因见第 3 节),新增 cost_score(基于 DFOP)。六个维度,每个输出 0-100 分。

维度 1: 延迟 (Latency Score)

输入: TTFT (ms), TPOT (ms), SLO 配置

设计思想: 采用 SLO 约束式评分。满足 SLO 目标即可获得高分,超过 SLO 后分数快速衰减。这与 MLPerf Server 场景和 Goodput 的思想一致。

公式:

ttft_score = 100 * min(1, SLO_TTFT / actual_TTFT)
tpot_score = 100 * min(1, SLO_TPOT / actual_TPOT)
latency_score = ttft_score * 0.5 + tpot_score * 0.5

默认 SLO 参数(可配置):

场景SLO_TTFT (ms)SLO_TPOT (ms)
实时对话20050
在线服务500100
批量处理2000200

得分示例(实时对话场景):

  • TTFT=100ms, TPOT=30ms -> min(1, 200/100)*50 + min(1, 50/30)*50 = 50 + 50 = 100
  • TTFT=400ms, TPOT=30ms -> min(1, 200/400)*50 + min(1, 50/30)*50 = 25 + 50 = 75
  • TTFT=100ms, TPOT=100ms -> min(1, 200/100)*50 + min(1, 50/100)*50 = 50 + 25 = 75

边界处理: 当 actual_latency <= 0 时,使用 epsilon (1e-6) 替代,避免除零。

维度 2: 吞吐 (Throughput Score)

输入: TPS_per_chip, 模型参数量 (B)

设计思想: 使用 TPS_per_chip 而非总 TPS,因为它反映了单芯片的成本效益。归一化基准根据模型大小调整,因为大模型天然单芯片吞吐低。

公式:

ratio = tps_per_chip / baseline_tps
throughput_score = min(100, ratio * 100)

基准 TPS/Chip(按模型大小):

模型大小baseline_tps依据
< 10B100小模型单芯片可承载,吞吐应较高
10B - 100B30中等模型,典型 TP=2-8
100B - 500B10大模型,典型 TP=8-16
> 500B3超大模型 (MoE),TP+EP 分布

边界处理: 得分下限为 0,上限为 100。tps_per_chip <= 0 时得 0 分。

维度 3: 效率 (Efficiency Score)

输入: MFU (0-1), MBU (0-1)

设计思想: 基于 Roofline 模型,Prefill 是计算密集型看 MFU,Decode 是访存密集型看 MBU。一个好的部署方案应该在各自的瓶颈侧达到高利用率。因此取两者的最大值,反映"瓶颈资源是否被充分利用"。

数据来源: MFU 取自 Prefill 阶段(计算密集),MBU 取自 Decode 阶段(访存密集)。两者已分别对应各自阶段的瓶颈资源,因此 max 的含义是"两个阶段中更高效的那个"。若某阶段效率低,其影响会通过延迟维度(TTFT/TPOT)间接体现,无需效率维度重复惩罚。

公式:

efficiency_score = max(MFU, MBU) * 100

为什么用 max 而不是加权平均:

  • 加权平均 MFU*0.6 + MBU*0.4 物理上没有意义:Prefill 的 MBU 低和 Decode 的 MFU 低都是正常的,加权只会拉低分数
  • max(MFU, MBU) 的含义是:至少有一个阶段的瓶颈资源利用率达到了该水平
  • 如果 MFU 和 MBU 都低,说明计算和访存都没打满,方案确实低效

得分示例:

  • MFU=0.7, MBU=0.3 (典型 Prefill 密集) -> max(0.7, 0.3) * 100 = 70
  • MFU=0.2, MBU=0.8 (典型 Decode 密集) -> max(0.2, 0.8) * 100 = 80
  • MFU=0.1, MBU=0.1 (低效方案) -> max(0.1, 0.1) * 100 = 10

维度 4: 内存 (Memory Score)

输入: memory_used_gb, memory_capacity_gb

设计思想: 内存利用率存在一个"甜区"。太低说明芯片容量浪费(可以用更小/更少的芯片),太高则接近 OOM 风险,缺乏弹性(无法增大 batch size 或增加 KV Cache 长度)。

公式(分段函数):

utilization = memory_used_gb / memory_capacity_gb

if utilization <= 0.6:
score = 60 + utilization / 0.6 * 30 # [60, 90] 利用不足但可接受
elif utilization <= 0.85:
score = 90 + (utilization - 0.6) / 0.25 * 10 # [90, 100] 最优区间
elif utilization <= 0.95:
score = 100 - (utilization - 0.85) / 0.10 * 60 # [100, 40] 开始紧张,快速下降
else:
score = max(5, 40 - (utilization - 0.95) / 0.05 * 35) # [40, 5] 危险区域

设计要点:

  • 最优区间 60%-85%:留有足够余量用于 KV Cache 增长和 batch size 调整
  • 超过 85% 快速衰减:接近 OOM 是严重风险
  • 低于 60% 也给出较高分(60+):内存富余不是严重问题,只是不够经济
  • 最低分保底 5 分:避免几何平均中出现接近 0 的值

边界处理: memory_capacity_gb <= 0 时返回 0 分。

维度 5: 通信 (Communication Score)

输入: 通信时间, 计算时间(Prefill + Decode 阶段)

设计思想: 通信开销是并行扩展的核心代价。通信占比越低,说明并行策略越高效。

公式:

comm_ratio = total_comm_time / (total_comm_time + total_compute_time)
communication_score = max(10, (1 - comm_ratio) * 100)

其中:

total_comm_time = prefill_comm_time + decode_comm_time
total_compute_time = prefill_compute_time + decode_compute_time

得分示例:

  • 通信占比 5% -> 95 分(优秀,通信几乎可忽略)
  • 通信占比 20% -> 80 分(良好,TP=8 典型值)
  • 通信占比 50% -> 50 分(较差,通信成为瓶颈)
  • 通信占比 80% -> 20 分(极差,芯片大量空闲等待通信)

数据来源说明: comm_time 和 compute_time 来自后端仿真器的累积值。默认情况下两者是独立累加的(无 overlap)。若启用了 TBO 或 Ring Attention overlap,则 comm_time 已是 overlap 后的残留值。因此通信评分始终反映"实际暴露在外的通信开销占比"。

边界处理: 总时间 <= 0 时返回 0 分(无数据)。最低分 10 分。

维度 6: 成本效率 (Cost Score) — DFOP

输入: DFOP ($/TPS), 模型参数量 (B)

设计思想: DFOP (Dollar per TPS) 是唯一将经济成本纳入评分的维度。它衡量"产生 1 TPS 吞吐能力需要多少美元投入",越低越好。DFOP 已在后端 cost_analysis.py 中计算:dfop = total_cost / tps

与 Throughput 维度的区别:Throughput 是纯技术视角(TPS/chip -> 芯片利用得好不好),DFOP 是经济视角($/TPS -> 花的钱值不值)。同样的 TPS/chip,用 $2,500 的 SG2262 和 $6,300 的 B200 达成,DFOP 差距很大。

公式:

dfop_score = 100 * min(1, baseline_dfop / actual_dfop)

基准 DFOP(按模型大小,基于典型部署成本和期望吞吐推导):

模型大小baseline_dfop ($/TPS)推导依据
< 10B50单芯片 ~$3,000, 期望 TPS ~60
10B - 100B5004-8 芯片 ~$15k, 期望 TPS ~30
100B - 500B5,00016-32 芯片 ~$60k, 期望 TPS ~12
> 500B20,00064+ 芯片 ~$160k+, 期望 TPS ~8

得分示例(100-500B 模型,baseline = $5,000/TPS):

  • DFOP=$3,000 -> min(1, 5000/3000)*100 = 100(优于基准,满分)
  • DFOP=$5,000 -> min(1, 5000/5000)*100 = 100(达到基准)
  • DFOP=$10,000 -> min(1, 5000/10000)*100 = 50(成本偏高)
  • DFOP=$25,000 -> min(1, 5000/25000)*100 = 20(成本过高)

与 Throughput 的相关性说明: DFOP 和 Throughput 都包含 TPS,存在部分相关性。在几何平均中,一个 TPS 极低的方案会在两个维度都受惩罚。这是合理的 -- 既低效又不划算,应该双重体现。若用户认为惩罚过重,可降低其中一个维度的权重。

边界处理: actual_dfop <= 0 时返回 0 分(TPS 为 0 导致 DFOP 无法计算)。最低保底 5 分。

总分计算: 加权几何平均

公式:

overall = (latency^w1 * throughput^w2 * efficiency^w3 * memory^w4 * communication^w5 * cost^w6) ^ (1 / (w1+w2+w3+w4+w5+w6))

默认权重:

维度权重说明
latency0.20用户体验直接感知
throughput0.20单芯片吞吐能力
efficiency0.20硬件利用率
memory0.15可行性约束
communication0.10扩展效率
cost (DFOP)0.15部署成本效率

为什么用几何平均替代加权算术平均:

加权算术平均的问题:一个维度极差可以被其他维度"补偿"。例如 latency=10, throughput=90, efficiency=90, memory=90, communication=90,算术平均约 74 分,看起来"还行"。

几何平均的优势:

  • 短板效应: 任何一个维度的极低分都会显著拉低总分
  • 乘法性质: 各维度之间是"与"的关系 -- 一个好的方案需要各方面都合格
  • 物理直觉: 类似于木桶效应,部署方案的整体质量受限于最差的维度

计算示例:

方案 A: latency=80, throughput=80, efficiency=75, memory=85, communication=90, cost=80
算术: 80*0.20 + 80*0.20 + 75*0.20 + 85*0.15 + 90*0.10 + 80*0.15 = 80.75
几何: exp(0.20*ln80 + 0.20*ln80 + 0.20*ln75 + 0.15*ln85 + 0.10*ln90 + 0.15*ln80) = 80.5
-> 均衡方案,两种方法差异不大

方案 B: latency=10, throughput=95, efficiency=90, memory=90, communication=95, cost=85
算术: 10*0.20 + 95*0.20 + 90*0.20 + 90*0.15 + 95*0.10 + 85*0.15 = 71.75
几何: exp(0.20*ln10 + 0.20*ln95 + 0.20*ln90 + 0.15*ln90 + 0.10*ln95 + 0.15*ln85) = 55.5
-> 延迟极差的方案,几何平均严厉惩罚到 56 分

避免 log(0) 问题: 几何平均等价于 exp(sum(wi * ln(si)) / sum(wi))。当任意 si = 0ln(0) 无定义。实现时对所有维度分数取下限 epsilon:

safe_score = max(score, EPSILON)    # EPSILON = 1e-3

这保证了即使某维度接近 0 分,总分也会被拉到极低值(而非产生数学错误)。

用 cost_score (DFOP) 替代 balance_score 的理由

balance_score 当前实现的问题

现有的 balance_score 惩罚 MFU 和 MBU 之间的差距:

// 现有代码 (scoreCalculator.ts)
const diff = Math.abs(mfu - mbu)
const score = 100 - diff * 100

为什么这在 LLM 推理中没有意义

Prefill 和 Decode 有本质不同的计算特征:

  • Prefill 阶段: 处理整个 prompt,是大规模矩阵乘法,算术强度高。此时 MFU 高、MBU 低是正常且理想的状态
  • Decode 阶段: 逐 token 生成,每次只做一个小矩阵运算但需读取大量 KV Cache。此时 MBU 高、MFU 低是正常且理想的状态

Roofline 模型告诉我们:一个工作负载要么是计算瓶颈,要么是访存瓶颈,两者同时打满只发生在 Roofline 的拐点处,而大多数实际负载不在拐点上。

惩罚 MFU-MBU 差距的后果: 一个 Prefill MFU=0.8、Decode MBU=0.85 的方案,在各自的瓶颈维度上都表现优秀。但如果混合来看 |MFU-MBU| 很大(因为 Prefill 的 MBU 低、Decode 的 MFU 低),balance_score 会给出低分。这与实际情况相矛盾。

替代方案: 用 DFOP 成本维度填补

效率维度已经用 max(MFU, MBU) 来捕获"瓶颈侧利用率"。如果 MFU 和 MBU 都低,效率分自然就低 -- 不需要额外的"均衡性"维度。

删除 balance_score 后空出的位置由 cost_score (DFOP) 填入,理由:

  1. 填补成本盲区: 原五维度全是技术指标,没有经济性考量。对于部署方案探索工具,成本效率是核心关切
  2. DFOP 数据已就绪: 后端 cost_analysis.py 已计算 DFOP,前端已展示,只需接入评分
  3. 维持六维雷达图: 替换而非删除,保持雷达图的六角形对称性

实现注意事项

分数范围保证

所有维度分数必须在 [0, 100] 范围内。实现时每个计算函数结果都做 clamp:

function clamp(value: number, min: number = 0, max: number = 100): number {
return Math.max(min, Math.min(max, value))
}

避免除零和 log(0)

场景处理方式
actual_latency <= 0使用 epsilon = 1e-6 替代
memory_capacity <= 0返回 0 分
total_time <= 0返回 0 分(无数据)
actual_dfop <= 0返回 0 分(TPS 为 0 导致 DFOP 无法计算)
几何平均中 score = 0使用 epsilon = 1e-3 替代

SLO 参数可配置

SLO 参数应作为评分函数的可选参数传入,而非硬编码:

interface SLOConfig {
ttft_ms: number // TTFT SLO 目标值 (默认 200ms)
tpot_ms: number // TPOT SLO 目标值 (默认 50ms)
}

interface ThroughputBaseline {
small: number // <10B 模型基准 TPS/Chip (默认 100)
medium: number // 10-100B 基准 (默认 30)
large: number // 100-500B 基准 (默认 10)
xlarge: number // >500B 基准 (默认 3)
}

interface DfopBaseline {
small: number // <10B 模型基准 $/TPS (默认 50)
medium: number // 10-100B 基准 (默认 500)
large: number // 100-500B 基准 (默认 5000)
xlarge: number // >500B 基准 (默认 20000)
}

interface ScoringWeights {
latency: number // 默认 0.20
throughput: number // 默认 0.20
efficiency: number // 默认 0.20
memory: number // 默认 0.15
communication: number // 默认 0.10
cost: number // 默认 0.15
}

后端评分同步

当前后端 score = tps(直接使用 TPS 作为得分),应改为调用统一的评分逻辑,使前后端评分一致。两种实现路径:

  • 方案 A: 后端用 Python 实现相同的评分算法,存入数据库
  • 方案 B: 后端只存原始指标,前端负责所有评分计算(当前架构更接近此方案)

推荐方案 B,理由是:评分权重和 SLO 配置是用户可调的前端参数,前端统一计算更灵活。后端数据库中的 score 字段可保留用于默认排序(使用 TPS 或前端回写的综合分)。

涉及的代码文件

文件当前状态改动方向
frontend/src/utils/llmDeployment/scoreCalculator.ts六维评分 + 加权算术平均替换 balance -> cost(DFOP) + 几何平均
backend/perf_model/L5_reporting/cost_analysis.py已计算 DFOP无需改动,数据已就绪
backend/perf_model/L0_entry/api/_helpers.pyscore = tps保持或改为前端回写

评分体系总结

+------------------+------------------------------------------+--------+
| 维度 | 核心公式 | 权重 |
+------------------+------------------------------------------+--------+
| Latency | 100 * min(1, SLO / actual), TTFT+TPOT | 0.20 |
| Throughput | min(100, tps_per_chip / baseline * 100) | 0.20 |
| Efficiency | max(MFU, MBU) * 100 | 0.20 |
| Memory | 分段函数, 60-85% 最优 | 0.15 |
| Communication | max(10, (1 - comm_ratio) * 100) | 0.10 |
| Cost (DFOP) | 100 * min(1, baseline_dfop / actual_dfop)| 0.15 |
+------------------+------------------------------------------+--------+
| Overall | 加权几何平均 (短板惩罚) | -- |
+------------------+------------------------------------------+--------+