10.人工智能实战:大模型系统如何做全链路性能优化?从请求进入到 GPU 推理的端到端瓶颈分析与落地方案

人工智能实战:大模型系统如何做全链路性能优化?从请求进入到 GPU 推理的端到端瓶颈分析与落地方案


一、问题场景:单点都优化了,为什么系统还是不够快?

做到第10篇时,我们已经完成了很多优化:

text 复制代码
1. 模型启动时加载,避免重复初始化
2. vLLM 提升推理吞吐
3. Redis 队列削峰填谷
4. 多 GPU 扩展
5. 限流、熔断、降级
6. Prometheus 监控
7. Kubernetes GPU 部署

看起来每一层都已经做了优化。

但上线压测时,仍然出现一个让人头疼的问题:

text 复制代码
单独看每个模块都不慢;
但用户端到端响应就是不够快。

接口日志显示:

text 复制代码
Gateway 耗时:20ms
队列入队:5ms
Worker 处理:正常
vLLM 推理:1.2s
总耗时:却经常达到 4s ~ 8s

这说明一个很关键的问题:

text 复制代码
系统慢,不一定是某一段慢,而是全链路累积慢。

很多大模型系统的性能问题,不是靠"调一个参数"能解决的,而是需要从请求进入系统开始,逐段拆解。

这篇文章就围绕一个核心目标:

text 复制代码
如何做大模型系统全链路性能优化。

二、真实问题:为什么"局部优化"解决不了整体慢?

传统优化经常是这样:

text 复制代码
接口慢 → 优化接口
模型慢 → 换 vLLM
GPU 满 → 加 GPU
队列堵 → 加 Worker

这些动作都可能有效,但也可能制造新问题。

例如:

text 复制代码
增加 Worker

可能导致:

text 复制代码
更多请求同时打到 vLLM
GPU 抢占更严重
P99 反而升高

又比如:

text 复制代码
加大队列长度

可能导致:

text 复制代码
请求不失败了
但用户等得更久

所以全链路性能优化的第一原则是:

text 复制代码
先拆链路,再优化瓶颈。

三、大模型请求的完整链路

一个典型大模型请求链路如下:

text 复制代码
Client
  ↓
Nginx / Ingress
  ↓
API Gateway
  ↓
参数校验
  ↓
限流 / 熔断
  ↓
Redis Queue
  ↓
Worker
  ↓
vLLM Server
  ↓
GPU 推理
  ↓
结果返回

我们要优化的不是某一段,而是每一段的耗时构成。

端到端耗时可以拆成:

text 复制代码
Total Latency =
网络耗时
+ 网关处理耗时
+ 排队等待耗时
+ Worker 取任务耗时
+ 模型推理耗时
+ 输出传输耗时

如果你没有按这个公式拆解,就很容易误判。


四、建立全链路 Trace ID

第一步不是优化代码,而是给每个请求加 Trace ID。

没有 Trace ID,你无法串联:

text 复制代码
Gateway 日志
Worker 日志
vLLM 日志
Nginx 日志

五、FastAPI 增加 Trace ID

python 复制代码
import uuid
import time
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()


class TraceMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        trace_id = request.headers.get("X-Trace-Id", str(uuid.uuid4()))
        request.state.trace_id = trace_id

        start = time.time()
        response = await call_next(request)
        cost = round((time.time() - start) * 1000, 2)

        response.headers["X-Trace-Id"] = trace_id
        response.headers["X-Cost-Ms"] = str(cost)

        print({
            "trace_id": trace_id,
            "path": request.url.path,
            "cost_ms": cost
        })

        return response


app.add_middleware(TraceMiddleware)

这样每个请求都会有:

text 复制代码
X-Trace-Id

后续日志全部带上它。


六、分段记录耗时

在 Gateway 中记录:

python 复制代码
import time

def now_ms():
    return int(time.time() * 1000)


@app.post("/chat")
async def chat(req: dict, request: Request):
    trace_id = request.state.trace_id

    t0 = now_ms()

    # 参数校验
    prompt = req.get("prompt")
    max_tokens = req.get("max_tokens", 128)

    t1 = now_ms()

    # 入队
    job = queue.enqueue("tasks.llm_task", trace_id, prompt, max_tokens)

    t2 = now_ms()

    print({
        "trace_id": trace_id,
        "stage": "gateway",
        "validate_ms": t1 - t0,
        "enqueue_ms": t2 - t1
    })

    return {
        "trace_id": trace_id,
        "job_id": job.id
    }

在 Worker 中记录:

