前言

  1. 模型说明:DeepSeek-V4-Flash 官方原生支持 1M(100 万 token)上下文,本文通过参数限制为 128K 降低显存压力。

  2. 硬件限制:RTX 4090 24G 仅支持 INT4/MXFP4 量化,FP8/FP16 显存不足,且 4090 无原生 FP8 硬件加速,INT4/MXFP4 才可行,显卡不足建议部署采用 CPU/GPU 混合卸载部署,内存得用DDR5以上带宽的。

  3. 部署引擎:全量GPU部署显存压力过大,要求5090以上显卡配置。且vLLM不支持内存卸载,推荐使用ktransformers推理引擎部署,也可使用llama.cpp。内存卸载方案建议内存256G。

  4. 性能预期:INT4/MXFP4至少2张 4090 起步,要求有一定并发和较长上下文窗口建议4张起显卡。另外,显卡不足还可考虑Q2、Q3模型。

一、环境准备

基础环境及版本要求如下:

组件

版本

说明

ktransformers

≥ 0.6.2

v0.6.2 首次原生支持 DeepSeek-V4-Flash

(PR #1970:kt-kernel MXFP4 MoE + sglang hybrid inference),

实用v0.6.4

Python

3.10 / 3.11

建议3.11

CUDA

≥ 12.8

官方文档要求 12.8+(含 4090),可选12.8或13.0,实用12.8

NVIDIA 驱动

≥570(CUDA 12.8 兼容)

可直接下载 CUDA+NVIDIA 驱动 组合包

https://developer.download.nvidia.com/compute/cuda/12.8.0/

local_installers/cuda_12.8.0_570.86.10_linux.run

transformers

== 4.57.1(必须钉版)

5.x 会让 DeepSeekV4Config 报 TypeError,必须手动钉 4.57.1。在线安装 sglang-kt 0.6.4会连带装transformers。

flashinfer

≥ 0.6.9(flashinfer-python + flashinfer-cubin 同版本)

V4-Flash 的 MXFP4 MoE 模块需要 0.6.9 才有的 mxfp8_quantize 等 API。版本下载准确否则后期折腾起来很痛苦。

tilelang

== 0.1.8

4090 必装——NSA sparse-MLA indexer 在非 Hopper GPU

上走 tilelang 路径。

sglang

kvcache-ai fork(ktransformers 内置 submodule)

不是上游 sglang,是 KT 专用 fork。选在线装sglang-kt的话会连带。

1. 基础环境配置

推荐使用 Ubuntu 22.04 系统,搭配 CUDA 12.8+,NVIDIA 驱动 ≥ 570,python ≥ 3.10, ktransformers ≥ 0.6.2:

# 创建并激活虚拟环境
conda create -n deepseek python=3.11 -y
conda activate deepseek

2. 安装 ktransformers 与依赖

# 1. 克隆 ktransformers(含全部 submodule)
git clone https://github.com/kvcache-ai/ktransformers.git
cd ktransformers
git submodule update --init --recursive

# 2. 编译 KT-Kernel(C++/CUDA 扩展,10-20 分钟)
cd kt-kernel && ./install.sh && cd ..

# 3. 安装 SGLang(kvcache-ai 专用 fork,不是上游 sglang)
./install.sh

# 4. 关键依赖(三个必须钉版/版本)
pip install --upgrade flashinfer-python flashinfer-cubin    # ≥0.6.9,两个包同版本
pip install "transformers==4.57.1"                           # 必须!5.x 会报 TypeError
pip install tilelang==0.1.8                                  # 4090 必装!NSA sparse-MLA 路径

# 5. 验证
python -c "import ktransformers; print(ktransformers.__version__)"  # 期望 ≥0.6.2

能在线安装推荐在线安装,直接pip install kt-kernel sglang-kt即可,pip可用国内镜像源比github官网下载速度更快且更方便。具体可参考文档 KTransformers Docs


二、下载 DeepSeek-V4-Flash 模型

1.下载路径

国内用户优先使用 魔搭社区(ModelScope) 下载,速度更快:

优先级地址
Q4首选DeepSeek-V4-Flash-GGUF
Q4可选DeepSeek-V4-Flash-GGUF
官方原版,MXFP4可用DeepSeek-V4-Flash
官方0731原版,MXFP4首选DeepSeek-V4-Flash-0731

2.模型选择

这里我用的 kt 部署的官方0731原版,安全稳定可靠。部署Q4的话首选Unsloth 这个仓库,推荐下载 UD-Q4_K_XL,可使用llama.cpp部署。

官方0731原版下载:

Q4模型下载:

量化方案位宽文件大小你这套机器的评价
UD-Q4_K_XL4-bit155 GB✅ 首选——Unsloth 自家推荐,跟标准 Q4_K_M 效果对齐,200GB 内存宽宽松松
UD-IQ4_NL / UD-IQ4_XS4-bit iMatrix138 GB✅ 备选——重要性加权 4-bit,部分基准反而略好;想留更多内存余量时选这个
UD-Q3_K_M / UD-Q3_K_XL3-bit129 GB⚠️ 仅在你实际只有 192GB、想跑更长上下文时考虑,质量会降
UD-IQ3_S3-bit iMatrix117 GB同上,但要 3-bit 时比 Q3_K_M 更值
UD-Q8_K_XL8-bit162 GB❌ 你这套机器跑没意义——只有 4 卡 24G,仍需卸载,不如直接用 Q4
UD-Q2_K_XL / UD-IQ2_*2-bit91–97 GB❌ V4-Flash 量化损失过大,长链推理会瞎
UD-IQ1_S / UD-IQ1_M1-bit83–87 GB❌ 极端压损,不适合生产

三、RTX 4090 部署核心配置

1. 启动服务

#!/bin/bash
# ========== 4090 专属环境变量 ==========
export CUDA_VISIBLE_DEVICES=0,1,2,3
export FLASHINFER_CUDA_ARCH_LIST=8.9a          # SM_89 = RTX 4090
export TORCH_CUDA_ARCH_LIST="8.9+PTX"
export SGLANG_DSV4_MODE=2604                   # V4-Flash 必须
export SGLANG_DSV4_2604_SUBMODE=2604B          # V4-Flash 必须,缺失会报 swiglu_limit 断言

# ========== 新增gcc配置、禁用NPU ==========
export CC=/usr/bin/gcc
export CXX=/usr/bin/g++
export NVCC_CCBIN=/usr/bin/g++
export SGLANG_USE——NPU=O
export SGLANG_DISABLE_NPU=1
export SGLANG_BACKEND=cuda

# ========== 启动 SGLang + KT-Kernel 服务 ==========
numactl --interleave=all python -m sglang.launch_server \
  --host 0.0.0.0 \
  --port 8000 \
  --model /data/models/DeepSeek-V4-Flash \
  --kt-weight-path /data/models/DeepSeek-V4-Flash \
  --kt-method MXFP4 \
  --kt-num-gpu-experts 20 \
  --kt-cpuinfer 64 \
  --kt-threadpool-count 2 \
  --kt-gpu-prefill-token-threshold 2048 \
  --kt-enable-dynamic-expert-update \
  --tensor-parallel-size 4 \
  --context-length 131200 \
  --attention-backend flashinfer \
  --mem-fraction-static 0.85 \
  --chunked-prefill-size 4096 \
  --max-prefill-tokens 4096 \
  --max-running-requests 3 \
  --watchdog-timeout 1800 \
  --disable-shared-experts-fusion \
  --trust-remote-code \
  --cuda-graph-bs 1 \
  --cuda-graph-max-bs 1 \
  --disable-radix-cache \
  --skip-server-warmup \
  --tool-call-parser deepseekv4 \
  --reasoning-parser deepseek-v4

关键参数说明

参数

说明

4×4090 调优

--kt-method MXFP4

CPU 专家用 MXFP4 精度计算(官方 FP4 原版权重直接用)

默认

--kt-num-gpu-experts 20

放在 GPU 上的路由专家数量

4 卡 96GB 可放 20–30 个(每个约 0.5GB),根据并发数、上下文窗口、显存占比 及自身速度要求进行调节。

--kt-cpuinfer 64

CPU 推理线程数

≈ 物理核心数(留几个给系统)

--kt-threadpool-count 2

CPU 线程池数

双路 CPU 可设 2–4

--tensor-parallel-size 4

4 卡张量并行

= 4090 数量

--context-length 131200

上下文长度

可根据内存大小进行调整,先按32K 起步,有余量再加到128K,要结合并发数、显存占比进行调节 。

--mem-fraction-static 0.85

静态显存占用比例

0.85–0.90

--max-running-requests 2

最大并发请求数

4090 offload 方案,个人使用建议 1-2个,企业内部用5-20个。

SGLANG_DSV4_MODE=2604

V4-Flash 必须设置

缺失会报 swiglu_limit assertion error

FLASHINFER_CUDA_ARCH_LIST=8.9a

4090 专属,告诉 flashinfer JIT 编译 SM_89 kernel

5090 是 12.0a

首次启动耗时 4–10 分钟(权重加载 + MXFP4 swizzling + CUDA Graph 捕获)。

2.服务化与验证

健康检查:

# 模型列表
curl http://192.168.x.x:8000/v1/models

# 对话测试(官方推荐 temperature=1.0, top_p=1.0)
curl http://192.168.x.x:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-ai/DeepSeek-V4-Flash",
    "messages": [{"role": "user", "content": "你好,介绍一下你自己"}],
    "temperature": 1.0,
    "top_p": 1.0,
    "max_tokens": 256
  }'

