跳到主内容
快讯直播
AI智模界
教程

单张 RTX 4090 跑 125B 模型冲 100T/s 实战

先说明一件事:Qwen 3.8 Flash Next 125B 这个写法在不同渠道的叫法并不统一,参数量、激活参数量、开源了哪些量化格式,都需要以官方模型仓库和官方文档为准。本文不给具体版本号和下载地址,但把从零到压测的完整流程拆开讲清楚——这套方法对任何一个「百亿到千亿参数级别、准备塞进单张 24GB 卡」的模型都适用。

这篇能做出什么

按下面的步骤走完,能得到三样东西:

1. 一个本地可用的 OpenAI 兼容接口,模型是 125B 级别的稀疏(MoE)权重,跑在单张 RTX 4090 上;

2. 一份可复现的吞吐测试脚本,能分别读出首 token 延迟(TTFT)、单流解码速度、以及并发聚合吞吐;

3. 一套调参对照表,知道每个旋钮拧大拧小分别牺牲什么。

关于「100T/s」,先对齐口径,否则后面全是白忙:

口径 A:单流解码速度。一个请求、一次生成,每秒吐出多少 token。这个数由「每 token 需要从显存读取多少权重字节」决定。RTX 4090 的显存带宽在 1 TB/s 量级(具体以官方规格为准)。

口径 B:聚合吞吐。N 个请求同时打进来,所有请求输出 token 的总和除以墙钟时间。批处理会把同一份权重读取摊到多个请求上,这个数轻松做到单流的数倍。

判断能不能碰到 100:

  • 如果是 125B 稠密模型,即使 4 bit 量化,每生成一个 token 也要过一遍几十 GB 的权重,单流上限远低于 100 tok/s,基本不用试。这种情况下只能走「把部分权重放内存 + 批处理」的路子,目标放在聚合吞吐上。
  • 如果是 MoE 稀疏模型,且激活参数量只有几 B 量级,4 bit 下每 token 需要读取的权重降到几 GB,单流几十到上百 tok/s 是有希望的,剩下的靠 KV cache 格式、批处理和卸载策略去榨。

所以第一步不是装环境,是确认你手上的到底是不是稀疏模型、激活参数是多少。

前置条件清单

项目要求说明
GPURTX 4090 24GB单卡
系统内存建议 128GB 起4 bit 的 125B 权重文件通常几十 GB,卸载时需要内存兜底
磁盘NVMe,预留 150GB+模型分片下载 + 转换中间文件
系统Linux(Ubuntu 系)推理引擎对 Linux 支持更完整
驱动较新的 NVIDIA 驱动版本以官方文档当前版本为准
Python3.10 及以上建议独立虚拟环境
账号模型仓库下载权限部分仓库需要先登录

时间预期:下载几十 GB 的权重、首次跑起来编译内核,加起来通常要一两个小时,不要以为是卡死了。

第 1 步:确认模型与量化版本

去官方模型仓库确认四件事:准确的模型 ID、总参数量、激活参数量、官方提供了哪些量化版本。

量化格式的选择大致是这样:

格式位宽适合场景注意点
GGUF(Q4_K_M 等)4 bit 上下llama.cpp 生态,CPU+GPU 混合MoE 可以把专家层留在 CPU
AWQ4 bitvLLM 等 GPU 引擎需要预量化权重,不是自己转
GPTQ3~4 bitvLLM 等同上,注意 group size
bitsandbytes NF44 bit快速试跑内核效率通常不如前两者
FP88 bit新架构 GPUAda 上的支持情况以内核实现和引擎文档为准

经验法则:优先选官方已经放出来的 4 bit 量化,别自己从 FP16 现转,既费时间又容易掉质量。位宽低于 4 bit(2~3 bit)虽然能塞进显存,但输出质量退化会很明显,建议只在实在装不下时作为临时方案。

下载:

```bash

pip install -U "huggingface_hub[cli]"

需要登录的仓库先执行

hf auth login

export HF_HOME=~/hf

hf download <org>/<model-id> --local-dir ~/models/<local-name>

```

第 2 步:搭好运行环境

```bash

python3 -m venv ~/venvs/bigmodel

source ~/venvs/bigmodel/bin/activate

pip install -U pip wheel

```

