附录B 关键性能指标公式
附录B 关键性能指标公式
你无法优化你无法衡量的东西。AI Infra 工程师的核心能力之一是精确地量化系统性能。
本附录给出 AI Infra 最关键的性能指标定义、公式和 Python 计算代码。每个指标都配有实际数值示例。
B.1 训练效率指标
B.1.1 MFU(Model FLOPS Utilization)
定义:实际训练吞吐量与硬件理论峰值的比值。这是衡量训练效率最重要的指标。
import torch
def compute_mfu(
model_params: int,
batch_size: int,
seq_len: int,
step_time: float, # 秒
num_gpus: int,
gpu_flops: float, # 单 GPU 理论 FLOPS
forward_only: bool = False,
):
"""
计算 MFU(Model FLOPS Utilization)
公式:
MFU = 实际 FLOPS / 理论 FLOPS
其中:
- 实际 FLOPS = 训练一步的总 FLOP / step_time
- 训练一步的总 FLOP ≈ 6 * N * B * S (前向+反向)
- N = 参数量, B = batch size, S = 序列长度
"""
# 训练一步的计算量(3x 参数量 × tokens,前向 1x + 反向 2x)
factor = 2 if forward_only else 6 # 推理 2x,训练 6x
total_flop = factor * model_params * batch_size * seq_len
actual_flops = total_flop / step_time
theoretical_flops = num_gpus * gpu_flops
mfu = actual_flops / theoretical_flops
print(f"=== MFU 计算 ===")
print(f"参数量: {model_params / 1e9:.1f}B")
print(f"Batch: {batch_size}, Seq: {seq_len}")
print(f"Step time: {step_time:.3f}s")
print(f"总 FLOP: {total_flop / 1e12:.1f} TFLOP")
print(f"实际: {actual_flops / 1e12:.1f} TFLOPS")
print(f"理论: {theoretical_flops / 1e12:.1f} TFLOPS")
print(f"MFU: {mfu * 100:.1f}%")
return mfu
# 示例:7B 模型在 8x A100 上训练
compute_mfu(
model_params=7e9,
batch_size=8 * 8, # 8 GPU × micro-batch 8
seq_len=4096,
step_time=2.5, # 秒
num_gpus=8,
gpu_flops=312e12, # A100 BF16: 312 TFLOPS
)
# 输出: MFU ≈ 44.5%(对于 7B Dense 模型这是合理的)B.1.2 HFU(Hardware FLOPS Utilization)
定义:与 MFU 类似,但考虑了重计算(gradient checkpointing)的额外 FLOP。
def compute_hfu(
model_params: int,
batch_size: int,
seq_len: int,
step_time: float,
num_gpus: int,
gpu_flops: float,
recomputation_factor: float = 1.0, # 无重计算=1.0, 全重计算≈1.33
):
"""
HFU 考虑了重计算的额外计算开销。
公式:
HFU = (6 * N * B * S * (1 + α)) / (step_time * num_gpus * gpu_flops)
其中 α 是重计算因子:
- 无重计算:α = 0
- 部分重计算:α ≈ 0.15-0.33
- 全重计算:α ≈ 0.33
"""
total_flop = 6 * model_params * batch_size * seq_len * (1 + recomputation_factor)
actual_flops = total_flop / step_time
hfu = actual_flops / (num_gpus * gpu_flops)
print(f"HFU: {hfu * 100:.1f}%(重计算因子: {recomputation_factor})")
return hfu
# 对比 MFU 和 HFU
print("=== 无重计算 ===")
compute_hfu(7e9, 64, 4096, 2.5, 8, 312e12, 0.0)
print("\n=== 全重计算 ===")
compute_hfu(7e9, 64, 4096, 2.5, 8, 312e12, 0.33)B.1.3 训练吞吐量(Tokens/sec)
def compute_training_throughput(
batch_size: int,
seq_len: int,
step_time: float,
gradient_accumulation_steps: int = 1,
):
"""
训练吞吐量:每秒处理的 token 数
公式:
Throughput = (batch_size × seq_len × grad_accum_steps) / step_time
"""
tokens_per_step = batch_size * seq_len * gradient_accumulation_steps
throughput = tokens_per_step / step_time
print(f"Tokens/step: {tokens_per_step:,}")
print(f"Throughput: {throughput:,.0f} tokens/sec")
print(f"Throughput: {throughput / 1e6:.2f}M tokens/sec")
return throughput
# 示例
compute_training_throughput(
batch_size=64, # 全局 batch
seq_len=4096,
step_time=2.5,
)B.1.4 Wall-clock Training Time
def estimate_wall_clock_time(
total_tokens: int, # 训练总 token 数
throughput: float, # tokens/sec
overhead_factor: float = 1.1, # 额外开销(10%)
):
"""
估算训练总时间
公式:
Wall-clock = total_tokens × overhead / throughput
"""
total_seconds = total_tokens * overhead_factor / throughput
days = total_seconds / 86400
print(f"总 token 数: {total_tokens / 1e9:.0f}B")
print(f"吞吐量: {throughput / 1e6:.2f}M tokens/sec")
print(f"预估时间: {days:.1f} 天 ({total_seconds / 3600:.0f} 小时)")
return days
# 示例:训练一个 1T token 的模型
estimate_wall_clock_time(
total_tokens=1e12, # 1T tokens
throughput=200_000, # 200K tokens/sec
)
# 输出: 约 63.4 天B.2 推理性能指标
B.2.1 TTFT(Time To First Token)
import time
from dataclasses import dataclass
@dataclass
class InferenceMetrics:
ttft: float # 首 token 延迟(秒)
tpot: float # 每输出 token 延迟(秒)
total_tokens: int
input_tokens: int
def measure_inference(llm, prompt, max_tokens=256):
"""测量推理性能"""
# 模拟流式推理
start = time.time()
first_token_time = None
tokens = []
for token in llm.generate_stream(prompt, max_tokens=max_tokens):
if first_token_time is None:
first_token_time = time.time()
tokens.append(token)
end = time.time()
metrics = InferenceMetrics(
ttft=first_token_time - start,
tpot=(end - first_token_time) / (len(tokens) - 1) if len(tokens) > 1 else 0,
total_tokens=len(tokens),
input_tokens=len(prompt.split()),
)
print(f"=== 推理性能 ===")
print(f"TTFT: {metrics.ttot * 1000:.1f}ms")
print(f"TPOT: {metrics.tpot * 1000:.1f}ms")
print(f"总延迟: {(end - start) * 1000:.1f}ms")
print(f"输出 tokens: {metrics.total_tokens}")
return metrics
# 目标值参考
ttft_targets = {
"实时对话": 0.2, # 200ms
"交互式应用": 0.5, # 500ms
"后台任务": 5.0, # 5s
"批处理": 30.0, # 30s
}B.2.2 TPOT(Time Per Output Token)
def compute_tpot(total_output_time, num_output_tokens):
"""
TPOT = 总输出时间 / 输出 token 数
注意:总输出时间 = 总时间 - TTFT
"""
tpot = total_output_time / max(num_output_tokens - 1, 1)
print(f"TPOT: {tpot * 1000:.1f}ms/token")
print(f"生成速度: {1 / tpot:.1f} tokens/sec")
return tpot
# TPOT 目标值
tpot_targets = {
"人类阅读速度": 0.015, # ~65 tokens/sec
"实时交互": 0.05, # 20 tokens/sec
"可接受延迟": 0.1, # 10 tokens/sec
}B.2.3 推理吞吐量(Throughput)
def compute_inference_throughput(
num_requests: int,
avg_output_tokens: int,
total_time: float,
):
"""
推理吞吐量:每秒生成的 token 总数
公式:
Throughput = (num_requests × avg_output_tokens) / total_time
"""
total_tokens = num_requests * avg_output_tokens
throughput = total_tokens / total_time
print(f"=== 推理吞吐量 ===")
print(f"请求数: {num_requests}")
print(f"总输出 tokens: {total_tokens:,}")
print(f"总时间: {total_time:.1f}s")
print(f"吞吐量: {throughput:,.0f} tokens/sec")
return throughput
# 对比不同 batch size 下的吞吐量
print("--- Batch Size = 1 ---")
compute_inference_throughput(1, 256, 12.8)
print("\n--- Batch Size = 32 ---")
compute_inference_throughput(32, 256, 30.5)B.2.4 Goodput
定义:满足 SLO 要求的请求吞吐量。这是真正有业务意义的指标。
def compute_goodput(
requests: list, # 每个请求的延迟记录
slo_ttft: float, # TTFT SLO(秒)
slo_e2e: float, # 端到端延迟 SLO(秒)
total_time: float,
):
"""
Goodput = 满足 SLO 的请求数 / 总时间
只有同时满足 TTFT 和 端到端延迟 的请求才计入。
"""
good_requests = [
r for r in requests
if r['ttft'] <= slo_ttft and r['e2e_latency'] <= slo_e2e
]
goodput = len(good_requests) / total_time
total_throughput = len(requests) / total_time
slo_compliance = len(good_requests) / len(requests)
print(f"=== Goodput ===")
print(f"总请求数: {len(requests)}")
print(f"满足 SLO: {len(good_requests)} ({slo_compliance * 100:.1f}%)")
print(f"总吞吐量: {total_throughput:.1f} req/s")
print(f"Goodput: {goodput:.1f} req/s")
print(f"SLO 目标: TTFT < {slo_ttft*1000:.0f}ms, E2E < {slo_e2e:.1f}s")
return goodputB.2.5 端到端延迟分解
def breakdown_inference_latency(
model_params: int,
input_tokens: int,
output_tokens: int,
batch_size: int,
gpu_memory_bandwidth: float, # bytes/sec
gpu_flops: float,
num_gpus: int,
):
"""
端到端延迟的近似分解:
Total = Prefill 时间 + Decode 时间 + 通信开销
Prefill(预填充):处理输入 prompt,计算密集型
Decode(解码):逐 token 生成,内存密集型
"""
# 简化估算
bytes_per_param = 2 # FP16
# Prefill: ~6 * N * input_tokens / (num_gpus * gpu_flops)
prefill_flops = 2 * model_params * input_tokens # 前向 only
prefill_time = prefill_flops / (num_gpus * gpu_flops)
# Decode: 逐 token,主要受内存带宽限制
# 每步需要读取全部权重 + KV Cache
weight_bytes = model_params * bytes_per_param
decode_per_token = weight_bytes / (num_gpus * gpu_memory_bandwidth)
decode_time = decode_per_token * output_tokens
# KV Cache 读写
kv_cache_per_token = 2 * 32 * 32 * 128 * 2 # layers × heads × head_dim × fp16
kv_total = kv_cache_per_token * (input_tokens + output_tokens) * batch_size
kv_time = kv_total / (num_gpus * gpu_memory_bandwidth) * output_tokens
total = prefill_time + decode_time + kv_time
print(f"=== 延迟分解(近似)===")
print(f"Prefill: {prefill_time * 1000:.1f}ms")
print(f"Decode ({output_tokens} tokens): {decode_time * 1000:.1f}ms")
print(f" - 每步: {decode_per_token * 1000:.1f}ms")
print(f"KV Cache 访问: {kv_time * 1000:.1f}ms")
print(f"总计: {total * 1000:.1f}ms")
return total
# 示例:7B 模型在单 A100 上
breakdown_inference_latency(
model_params=7e9,
input_tokens=512,
output_tokens=256,
batch_size=1,
gpu_memory_bandwidth=2e12, # A100: 2TB/s
gpu_flops=312e12,
num_gpus=1,
)B.3 集群利用率指标
B.3.1 GPU 利用率
def compute_gpu_utilization(
busy_seconds: float, # GPU 执行 kernel 的时间
total_seconds: float, # 总观察时间
):
"""
GPU 利用率 = GPU 执行 kernel 的时间 / 总时间
注意:nvidia-smi 显示的 utilization 反映的是"有 kernel 在执行"的
时间百分比,而不是 FLOPS 利用率。GPU 利用率 100% 不等于 MFU 100%。
"""
utilization = busy_seconds / total_seconds
print(f"GPU 利用率: {utilization * 100:.1f}%")
print(f"⚠️ 注意:GPU 利用率 ≠ MFU")
print(f" GPU 利用率 100% 可能 MFU 只有 40%")
return utilization
# 从 nvidia-smi 获取
# nvidia-smi --query-gpu=utilization.gpu,memory.used --format=csv -l 1B.3.2 集群空闲率与排队延迟
def compute_cluster_metrics(
gpu_hours_total: float, # 总 GPU 小时
gpu_hours_used: float, # 实际使用 GPU 小时
gpu_hours_idle: float, # 空闲 GPU 小时
gpu_hours_queue: float, # 排队等待 GPU 小时
):
"""
集群利用率指标
"""
utilization = gpu_hours_used / gpu_hours_total
idle_rate = gpu_hours_idle / gpu_hours_total
queue_rate = gpu_hours_queue / gpu_hours_total
wasted_rate = idle_rate # 空闲 = 浪费
print(f"=== 集群利用率 ===")
print(f"总 GPU 小时: {gpu_hours_total:,.0f}")
print(f"使用率: {utilization * 100:.1f}%")
print(f"空闲率: {idle_rate * 100:.1f}%")
print(f"排队占比: {queue_rate * 100:.1f}%")
# 健康度评估
if utilization > 0.85:
print("✅ 使用率高,考虑扩容")
elif utilization > 0.6:
print("✅ 使用率健康")
elif utilization > 0.3:
print("⚠️ 使用率偏低,考虑优化调度")
else:
print("❌ 使用率过低,资源浪费")
return utilization
# 月度集群统计
compute_cluster_metrics(
gpu_hours_total=8 * 24 * 30, # 8 GPU × 30 天
gpu_hours_used=3850,
gpu_hours_idle=1250,
gpu_hours_queue=240,
)B.4 成本指标
B.4.1 每千 Token 成本($/1M tokens)
def compute_cost_per_token(
# 硬件成本
gpu_hourly_cost: float, # $/GPU/hour
num_gpus: int,
# 性能数据
throughput: float, # tokens/sec
# 额外成本
overhead_factor: float = 1.3, # 电力+冷却+网络等(30%)
):
"""
计算每百万 token 的推理成本
公式:
Cost_per_1M_tokens = (gpu_cost_per_hour × overhead) / (throughput × 3600) × 1e6
"""
gpu_cost_per_hour = gpu_hourly_cost * num_gpus
total_cost_per_hour = gpu_cost_per_hour * overhead_factor
tokens_per_hour = throughput * 3600
cost_per_1m = total_cost_per_hour / tokens_per_hour * 1e6
print(f"=== 推理成本 ===")
print(f"硬件成本: ${gpu_cost_per_hour:.2f}/hour")
print(f"总成本(含开销): ${total_cost_per_hour:.2f}/hour")
print(f"吞吐量: {throughput:,.0f} tokens/sec ({throughput * 3600 / 1e6:.1f}M/hour)")
print(f"成本: ${cost_per_1m:.4f} / 1M tokens")
return cost_per_1m
# 示例对比
print("=== A100 推理 7B 模型 ===")
compute_cost_per_token(
gpu_hourly_cost=2.5, # A100 按需约 $2.5/hour
num_gpus=1,
throughput=3000, # ~3000 tokens/sec
)
print("\n=== H100 推理 70B 模型 ===")
compute_cost_per_token(
gpu_hourly_cost=4.0, # H100 按需约 $4/hour
num_gpus=4,
throughput=1500,
)B.4.2 每 FLOP 成本($/FLOP for Training)
def compute_training_cost(
gpu_hourly_cost: float,
num_gpus: int,
training_days: float,
model_params: int,
training_tokens: int,
mfu: float,
gpu_flops: float,
):
"""
训练成本分析
"""
total_hours = training_days * 24
total_cost = gpu_hourly_cost * num_gpus * total_hours
# 理论 FLOP
theoretical_flops = gpu_flops * num_gpus * total_hours * 3600
actual_flops = theoretical_flops * mfu
# 每 token 成本
cost_per_token = total_cost / training_tokens
print(f"=== 训练成本 ===")
print(f"GPU: {num_gpus} × ${gpu_hourly_cost}/hour × {training_days}天")
print(f"总成本: ${total_cost:,.0f}")
print(f"训练 tokens: {training_tokens / 1e9:.1f}B")
print(f"MFU: {mfu * 100:.1f}%")
print(f"实际 FLOP: {actual_flops / 1e21:.2f} ZFLOP")
print(f"成本/token: ${cost_per_token:.2e}")
return total_cost
# 示例:训练 70B 模型
compute_training_cost(
gpu_hourly_cost=2.5, # A100
num_gpus=256,
training_days=30,
model_params=70e9,
training_tokens=1.5e12, # 1.5T tokens
mfu=0.45,
gpu_flops=312e12,
)
# 总成本约 $460,800B.4.3 TCO(Total Cost of Ownership)
def compute_tco(
# 硬件采购
gpu_unit_price: float, # 单 GPU 采购价
num_gpus: int,
server_unit_price: float, # 每台服务器(不含 GPU)
num_servers: int,
network_cost: float, # 网络设备总价
# 数据中心
power_per_gpu: float, # W
pue: float, # 数据中心 PUE
electricity_rate: float, # $/kWh
# 运营
years: float, # 使用年限
ops_team_cost: float, # 运维团队年成本
facility_cost: float, # 机房租赁/建设年成本
):
"""
GPU 集群的 TCO(总拥有成本)
"""
# 资本支出(CapEx)
gpu_capex = gpu_unit_price * num_gpus
server_capex = server_unit_price * num_servers
capex = gpu_capex + server_capex + network_cost
# 运营支出(OpEx)
total_power_kw = (power_per_gpu * num_gpus / 1000) * pue
annual_electricity = total_power_kw * 24 * 365 * electricity_rate
opex_per_year = annual_electricity + ops_team_cost + facility_cost
total_opex = opex_per_year * years
# 折旧后的硬件残值(假设 3 年后 20%)
residual = capex * 0.2
tco = capex + total_opex - residual
# 单 GPU 月成本
monthly_cost_per_gpu = tco / (years * 12) / num_gpus
print(f"=== TCO 分析({years}年)===")
print(f"\nCapEx(资本支出):")
print(f" GPU: ${gpu_capex:,.0f}")
print(f" 服务器: ${server_capex:,.0f}")
print(f" 网络: ${network_cost:,.0f}")
print(f" 小计: ${capex:,.0f}")
print(f"\nOpEx(运营支出/年):")
print(f" 电费: ${annual_electricity:,.0f}")
print(f" 运维: ${ops_team_cost:,.0f}")
print(f" 机房: ${facility_cost:,.0f}")
print(f" 小计: ${opex_per_year:,.0f}/年")
print(f"\n残值: ${residual:,.0f}")
print(f"\nTCO({years}年): ${tco:,.0f}")
print(f"每 GPU 月成本: ${monthly_cost_per_gpu:,.0f}")
print(f"每 GPU 小时成本: ${monthly_cost_per_gpu / (30*24):.2f}")
return tco
# 示例:256 GPU 集群 3 年 TCO
compute_tco(
gpu_unit_price=30_000, # H100
num_gpus=256,
server_unit_price=50_000, # 每台 8-GPU 服务器
num_servers=32,
network_cost=500_000, # IB 交换机 + 线缆
power_per_gpu=700, # H100 SXM 700W
pue=1.2, # 液冷数据中心
electricity_rate=0.08, # 工业电价
years=3,
ops_team_cost=500_000,
facility_cost=300_000,
)
Tip成本优化经验法则
- 推理成本主要由首 token 延迟目标决定——越低的 TTFT 要求越高的 GPU 配比。
- 训练成本与 MFU 直接挂钩——MFU 从 40% 提升到 50%,训练成本降 20%。
- 自建 vs 云:GPU 利用率 > 70% 时自建更划算,< 40% 时用云更便宜。
- 折旧周期:GPU 性能每 2 年翻倍,建议按 3 年折旧计算,而非传统的 4-5 年。
小结
本附录的核心公式汇总:
| 指标 | 公式 | 用途 |
|---|---|---|
| MFU | 6NS / (T × GPU × FLOPS) | 训练效率 |
| HFU | 6NS(1+α) / (T × GPU × FLOPS) | 含重计算的训练效率 |
| 训练吞吐量 | B×S / T | tokens/sec |
| TTFT | 首 token 生成时间 | 用户体验 |
| TPOT | (总时间-TTFT) / (N-1) | 解码速度 |
| Goodput | 满足 SLO 请求数 / 总时间 | 业务有效吞吐 |
| 成本/1M token | $/hour / (TPS × 3600) × 1e6 | 推理经济性 |
| TCO | CapEx + OpEx × Years - Residual | 总拥有成本 |
记住一点:指标本身没有意义,与目标值的对比和趋势变化才有意义。 建立你的指标基线,持续追踪。
延伸阅读
- PaLM 训练论文(MFU 的经典应用案例)
- LLM-Perf benchmark: https://github.com/ray-project/llmperf
- MLPerf: https://mlcommons.org/benchmarks/
- vLLM benchmark suite: 内置的性能测试工具
- NVIDIA Nsight 文档: 性能分析的标准工具