客户端接入:

Dify / Chatbox / Cherry Studio / OpenAI SDK 填:

  • base_url: http://x.x.x.x:8000/v1
  • api_key: 任意值(SGLang 默认不校验,可加 nginx 前置鉴权)
  • model: deepseek-ai/DeepSeek-V4-Flash

四、部署成功后测试

1. OpenAI 兼容 API 调用

部署成功后,服务默认运行在 http://localhost:8000/v1,兼容 OpenAI 接口格式,可直接用 OpenAI SDK 调用:

from openai import OpenAI
​
# 初始化客户端
client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="dummy"  # 本地部署无需真实 API Key,填任意值即可
)
​
# 对话测试
response = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[
        {"role": "system", "content": "你是一个 helpful 的助手。"},
        {"role": "user", "content": "请用 100 字介绍一下 DeepSeek-V4-Flash。"}
    ],
    temperature=0.7,
    max_tokens=1024
)
​
print("模型回复:", response.choices[0].message.content)

2. 浏览器 Web 界面测试

可使用 Dify、Opencode 连接本地部署的 DeepSeek-V4-Flash模型,实现可视化对话。

五、常见问题

1. 依赖版本冲突

  • pytorch版本冲突问题最常见,建议安装torch==2.9.1或者2.5.0版本的,注意安装GPU版本的,且要CUDA版本匹配。

  • NCCL版本冲突问题也比较多,从国内镜像安装的torch不带NCCL,单独安装可能会有版本冲突问题需要自行调整。可以选择去pytorch官网上下载安装pytorch,会自带NCCL。

  • CUDA版本冲突,这个情况可能会保持找不到CUDA等,要安装与ktransformers适配的cuda版本。

  • tilelang、apache-tvm-ffi、flashinfer-python、flashinfer-cubin未安装或安装的版本不匹配,可单独使用pip install tilelang==0.1.8等方式安装适配版本。

  • gcc和g++版本问题,安装一致的版本即可解决。另外增加环境变量“CC=/usr/bin/gcc”、“CXX=/usr/bin/g++”、“NVCC_CCBIN=/usr/bin/g++”。