python 复制代码
def llm_task(trace_id: str, prompt: str, max_tokens: int):
    t0 = now_ms()

    # 模拟调用 vLLM
    result = call_vllm(prompt, max_tokens)

    t1 = now_ms()

    print({
        "trace_id": trace_id,
        "stage": "worker",
        "llm_call_ms": t1 - t0
    })

    return result

这样就可以得到完整链路耗时。


七、建立性能基线

优化前必须先建立基线。

建议记录以下指标:

text 复制代码
1. Gateway 耗时
2. 队列等待时间
3. Worker 执行时间
4. vLLM 推理时间
5. 总耗时
6. input tokens
7. output tokens

可以设计一个日志格式:

python 复制代码
{
    "trace_id": "xxx",
    "input_tokens": 512,
    "output_tokens": 128,
    "queue_wait_ms": 1200,
    "llm_ms": 1800,
    "total_ms": 3500
}

八、压测脚本:构造不同类型请求

为了真实发现瓶颈,压测不能只用一种 prompt。

python 复制代码
from locust import HttpUser, task, between
import random


class LLMUser(HttpUser):
    wait_time = between(0.5, 2)

    @task
    def chat(self):
        short_prompt = "解释一下Transformer。"
        medium_prompt = "请详细解释Transformer中的自注意力机制,并举一个例子。"
        long_prompt = "请写一篇关于大模型系统部署的技术文章,包含架构、监控、性能优化、稳定性治理。" * 20

        prompt = random.choice([
            short_prompt,
            medium_prompt,
            long_prompt
        ])

        self.client.post("/chat", json={
            "prompt": prompt,
            "max_tokens": random.choice([64, 128, 256, 512])
        })

启动:

bash 复制代码
locust -f locustfile.py --host=http://127.0.0.1:8000

观察不同请求类型下的:

text 复制代码
P95
P99
Queue Wait
LLM Latency
Token 分布

九、常见瓶颈 1:排队时间过长

如果你发现:

text 复制代码
total_ms 很高
llm_ms 正常
queue_wait_ms 很高

说明问题不在模型,而在队列。

可能原因:

text 复制代码
1. Worker 不足
2. GPU 已经满载
3. 请求速率超过处理能力
4. 长任务堵塞短任务

解决方案不是直接加 Worker,而是先判断 GPU 是否已满。

如果 GPU 未满:

text 复制代码
可以增加 Worker

如果 GPU 已满:

text 复制代码
增加 Worker 只会制造更多争抢

更合理的方案是:

text 复制代码
1. 请求分级队列
2. 限制 max_tokens
3. 增加 GPU 副本
4. 拒绝过载请求

十、常见瓶颈 2:长 prompt 拖慢系统

长 prompt 会带来两个成本:

text 复制代码
1. Prefill 成本
2. KV Cache 成本

Prefill 阶段负责处理输入 token。

如果 prompt 很长,哪怕输出很短,也会慢。

建议在 Gateway 侧限制:

python 复制代码
MAX_PROMPT_CHARS = 4000
MAX_TOKENS = 256

if len(prompt) > MAX_PROMPT_CHARS:
    raise ValueError("prompt too long")

max_tokens = min(max_tokens, MAX_TOKENS)

更进一步,可以做请求分级:

python 复制代码
def classify_request(input_tokens: int, max_tokens: int):
    estimated_cost = input_tokens + max_tokens * 2

    if estimated_cost < 512:
        return "short"
    elif estimated_cost < 2048:
        return "medium"
    else:
        return "long"

然后路由到不同队列:

python 复制代码
request_type = classify_request(input_tokens, max_tokens)

if request_type == "short":
    queue = short_queue
elif request_type == "medium":
    queue = medium_queue
else:
    queue = long_queue

这样可以避免:

text 复制代码
长任务拖慢短任务。

十一、常见瓶颈 3:同步等待导致接口占用

错误做法:

text 复制代码
用户请求进来后,一直等待模型生成完成

这会占用连接和服务线程。

对于长文本生成,更好的方式是:

text 复制代码
1. 短任务同步返回
2. 长任务异步提交
3. 前端轮询或 WebSocket 获取结果

示例:

python 复制代码
if request_type == "short":
    return call_llm_sync(prompt, max_tokens)
else:
    job = queue.enqueue("tasks.llm_task", prompt, max_tokens)
    return {
        "mode": "async",
        "job_id": job.id
    }

十二、常见瓶颈 4:没有流式输出

如果生成内容较长,用户不一定需要等全部生成完。

可以使用 Streaming。

