共计 2192 个字符,预计需要花费 6 分钟才能阅读完成。
问题诊断:自回归生成的延迟本质
当用户抱怨 ”ChatGPT 太慢了 ” 时,实际存在两种不同的延迟:

- 真实计算延迟 :模型执行 forward pass 的计算时间,与硬件性能强相关。以 A100 GPU 为例,单个 token 生成约需 15-30ms
- 用户感知延迟 :从发送请求到接收完整响应的时间,受序列长度影响极大。生成 100 个 token 的对话,即使每个 token 仅需 20ms,用户仍需等待 2 秒
关键瓶颈在于 Transformer 的自回归机制——每个新 token 的生成都依赖之前所有 token。用计算图表示如下:
[Input] → [Token_1] → [Token_2] → ... → [Token_N]
↓ ↓ ↓ ↓
[Time] [Time+Δt] [Time+2Δt] [Time+NΔt]
优化方案对比
流式传输 (SSE) vs 长轮询
- SSE 优势 :
- 单次 TCP 连接持续传输
- 每个 token 到达立即推送
-
示例 Header:
headers = { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' } -
长轮询缺陷 :
- 每个请求独立建立连接
- TLS 握手增加 200-300ms 开销
- 需要预测合理超时时间
KV Cache 复用实战
from transformers import AutoModelForCausalLM
import torch
class CachedGenerator:
def __init__(self, model_name: str):
self.model = AutoModelForCausalLM.from_pretrained(model_name)
self.past_key_values = None # 类型提示: Optional[Tuple[torch.Tensor]]
def generate(self, input_ids: torch.Tensor) -> torch.Tensor:
outputs = self.model(
input_ids,
past_key_values=self.past_key_values,
use_cache=True # 启用 KV 缓存
)
self.past_key_values = outputs.past_key_values
return outputs.logits
8-bit 量化效果
| 精度 | 显存占用 (MB) | P99 延迟 (ms) |
|---|---|---|
| FP16 | 15872 | 210 |
| INT8 | 7936 | 185 |
| 4-bit | 3968 | 230 |
实战示例:Flask 流式中间件
from flask import Response, stream_with_context
import asyncio
from functools import wraps
# 内存监控装饰器
def profile(func):
@wraps(func)
async def wrapper(*args, **kwargs):
import tracemalloc
tracemalloc.start()
result = await func(*args, **kwargs)
print(f"Memory peak: {tracemalloc.get_traced_memory()[1]/1024:.2f} KB")
tracemalloc.stop()
return result
return wrapper
@app.route('/stream')
@profile
async def stream_response():
async def generate():
try:
for token in await async_generator(
timeout=10, # 熔断阈值
max_tokens=200
):
yield f"data: {token}\n\n"
await asyncio.sleep(0) # 释放事件循环
except asyncio.TimeoutError:
yield "event: timeout\ndata: {}"
return Response(stream_with_context(generate()),
mimetype='text/event-stream'
)
生产环境建议
降级方案触发条件
# Prometheus 告警规则
alert: HighLLMLatency
expr: histogram_quantile(0.99, rate(llm_request_duration_seconds_bucket[1m])) > 2
for: 2m
状态保持策略
- 热缓存:最近 5 分钟对话保持 KV Cache
- 温启动:超过 5 分钟但小于 1 小时的对话保留 embedding
- 冷启动:超过 1 小时完整重新初始化
延伸实验建议
通过 ab 测试验证 max_token 的影响:
# 测试不同 max_token 的吞吐量
for n in 50 100 200; do
ab -c 10 -n 100 "http://api/chat?max_tokens=$n" | grep "Requests per second"
done
关键发现:当 max_token 从 50 增加到 200 时,QPS 通常下降 40-60%,但平均响应时间仅增加 30%。这说明更长的序列会显著降低系统整体吞吐能力。
最终建议根据业务场景在响应速度和内容长度间寻找平衡点,例如:
– 客服场景:优先低延迟,设置 max_token=80
– 创作场景:接受较高延迟,设置 max_token=300
正文完
发表至: 人工智能工程
近两天内