2. 生成速度很慢

  • 部分权重放在CPU中调用起来会稍微慢一点是正常的。

  • 模型常驻 GPU的专家数越多,速度越快。有一定概率会命中到 GPU的专家,此时输出速度要比命中到 CPU的专家的速度快很多。

  • 上下文窗口--context-length、并发数 --max-running-requests、常驻GPU专家数 --kt-num-gpu-experts、控制权重 + CUDA Graph + KV pool 占用显存的比例 --mem-fraction-static 这几个参数都对速度有影响,需要根据实际情况权衡。

  • 多卡并行可显著提升速度,预算受限的情况还得忍忍。

3. 无法调用工具

  • 一般vLLM部署的模型无法调工具增加--enable-auto-tool-choice、--tool-call-parser hermes这2个参数即可解决。Kt部署的DS--V4-Flash,实测需要增加--tool-call-parser deepseekv4、--reasoning-parser deepseek-v4这2个参数。

4. 精度下降明显

  • INT4/MXFP4 量化会轻微损失精度,对话、写作场景无明显影响。

六、部署经验

6.1 mem-fraction-static参数说明

mem-fraction-static 0.75为例:

部分占谁的预算例子
静态:模型权重(含 GPU 专家)从 0.75 里扣稠密层 + 专家权重,常驻
静态:KV 缓存池0.75 扣完权重后的剩余预分配,按并发×上下文使用
动态:运行时临时开销0.25 的余量里激活值、临时缓冲、CUDA 上下文、CUDA Graph 捕获、专家换入换出的搬运缓冲、显存碎片