在 vLLM OpenAI 接口中可以使用:

json 复制代码
{
  "stream": true
}

FastAPI 示例:

python 复制代码
from fastapi.responses import StreamingResponse
import httpx
import json


async def stream_vllm(payload):
    async with httpx.AsyncClient(timeout=None) as client:
        async with client.stream("POST", VLLM_URL, json=payload) as resp:
            async for line in resp.aiter_lines():
                if line:
                    yield line + "\n"


@app.post("/chat/stream")
async def chat_stream(req: dict):
    payload = {
        "model": MODEL_NAME,
        "messages": [
            {"role": "user", "content": req["prompt"]}
        ],
        "max_tokens": req.get("max_tokens", 256),
        "temperature": 0.7,
        "stream": True
    }

    return StreamingResponse(
        stream_vllm(payload),
        media_type="text/event-stream"
    )

流式输出不能减少总计算量,但能显著改善用户感知延迟。

用户体验从:

text 复制代码
等 8 秒看到完整结果

变成:

text 复制代码
1 秒内看到第一个 token

这在产品体验上差别很大。


十三、常见瓶颈 5:HTTP 超时配置不合理

大模型接口天然比普通接口慢。

如果 Nginx、Ingress、Gateway 的超时设置太短,会出现:

text 复制代码
模型还在生成
上游连接已经断开

Nginx 配置示例:

nginx 复制代码
location / {
    proxy_pass http://llm_gateway;

    proxy_connect_timeout 60s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;

    proxy_buffering off;
}

如果使用流式输出:

text 复制代码
proxy_buffering 必须关闭

否则前端可能收不到实时 token。


十四、常见瓶颈 6:缓存完全没做

很多业务场景中,用户会重复问类似问题。

例如:

text 复制代码
"什么是Transformer?"
"Transformer是什么?"
"解释一下Transformer"

可以做简单缓存。

python 复制代码
import hashlib
import time

cache = {}


def cache_key(prompt: str, max_tokens: int):
    raw = f"{prompt}:{max_tokens}"
    return hashlib.md5(raw.encode()).hexdigest()


def get_cache(key):
    item = cache.get(key)
    if not item:
        return None

    value, expire_at = item

    if time.time() > expire_at:
        cache.pop(key, None)
        return None

    return value


def set_cache(key, value, ttl=300):
    cache[key] = (value, time.time() + ttl)

在接口中使用:

python 复制代码
key = cache_key(prompt, max_tokens)
cached = get_cache(key)

if cached:
    return {
        "answer": cached,
        "cache_hit": True
    }

result = call_llm(prompt, max_tokens)
set_cache(key, result)

return {
    "answer": result,
    "cache_hit": False
}

缓存适合:

text 复制代码
FAQ
固定知识问答
系统提示词生成
结构化分析结果

不适合:

text 复制代码
强实时问题
个性化上下文
高随机性创作

十五、常见瓶颈 7:系统 Prompt 太长

很多 AI 应用会在每次请求前拼接一大段系统 Prompt。

例如:

text 复制代码
你是一个专业助手......
你必须遵守......
你需要按照......
以下是知识库上下文......

这会导致每次请求都重复消耗 token。

优化方式:

text 复制代码
1. 精简系统 Prompt
2. 把固定规则压缩成短指令
3. 知识库上下文只取 TopK
4. 避免把无关历史对话全部塞进去

错误示例:

python 复制代码
context = "\n".join(all_docs)

推荐:

python 复制代码
context = "\n".join(top_k_docs[:5])

大模型系统里,最贵的不是代码,而是:

text 复制代码
每一次重复传入的 token。

十六、优化前后验证结果

在一次内部压测中,优化前指标:

text 复制代码
平均延迟:3.8s
P95:9.5s
P99:18s
队列等待 P95:5.2s
首 token 时间:3.5s

逐步优化后:

text 复制代码
1. 限制 max_tokens
2. 请求分级队列
3. 长任务异步化
4. 开启流式输出
5. 增加缓存
6. 精简系统 Prompt

优化后:

text 复制代码
平均延迟:1.9s
P95:4.1s
P99:7.5s
队列等待 P95:1.3s
首 token 时间:0.8s

注意:

text 复制代码
没有一个优化是"银弹"。
性能提升来自全链路叠加。

十七、踩坑记录

坑 1:只看模型推理耗时

很多人只看:

text 复制代码
vLLM latency

但用户感受到的是:

text 复制代码
端到端延迟

必须看:

text 复制代码
total latency

坑 2:盲目增加 Worker

Worker 多不一定快。

