[Deepseek-V4-Flash-0731] 在单张RTX5090 + DDR5桌面配置下,通过VLLM CPU/内存卸载实现完整1M上下文,~800 tps预填充 & 15+ tps解码 [智能体编码]

Reddit r/LocalLLaMA 模型

摘要

关于在单张 RTX 5090 + 256GB DDR5 台式机上使用 vLLM 配合 CPU/内存卸载运行 DeepSeek-V4-Flash-0731 完整 100 万 token 上下文的技术报告,实现了约 800 tokens/秒的预填充速度和约 15 tokens/秒的解码速度。

首先,显然我借助了 AI 来撰写这篇帖子,而这个主题正是让我能够完成这一切的关键:https://old.reddit.com/r/LocalLLaMA/comments/1veow4b/deepseek_v4flash_284b_moe_at_33_toks_single_68/ 我这篇帖子基于上面的链接。 我的硬件: - RTX 5090 32GB - Ryzen 9 9950X3D - 256GB DDR5-5600 - 单 NUMA 节点 - Linux Mint - NVIDIA 驱动 595.71.05 - CUDA 13.2 软件: - guqiong96/Lvllmds4-x - vLLM 2.3.9 - lk_moe 2.3.2 - PyTorch 2.11.0+cu130 原生版 DeepSeek-V4-Flash-0731 safetensors 检查点: - 48 个 safetensors 分片 - 检查点大小约 155.4 GiB ## 我需要做的一个修复 启动过程中,FlashInfer 的 CUDA IPC 辅助程序可能会意外找到 TileLang 的 `libcudart_stub.so`,而不是真正加载的 CUDA 运行时。这最终会导致 `undefined symbol: cudaDeviceReset` 错误。 问题在于 FlashInfer 的 `find_loaded_library("libcudart")` 会对 `/proc/self/maps` 做子字符串搜索。我修补了 `flashinfer/comm/cuda_ipc.py`,让它改为检查实际文件名: ```python def find_loaded_library(lib_name): with open("/proc/self/maps") as f: for line in f: if "/" not in line: continue start = line.index("/") path = line[start:].strip() filename = path.split("/")[-1] if ( filename.startswith(lib_name + ".so") or filename.startswith(lib_name + "-") ): return path return None ``` 打上这个补丁后,FlashInfer 就能正确解析到真正的 libcudart,而不是 TileLang 的桩库。这是一个本地补丁,显然如果相关包被替换,需要重新应用。 ## 当前启动配置 以下是我最终使用的配置: ```bash source ~/ds4x-venv/bin/activate MODEL="/home/blackbeard/models/DeepSeek-V4-Flash-0731" export CUDA_DEVICE_ORDER=PCI_BUS_ID export CUDA_VISIBLE_DEVICES=0 export LVLLM_MOE_NUMA_ENABLED=1 export LK_THREADS=12 export OMP_NUM_THREADS=12 export LK_THREAD_BINDING=CPU_CORE # 将两个完整的路由 MoE 层常驻在 GPU 上。 export LVLLM_GPU_RESIDENT_MOE_LAYERS=0,1 # 目前走 CPU/混合预填充路径。 export LVLLM_GPU_PREFILL_MIN_BATCH_SIZE=0 export FLASHINFER_DISABLE_VERSION_CHECK=1 export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True vllm serve "$MODEL" \ --host 0.0.0.0 \ --port 8070 \ --tensor-parallel-size 1 \ --max-model-len 1048576 \ --gpu-memory-utilization 0.92 \ --trust-remote-code \ --served-model-name DeepSeek-V4-Flash-0731 \ --compilation_config.cudagraph_mode FULL_DECODE_ONLY \ --enable-prefix-caching \ --enable-chunked-prefill \ --max-num-batched-tokens 8192 \ --dtype bfloat16 \ --max-num-seqs 2 \ --enable-auto-tool-choice \ --tool-call-parser deepseek_v4 \ --kv-cache-dtype fp8_ds_mla \ --tokenizer-mode deepseek_v4 \ --reasoning-parser deepseek_v4 \ --default-chat-template-kwargs '{"enable_thinking": true, "reasoning_effort": "max"}' \ --speculative-config '{"method":"dspark","num_speculative_tokens":2,"draft_sample_method":"greedy"}' \ --disable-custom-all-reduce ``` 完整的原生 1M 上下文可以塞进这块 32GB GPU,即使有两个完整的路由 MoE 层常驻在 GPU 上。其余专家保持驻留在系统内存中。 ## DSpark 在推理过程中的表现非常不同 在长时间推理段落中,草稿接受率可能会骤降。我观察到长时间段的表现大约为: - 草稿接受率:约 30-50% - 生成速度:约 11-13 tok/s 有一段约 6 分钟的时间,平均值大约为: - 草稿接受率:约 40% - 生成速度:约 11.9 tok/s 然后模型过渡到一个可预测性高得多的生成阶段,数字跃升到大约: - 草稿接受率:约 87-88% - 生成速度:约 17.4-17.6 tok/s 这种关联性极强:吞吐量基本上跟随 DSpark 接受率变化。某些高接受率的窗口看起来像: - 平均草稿接受率:89.8% - 平均生成吞吐量:17.9 tokens/s 而低接受率的推理窗口则表现为: - 平均草稿接受率:38% - 平均生成吞吐量:约 12 tokens/s 这暗示了一个显而易见的优化方向。 ## 动态 DSpark 深度 对于这种工作负载,我猜测理想的行为大致是: - 推理/思考阶段:1 个推测 token - 正常/最终解码阶段:2 个推测 token 当模型在做困难推理时,第二个草稿 token 通常不值得计算;但当它过渡到可预测性更高的代码/文本生成时,第二个 token 就变得非常有价值。vLLM 目前没有给我一个简单的运行时开关来做这件事,所以我可能会在之后修补推测解码路径,尝试根据模型当前是在输出推理内容还是最终输出,来动态改变草稿深度。这看起来是剩余解码优化中最大的一个方向。 ---非 AI 评论部分开始--- 敬请期待,我正在写一个 if/else 块来修复推理期间变慢的那个愚蠢行为,并从这套技术栈里再压榨出更多 tps。 ---非 AI 评论部分结束---
查看原文

相似文章

Deepseek V4 Flash 在 RTX 5090 MoE 上运行

Reddit r/LocalLLaMA

用户分享了在 RTX 5090 上使用 llama.cpp 的一个分支运行 DeepSeek-V4-Flash (Q2_K) 的优化基准测试结果,实现了 21.3 token/秒的生成速度和 100 万上下文大小。

DeepSeek V4 Flash on a Single AMD MI300X

Hacker News Top

This repository provides configuration, patches, and tuning to run the DeepSeek V4 Flash 304B checkpoint on a single AMD MI300X in production, achieving 168 tok/s decode without quantization. It includes correctness overlays for vLLM ROCm, AITER tuning tables, and a hybrid KV cache strategy.