模型的实际占用是:提问时瞬时占用 = 权重 + KV池(≤0.75) + 动态开销(来自0.25)

提问时总量可能超过 0.75——激活值和临时缓冲就是从 0.25 里现拿的,用完全部释放。0.25 不是“预留不用”,而是“随用随取的安全垫”。

两个常见误解

  1. “模型所有能用得上的显存 = 0.75” ❌
    动态部分(激活、graph 捕获、专家搬运)用的就是 0.75 之外的空间,只是不常驻、不预分配。

  2. “0.25 完全闲置” ❌
    它平时看起来闲着,提问时会被激活值等临时借用;而且 CUDA 上下文本身要先占约 0.5~1GB/卡,实际动态余量比 25% 略少。

调 mem-fraction 的本质

它是在静态 KV 容量和动态安全余量之间做分配:

  • 0.85:KV 池更大(能扛更多并发×上下文),但动态余量只剩 15%,提问时激活值一涨就容易 OOM;
  • 0.75:KV 少一点,但动态余量 25%,运行更稳;
  • 0.6:KV 更少,动态余量 40%,最稳但并发/长上下文容量最小。

6.2 kt-gpu-prefill-token-threshold填2048和4096优劣势

2048 和 4096 的差别只有一个:2K~4K 这个区间的提示词走哪条路。4K 以上两种值都走 layerwise,2K 以下两种值都走常规 GPU,完全无差别。

2048 的优劣势

说明
优势2K~4K 的提示也走 layerwise:单次预填充的峰值显存更可控,高并发下多个中等长度请求同时 prefill 时更不容易 OOM;这也是官方 Docker 镜像的默认值
劣势2K~4K 的提示反而变慢:layerwise 有逐层开销,且第一个触发它的请求要承担一次性的槽位分配成本;如果你们这种长度流量多,TTFT 会差一点

4096 的优劣势

说明
优势2K~4K 提示走常规 GPU 预填充:这个区间 TTFT 更快;与官方教程源码示例的值一致(教程 5090 示例用的就是 4096)
劣势2K~4K 提示的预填充峰值显存更高(整段一次处理),高并发下显存压力略大

实际的影响

先看流量分布:

  • 短问答(几个字到几百 token):远低于两个阈值 → 填 2048 还是 4096 完全没区别,主要场景不受影响;
  • 长文档(≥4K):两种值都走 layerwise → 也没区别;
  • 只有 2K~4K 的中等长度请求才是分水岭:4096 更快但更吃显存,2048 更稳但略慢。

建议

使用 4096 ——它和文档、官方示例一致,短问答流量不经过这个分界。只有一种情况值得改成 2048:压测发现多个 2K~4K 请求并发 prefill 时显存吃紧或 OOM,这时降阈值让它们走更省显存的 layerwise 路径。