如果 GPU 已经满了,增加 Worker 只会让系统更拥挤。


坑 3:没有区分短任务和长任务

这是大模型系统里最常见的性能坑。

短任务被长任务拖慢,用户体验会非常差。


坑 4:所有请求都同步等待

同步模式简单,但不适合长文本生成。

需要按任务类型区分:

text 复制代码
短任务同步
长任务异步

坑 5:流式输出没有关 proxy_buffering

如果 Nginx 缓冲没关,后端虽然流式返回,前端仍然可能一次性收到。


坑 6:系统 Prompt 越写越长

很多团队为了修复模型行为,不断往系统 Prompt 里加规则。

最后系统 Prompt 变得很长,所有请求成本都上升。


十八、适合收藏的全链路优化 Checklist

text 复制代码
请求入口:
[ ] 是否有 Trace ID
[ ] 是否限制 prompt 长度
[ ] 是否限制 max_tokens
[ ] 是否区分短任务和长任务

队列层:
[ ] 是否统计 queue_wait_ms
[ ] 是否有最大队列长度
[ ] 是否有短/中/长分级队列
[ ] 是否有过载拒绝策略

推理层:
[ ] 是否使用 vLLM
[ ] 是否控制 max_model_len
[ ] 是否监控 tokens/s
[ ] 是否监控 input/output tokens

体验层:
[ ] 是否支持流式输出
[ ] 长任务是否异步化
[ ] 是否有缓存
[ ] 是否有降级结果

网关层:
[ ] Nginx 超时是否足够
[ ] 是否关闭 proxy_buffering
[ ] 是否记录端到端耗时

监控层:
[ ] 是否监控 Avg/P95/P99
[ ] 是否监控 GPU
[ ] 是否监控 Queue Size
[ ] 是否监控错误率

十九、经验总结

这篇文章想强调一个核心观点:

text 复制代码
大模型系统的性能优化,不是模型优化,而是系统工程优化。

单点优化只能解决局部问题。

真正的线上性能来自:

text 复制代码
1. 请求成本可控
2. 队列等待可控
3. 推理吞吐可控
4. 输出体验可控
5. 监控指标可控

如果你只看模型推理时间,很容易忽略:

text 复制代码
队列等待
网关超时
长 prompt
缓存缺失
同步阻塞
系统 Prompt 冗余

这些才是很多线上系统真正慢的原因。


二十、后续优化建议

可以继续深入:

text 复制代码
1. 接入 OpenTelemetry 做 Trace
2. 使用语义缓存降低重复请求
3. 用请求成本模型做智能路由
4. 使用 KEDA 按队列长度扩容 Worker
5. 使用多 vLLM 副本做负载均衡
6. 对不同业务配置不同 max_tokens
7. 结合 RAG 做上下文裁剪
8. 用模型网关统一管理多模型

一句话总结:

text 复制代码
大模型系统不是把模型包成接口,而是要把每一个 token、每一次排队、每一次等待都纳入工程控制。
相关推荐
科技互联.7 小时前
2026年小程序定制市场:个性化需求激增,技术深度成竞争关键
人工智能·小程序
小超同学你好7 小时前
OpenClaw 深度解析与源代码导读 · 第11篇:子 Agent(Sub-Agent)——隔离执行与“向上汇报“的有限协作
人工智能·语言模型·transformer
code 小楊7 小时前
image-2国内开源平替商汤科技SenseNova-U1模型全面解析
人工智能·科技·开源
龙侠九重天7 小时前
VS Code AI 插件生态全景对比:Tabnine、Codeium、Blackbox 等主流工具深度横评
人工智能·vs code·ai 插件
keineahnung23457 小时前
為什麼這個 Tensor 算 dense?PyTorch _eval_is_non_overlapping_and_dense 深入解析
人工智能·pytorch·python·深度学习
IT_陈寒7 小时前
为什么我的Python multiprocessing总是卡在join()?
前端·人工智能·后端
云天AI实战派7 小时前
ChatGPT/AI 智能体功能异常排查指南:账号安全、权限灰度到审批流卡点的全流程解决方案
人工智能·安全·chatgpt
薛定猫AI7 小时前
【深度解析】Open Code Skills 工作流:用知识图谱、Spec 驱动与 UI 设计系统提升 AI Coding Agent 生产力
人工智能·ui·知识图谱
袋子(PJ)7 小时前
2026年pytorch基础学习(基于jupyter notebook开发)——从原理到落地:PyTorch神经网络架构与工程优化解析
人工智能·pytorch·深度学习·学习·jupyter