装推理引擎。GPU 版推理框架的 wheel 通常自带 CUDA runtime,系统里不一定需要单独装完整 CUDA Toolkit,但驱动版本要够新。版本以官方文档当前版本为准:

```bash

pip install -U vllm

pip install -U openai

```

装完先做一次自检:

```bash

nvidia-smi

python -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.get_device_name(0))"

```

cuda.is_available() 是 False,后面所有事都别做,先把驱动和 wheel 的匹配问题解决。

第 3 步:先跑通,别急着调速度

第一次启动,把上下文长度压小,先把服务拉起来看到输出:

```bash

vllm serve ~/models/<local-name> \

--served-model-name big \

--tensor-parallel-size 1 \

--max-model-len 8192 \

--gpu-memory-utilization 0.90 \

--max-num-seqs 8 \

--port 8000

```

注意 --tensor-parallel-size 1——单卡不要开张量并行。

冒烟测试:

```bash

curl http://127.0.0.1:8000/v1/chat/completions \

-H "Content-Type: application/json" \

-d '{

"model": "big",

"messages": [{"role": "user", "content": "用 200 字解释稀疏专家模型的基本原理"}],

"max_tokens": 256,

"temperature": 0

}'

```

能正常返回,再进入下一步。如果这一步就 OOM,先把 --max-model-len 降到 4096,再降 --gpu-memory-utilization。

第 4 步:显存与内存卸载

24GB 装不下 125B 的 4 bit 权重,必然要卸载。两条路:

路线一:vLLM 权重卸载到内存

```bash

vllm serve ~/models/<local-name> \

--served-model-name big \

--max-model-len 8192 \

--gpu-memory-utilization 0.88 \

--cpu-offload-gb 48 \

--swap-space 24 \

--max-num-seqs 16 \

--port 8000

```

--cpu-offload-gb 越大,能塞下的权重越多,但每生成一个 token 都要通过 PCIe 把被卸载的部分搬到 GPU。PCIe 带宽比显存带宽低一个数量级,这是单流速度的主要杀手。观察到 GPU 利用率常年个位数、CPU 吃满,基本就是栽在这里。

路线二:llama.cpp 的 MoE 专家层卸载

对 MoE 模型,更划算的做法是只把注意力层和共享层放 GPU,把专家层留在 CPU,因为每个 token 只激活少数专家。

```bash

在 llama.cpp 源码目录下,仓库地址以官方页面为准

cmake -B build -DGGML_CUDA=ON

cmake --build build --config Release -j"$(nproc)"

./build/bin/llama-server \

-m ~/models/<model>-Q4_K_M.gguf \

-ngl 99 \

--n-cpu-moe 40 \

-c 16384 \

-fa \

-np 8 \

--host 0.0.0.0 --port 8001

```

--n-cpu-moe 是要留在 CPU 的 MoE 层数,从小往大调,直到刚好不 OOM。-fa 打开 flash attention,长上下文下省显存也提速。

两条路线都试一遍,用同一段 prompt 对比解码速度,选快的那条。

第 5 步:批处理与吞吐调优

单流调不动的时候,转向聚合吞吐。关键旋钮:

参数作用调大的代价
--max-num-seqs同时处理的序列数显存占用上升,单流延迟变差
--max-num-batched-tokens单次前向的 token 上限显存压力大,prefill 抢占 decode
--gpu-memory-utilization预留给 KV cache 的显存比例过高容易运行时 OOM
--kv-cache-dtype fp8KV cache 减半精度略降
--enable-chunked-prefill长 prompt 分块混合请求下延迟更平滑
--max-model-len上下文上限越大 KV cache 越吃显存

一个典型的调优起手式:

```bash

vllm serve ~/models/<local-name> \

--served-model-name big \

--max-model-len 16384 \

--gpu-memory-utilization 0.92 \

--kv-cache-dtype fp8 \

--enable-chunked-prefill \

--max-num-seqs 32 \

--max-num-batched-tokens 8192 \

--cpu-offload-gb 32 \

--port 8000

```

如果模型本身带 MTP(多 token 预测)头,或者能配一个同系列的小草稿模型做投机解码,单流解码速度还能再上一截。支持情况以引擎文档为准,不要硬套参数。

第 6 步:压测与瓶颈定位

先测单流,把 TTFT 和解码速度分开看:

```python

import time

from openai import OpenAI

client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="dummy")

t0 = time.perf_counter()

stream = client.chat.completions.create(

model="big",

messages=[{"role": "user", "content": "写一段 300 字的产品介绍"}],

max_tokens=256,

temperature=0,

stream=True,

)

ttft = None

n = 0

t_last = t0

for chunk in stream:

if chunk.choices and chunk.choices[0].delta.content:

n += 1

if ttft is None:

ttft = time.perf_counter() - t0

t_last = time.perf_counter()

print(f"TTFT {ttft:.3f}s")

print(f"解码阶段 {n / (t_last - t0 - ttft):.1f} tok/s")

```

再测并发聚合吞吐,这才是「100T/s」通常对应的口径:

```python

import asyncio, time

from openai import AsyncOpenAI

client = AsyncOpenAI(base_url="http://127.0.0.1:8000/v1", api_key="dummy")

async def one(i):

r = await client.chat.completions.create(

model="big",

messages=[{"role": "user", "content": f"写一段 {200 + i % 50} 字的说明文字"}],

max_tokens=256,

temperature=0.7,

)

return r.usage.completion_tokens

async def main(n=32):

t0 = time.perf_counter()

res = await asyncio.gather(*[one(i) for i in range(n)])

wall = time.perf_counter() - t0

total = sum(res)

print(f"并发 {n}:墙钟 {wall:.2f}s,总输出 {total} tokens,聚合 {total / wall:.1f} tok/s")

asyncio.run(main())

```

把并发数从 4、8、16、32 依次跑一遍,画出吞吐曲线。拐点出现在哪里,就说明 --max-num-seqs 调到头了——再加并发只会让延迟涨、吞吐不涨。

边跑边看 GPU:

```bash

nvidia-smi dmon -s pucvmet -d 1

```

  • GPU 利用率高、显存吃满 → 正常,继续加并发
  • 利用率低但 CPU 打满 → 卸载太多,PCIe 或系统内存成瓶颈
  • 利用率忽高忽低 → KV cache 不够,序列在排队等显存

常见坑与排错

启动就 OOM。 依次降 --max-model-len、降 --gpu-memory-utilization、加 --cpu-offload-gb。别一上来就开 128K 上下文。

跑一会儿才 OOM。 这是 KV cache 被长请求吃光,属于运行时 OOM。加 --kv-cache-dtype fp8,或者给 --max-model-len 设一个业务上真正需要的值。

把 GGUF 当成 transformers 权重加载。 两种格式走两套引擎,路径别搞混。

误判模型类型。 稠密 125B 和 MoE 125B 的调优策略完全不同。先看激活参数量,再决定目标定在单流还是聚合。

只看端到端时间。 端到端里混了 prefill 时间,不能当解码速度。测吞吐要分开 TTFT 和解码阶段。

长跑掉速。 连着压测十几分钟后速度下滑,先查功耗墙和温度墙:

```bash

nvidia-smi -q -d PERFORMANCE

```

分片下载不全。 下载中断后重跑,确认文件数量完整再启动,否则加载时报「文件缺失」很难一眼看出原因。

驱动与 torch 不匹配。 报 no kernel image is available 这类错误,基本都是驱动太旧或 wheel 与显卡架构不匹配。

显存被别的东西占了。 桌面环境、浏览器硬件加速、之前没退干净的进程都会吃显存。压测前先清一遍。

下一步建议

1. 补质量评估。 速度达标不等于能上线。拿真实业务 prompt 做一轮人工对比,确认量化后的输出质量在可接受范围内。

2. 做量化横向对比。 同一套 prompt,跑 AWQ / GPTQ / GGUF 几种量化,记录速度和质量,选一个平衡点。

3. 固化配置。 把验证过的启动参数写进 systemd unit 或容器 compose 文件,加上 --restart unless-stopped。

4. 加监控。 把引擎暴露的 Prometheus 指标接进看板,盯 TTFT、队列长度、KV cache 使用率,而不是等用户投诉。

5. 考虑多卡。 如果单卡吞吐到顶,再评估张量并行或多实例部署,这时候显存和 PCIe 拓扑会变成新的约束。

6. 跟官方更新。 模型和推理引擎迭代都很快,关注官方仓库的更新说明,新内核有时能带来一波免费提速。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。