6.3 skip-server-warmup参数必要性

skip-server-warmup参数是控制预热的,官网示例中保留了这个参数,去掉 = 让服务启动时就把预热做完(CUDA Graph 捕获、内核编译、自动调优、dummy 前向),代价是启动时间变长。优缺点对比如下:
1.去掉(即启动时预热)的优点
(1)第一个真实请求就是快的。不会有“服务已就绪但第一个请求要现场编译内核、捕获 CUDA Graph”的冷启动惩罚——对对外 SLA 或“运维启动后直接交用户”的场景很关键。
(2)错误提前暴露在启动阶段,预热相当于一次冒烟测试:如果 4090 内核缺失、flashinfer 版本不对、显存不足,启动时就会报错,而不是等第一个用户请求时才炸。
(3)行为可预期。看到 “The server is fired up and ready to roll!” 就代表真的 ready,而不是“半 ready”。
2.去掉的缺点
(1)启动时间明显变长。现在你们启动约 4~10 分钟(权重加载 + MXFP4 重排),预热会把 CUDA Graph 捕获和内核编译加进启动阶段,可能再多几分钟。你们现在正处于频繁改参数、反复重启调优的阶段,每轮多等几分钟会很难受。
(2)重启迭代变慢。调 --kt-num-gpu-experts、--mem-fraction-static、上下文长度都要重启,去掉这个参数等于每次重启都附带完整预热。
(3)不是“一次预热,永久全快”。SGLang 启动预热用的是固定 dummy 形状,真实请求如果形状差异大(比如超长 prefill、不同 batch),第一个这种请求仍可能触发新的编译/捕获。所以它消除的是“公共部分”的冷启动,不是所有情况。
(4)偏离官方教程的验证配置。官方 DSV4 教程的命令是带 --skip-server-warmup 的,去掉属于主动偏离(合理,但要知道自己在改什么)。
建议:
调优期:保留 --skip-server-warmup,启动快,重启迭代效率高。启动后手动发一个代表性请求预热(前面说过的方法)。
生产:两个选择

(1)直接去掉它,让启动阶段吸收预热成本(适合 SLA 严格、不允许首个用户吃冷启动);
(2)保留参数,但在启动脚本里自动加一步 warmup:服务 ready 后脚本自动 curl 一个代表性请求再对外开流量。这样兼顾“重启快”和“首个用户快”。

6.4 kt-threadpool-count 参数解析

--kt-threadpool-count 2 是 kt-kernel CPU 侧专家计算的线程池数量,要和 --kt-cpuinfer 配合理解:

两个参数的关系

参数含义你的值
--kt-cpuinferCPU 推理线程总数(= 物理核心数)64
--kt-threadpool-count把 64 个线程分成几个线程池(= NUMA 节点数)2

也就是说:64 个线程分成 2 个池,每池 32 个线程。每个池绑定到一个 CPU(NUMA 节点)上运行,只处理/优先访问该节点的内存。

设置不对会怎样

  • 1:双路机器只有一个池 → 一个节点闲置,吞吐明显下降;
  • 4:每节点挤 2 个池 → 线程争抢同一节点的内存控制器,性能反而变差;
  • 单路机器(1 个节点):设 2 反而多余,应设 1。

6.5 主要参数之间的关系

--mem-fraction-static 是“静态预算”(权重 + KV 池)的总开关;--kt-num-gpu-experts 决定这个预算里权重占多少,--context-length 和 --max-running-requests 决定 KV 池被请求“消耗”多少。 

参数对 mem-fraction 预算的影响本质
--kt-num-gpu-experts ↑权重占用 ↑ → KV 池 ↓(总量不变,专家从池子里挤)供给端:占预算
--context-length ↑不改变池子大小,但单请求占池上限 ↑ → 更容易 pool full需求端:消耗池
--max-running-requests ↑不改变池子大小,但同时消耗池的请求数 ↑ → 更容易排队/被拒需求端:消耗池

关键点:

  • context-length 和 max-running-requests 不会让显存占用变多(池子启动时就按 mem-fraction 定死了),它们只是决定“这个池子够不够用”;
  • 只有 kt-num-gpu-experts 会真实改变池子大小——因为专家权重从 0.75 的预算里优先扣,扣完剩多少,KV 池才有多少。

测试调优时每次只动一个变量:改专家数后看日志里的 max_total_num_tokens 和压测是否出现 KV cache pool is full,用数据判断是该调 mem-fraction、还是降上下文/并发、还是少放几个专家。

6.6 部署后的prefill和decode

部署后后台日志可看到prefill和decode,具体来讲一次请求分两个阶段:

阶段干什么特点
prefill(预填充)把整个输入从头到尾过一遍,算出 KV 缓存一次性处理全部输入,计算重、占内存
decode(生成)逐字生成输出每步只处理 1 个新 token,步数多、延迟敏感

6.7 CUDA Graph失败会有什么影响

CUDA Graph 的价值是把一整个 decode 步的内核序列提前录制成图,每步只做一次极低开销的回放,省掉每步的 CPU 启动开销。

SGLang 捕获 CUDA Graph 失败时,默认回退到 eager 执行(每步正常调度内核),服务继续运行,日志里出现类似 “Failed to capture CUDA graph, falling back to eager mode” 的警告。正确性不受影响,模型照样答得对,只是慢。

失败回退 eager 后:

  • decode 变慢:每步多一次次内核启动/调度的 CPU 开销,batch 越小、步数越多,损失占比越大;
  • 低并发场景损失最明显:这正是你要“快”的优先级场景,和高并发长文本(本来就允许慢)正好相反;
  • CPU 占用可能上升:eager 模式每步都要 CPU 参与调度,负载更高。

6.8 什么时候才会报OOM

“长请求/高并发”会使得0.75的显存池用满,“池子满→排队”不会 OOM,但运行期的动态开销有概率把剩余显存吃穿会报OOM。

6.9 多卡部署时第一张卡会多用显存

这是 TP 架构的正常特性:

来源说明
主进程角色模型加载、tokenizer、初始化都从 rank 0 发起
logits 聚合/采样TP 下采样在 rank 0 做,要把 4 卡的 logits 片段收拢(DeepSeek 128K vocab 下,并发高时这个缓冲有点分量)
CUDA graph 捕获rank 0 上的临时 buffer 通常比其它卡大一些

另外,调度器(scheduler)是跑在 CPU 侧的 Python 进程里的,负责排队、组 batch、决定谁 prefill 谁 decode,它占的是内存,几乎不占 GPU 显存,与多占用的显存无关。

什么时候会真的 OOM

启动期(模型加载时):

  • 权重(稠密层 + GPU 专家)超出显存总量,或超出 mem-fraction 预算——比如专家数设得太大、mem-fraction 设得接近 1;

  • 表现为:启动到一半报错退出,服务根本起不来。

运行期(动态分配时),常见触发点:

  • 激活值峰值:预填充/解码的中间张量。虽然 --chunked-prefill-size 2048 限制了每块激活,但有些算子按“整批 token 数”或“最大上下文”分配工作区(attention workspace、logits、CUDA Graph 捕获),10 路 × 128K 同时 prefill 时这些一次性分配可能很大;

  • CUDA Graph 捕获:捕获时需要额外显存,余量不足就失败;

  • 动态专家换入换出:--kt-enable-dynamic-expert-update 搬运专家时需要临时缓冲,余量接近 0 时最容易炸;

  • 显存碎片:memory.free 显示有空间,但凑不出一块连续的大块,分配失败(本质也是 OOM);

  • DSpark 投机解码:草稿/验证路径有自己的工作区,会额外吃动态显存。


七、总结

  1. RTX 4090 24G 可以部署 DeepSeek-V4-Flash,核心是 INT4/MXFP4 量化 + 限制 128K以内 上下文限制。

  2. 双卡可跑,4卡性能更稳,速度与并发能力显著提升。

  3. 部署后的服务兼容 OpenAI API,可直接对接各类应用与工具。


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