在双 RTX 3090 上跑满Qwen3.8-Flash-Next:Strata 本地部署与调优

在双 RTX 3090 上跑满 Qwen3.8-Flash-Next:Strata 本地部署与调优

从选型、绕过驱动和网络的坑,到 120 tok/s 稳态解码、256K 原生上下文、多模态与局域网服务。全部数字来自实测,测量方法和条件随数据一并给出。

0. 硬件底牌与目标

部件 配置 对部署的影响
GPU 2 × RTX 3090 24GB(共 48GB,sm_86) 每多 1GB 显存约多驻留 700 个 MoE 专家
CPU Xeon E5-2666 v3(10C20T,2.9GHz,AVX2) VRAM 未命中专家靠它补算
内存 62GB DDR4 四通道 决定能驻留多大的专家集
主板 X99,PCIe 3.0;GPU0 仅 x8 布线 实测 PCIe 6.1 GB/s(GPU1 x16 为 10.4),限制长提示预填充
系统 Ubuntu 24.04,NVIDIA 驱动 570.211.01 比 Strata 要求的 580 低
磁盘 NVMe,~294GB 可用 模型 78GB + pack 55GB + MTP 6GB
网络 huggingface.co / github.com 不可达,ModelScope / hf-mirror 通畅 第二道坎,也是篇幅最多的一章

目标:开发 + 测试两用,质量优先(保留全部专家),解码 ≥ 80 tok/s,不落盘(专家零 SSD 读取)。

1. 选型:为什么是 IQ3_S

  • Strata 跑的是 Qwen3.8-Flash-Next MoE(512 专家/层),有四个量化尺寸和三个"版本"(原版 / Coder / Swift / Unsloth 4-bit)。决策依据全部来自项目文档的实测数字,不是拍脑袋:

  • 原版 IQ3_S (3.5-bit,保留全部 512 专家):文档原话 "matches the full model on the published tests"。2×3090 的社区实测(#1447,同款双卡、32GB 内存、resident 模式)解码 137 tok/s。

  • Coder(IQ1_M):砍到 256 专家换代码能力(LiveCodeBench 保 99%),但中文会出错或循环(#438)。

  • IQ2_XS / Q2_0:62GB 内存装得下 IQ3_S,没理由牺牲质量。

  • Swift 1.5:同速度、想得更短,对复杂推理任务反而减分。

结论:--family qwen --model IQ3_S --gpus 0,1。

  • 选型阶段的一个重要点:显存比内存更值钱。每多 1GB 显存 ≈ 700 个专家常驻 GPU,被 GPU 接住的专家就不用 CPU 算。48GB 显存 + 62GB 内存的组合,可以把全部专家放在"显存 + 锁页内存"里。

2. 第一道坎:驱动 570 < 580

  • setup 直接拒绝:the NVIDIA driver is too old (570.211.01; 580 or newer is needed)。
  • 不想动驱动(生产机,升级有风险),答案就在报错文本里:--cuda 12 。Strata 为老卡保留了一个 CUDA 12.9 构建的实验引擎(-DSTRATA_EXPERIMENTAL_SM60=ON),CUDA 12 的 minor-version compatibility 把 Linux 驱动下限降到 525,570 轻松通过。

读 setup.py 确认了三件事:

  1. 选 CUDA 12 引擎时驱动检查阈值切换(min_driver = CUDA12_MIN_DRIVER if cuda_tk == 12 else MIN_DRIVER,即 525 vs 580);
  2. release 里只有 Windows 的 cuda12 资产,Linux 必须本地编译;
  3. 实验构建里那些老卡专用路径(Volta 注意力、Pascal BF16)只影响 sm_75 以下,3090 是 sm_86,内核与 CUDA 13 版完全相同,零速度损失。
  • 本机恰好有 /usr/local/cuda-12.8 工具链,编译路线成立。

3. 第二道坎:GitHub 和 Hugging Face 都不通

setup 默认的四个下载源里,两个死了(GitHub 引擎、HF 模型),两个活了(pip 已配清华镜像、NVIDIA 工具链本机就有)。

3.1 llama.cpp 源码:预置 zip + .done 标记

  • Linux 编译引擎需要 pinned commit 的 llama.cpp(Strata 用它的 ggml 和 gguf-py)。setup 的 download() 只认 <file>.done 标记才会跳过联网校验,它还是会去 HEAD github.com 然后挂死 5 次×60s。正确方法:
bash 复制代码
# 文件名必须精确匹配 setup 期望:commit 前 7 位
curl -L -o third_party/llama.cpp-3cf0325.zip \
  "https://gh-proxy.com/https://github.com/ggml-org/llama.cpp/archive/3cf03257f219afbe7334045ff7c6a06ac68c627d.zip"
touch third_party/llama.cpp-3cf0325.zip.done
  • 38MB,1.2 秒下完。这个 .done 标记机制是读 setup.py 源码才发现的,文档里没有。

3.2 --build:跳过不存在的下载

  • 即使加了 --build(本地编译、不用现成引擎),setup 在 Step 4 仍先尝试从 GitHub 拉引擎包,连接挂死。--build 跳过的是"用现成包",不是"跳过联网检查",所以 llama.cpp 的预置必须在第一次 --build 之前完成。

3.3 模型权重:ModelScope + modelscope CLI 并行下载

  • --source modelscope 让 setup 把 HF 的 pinned 仓库映射到 ModelScope 同名仓库(同路径、同尺寸,文件按 ModelScope 自己发布的 SHA-256 校验)。MTP 草稿层的获取脚本同样认 STRATA_SOURCE=modelscope。
  • 但 setup 自带的下载器是单流 urllib,对 78GB 的权重太慢。改用 conda base 里的 modelscope CLI 并行下载(这是和setup 流程并行跑的引擎编译不需要模型):
bash 复制代码
# 只下 IQ3_S 目录,四个尺寸全下要 300GB
modelscope download --model ISTA-DASLab/Qwen3.8-Flash-Next-GSQ-RCO-GGUF \
  --include "IQ3_S/*" --local_dir /mnt/data/Strata-data/gguf-src --max-workers 8
  • 78GB、双分片并行,29 分钟跑完(约 30MB/s)。期间引擎编译同步完成,互不等待。

3.4 --gguf-dir:让 setup 认本地权重

  • setup 支持 --gguf-dir(#305):分片存在时跳过全部下载,且 --gguf-dir 模式下连 .done 标记都不需要(源码里 have_model = all(s.exists() and (done(s) or a.gguf_dir) ...))。最终安装命令:
bash 复制代码
./setup.sh --yes --family qwen --model IQ3_S --gpus 0,1 --cuda 12 \
  --source modelscope --gguf-dir /mnt/data/Strata-data/gguf-src/IQ3_S \
  --data-dir /mnt/data/Strata-data --no-start
  • 一个细节:引擎编译完成后 setup 会自动进入模型下载步骤。必须盯着日志,一见 "engine compiled" 立刻杀掉 setup,把带宽全留给 CLI。

4. 首次启动与"零落盘"验证

bash 复制代码
./run-iq3_s.sh
# [strata] loading the experts into RAM (about 55 GB) and locking part of them for the GPU.

60 秒就绪。启动日志把部署质量交代得清清楚楚:

bash 复制代码
layer split auto: K=20 ... the caches hold 17688 of 24576 profiled pairs (~98.9% of the routed mass)
PCIe probe: 6.1 GB/s host->device (CUDA0) / 10.4 GB/s (CUDA1)
R4 hit path ON - resident experts are computed on the GPU

跑了两个请求后查日志,"不落盘"达标:

  • resident RAM: ... 0 blob reads from the file(稳定使用时从文件读专家为 0)
  • 专家缓存命中率 97-99%,仅 0.1-0.2% 的路由需要跨卡/PCIe 补取
  • 内存 58/62GB(锁页专家副本),/metrics 的 file_blobs 停在 0

5. 速度调优

5.1 MTP 草稿窗口深度(--spec)

--spec 稳态解码(1000-token × 3) 备注
4(默认) 116.1 / 117.0 tok/s 基线
6 119-122 tok/s ✓ 拐点,最终采用
8 106-115 tok/s 窗口验证成本线性涨,接受率撑不住
  • 草稿接受率是关键中间量:spec 6 时 ~80%,spec 8 时掉到让每个窗口携带的"有效 token"反而变少。

5.2 --adapt-async 1 的陷阱:文档数字不可盲抄

  • 文档(#1447)在同款 2×3090 上实测 +6%(137→145)。我加上后没效果 ,读启动日志才发现:--adapt-async 1 is off (it needs the resident RAM mode, --resident-experts)。setup 的常驻变体和显式 --resident-experts 是两回事。显式打开后跑了三组:
bash 复制代码
resident RAM: 21.59 GiB of experts in RAM, 4800 exchanged with the VRAM tier
1000 generated: 116.4 / 107.1 / 112.2 tok/s   ← 比不开还慢
  • 原因:这台机器 98.9% 命中率下几乎没有交换可做,异步交换的开销变成纯成本;而且显式模式只驻留 21.6GB、其余靠页缓存,布局反而更差。已撤销。

5.3 最终成绩

配置 稳态解码
IQ3_S + spec 6 + 双卡 K=20 + 常驻切分 113-122 tok/s(1000-token 连跑)
短上下文含冷启动 首轮 ~73,热身后稳定过百

6. 256K 原生长上下文:Agent 场景实测

  • 上下文从 131072 改 --max-context 262144(模型原生训练窗口)。setup 菜单警告"256K 需要 ~78GB 内存",但那是对"专家全在内存"的布局说的;我们的 KV 留显存(int8 下 256K 仅 ~3.6GB),内存纹丝不动(57GB)。文档 #406 也有 64GB 跑 IQ3_S@256K 的先例。
指标 ~105K 上下文 ~213K 上下文
冷读预填充 3,127 tok/s(34s) 3,494 tok/s(61s)
持续解码 70.2 tok/s 66.6 tok/s
内存/显存 57GB / 23.8+24.0GB 不变

Agent 关键路径,前缀复用,同一长提示词追加一轮后:

bash 复制代码
prompt 105683 tokens = 98304 reused + 7379 read in 4820 ms
  • TTFT 从 34 秒降到 4.8 秒。98,304 = 12 × 8192,恰好是分块边界。

  • 另外:256K 配置下短上下文解码比 128K 配置低 ~10 tok/s(预分配 KV 挤掉 ~700 专家槽位,命中率仍有 99.2%),上下文长度按实际负载选,不是越大越好。

7. 多模态:开启的正确姿势

  • 安装时图省显存关了图片能力,后补开。过程有一个必须预防的坑:setup --setup 会重写配置文件 ,把调好的 --spec 6 重置回 4。对策:命令行显式带上要保留的参数,装完复查一遍配置:
bash 复制代码
./setup.sh --yes --setup --family qwen --model IQ3_S --vision gpu \
  --context 262144 --gpus 0,1 --cuda 12 --source modelscope \
  --gguf-dir /mnt/data/Strata-data/gguf-src/IQ3_S --data-dir /mnt/data/Strata-data --no-start
  • mmproj(0.9GB)从 ModelScope 正常拉取,CUDA 视觉编码器编译 275 个目标,一次性 ~7 分钟
  • 装完手动把 spec 改回 6(旧配置被 setup 自动备份为 .bak)
  • 实测:图片 OCR 逐字准确;文本解码零回退(106-118 tok/s,编码器 1.4GB 从显存余量出,没挤专家缓存)

8. 局域网服务与一次被拦截的"高危"

  • 0.0.0.0 暴露必须配 API key,这是 Strata 文档的硬规则,也是常识。实操中还收获了一次工具链的安全教育:
  1. 我把密钥直接写进启动脚本 → 被本地安全扫描(Mimosa)拦截:硬编码凭据,高危;
  2. 改为正确做法:密钥存独立文件 .strata-api-key(umask 077,权限 600),脚本运行时读取;
  3. strata.sh 直接启动 server.py 并显式传 --host 0.0.0.0 --api-key------不经过 run-iq3_s.sh(它会被 setup 重装覆盖)。

验证:无 key 请求 401,带 key 200,ss -tlnp 确认 0.0.0.0:8080。客户端三行接入:

bash 复制代码
Base URL: http://<LAN-IP>:8080/v1     (OpenAI / Anthropic 双兼容)
API Key:  cat /mnt/data/code/Strata/.strata-api-key

9. 运维脚本与踩坑合集

  • 所有经验最终沉淀为一个 60 行的 strata.sh(start/stop/restart/status):
bash 复制代码
./strata.sh start|stop|restart|status
bash 复制代码
PORT=8080
DIR="/code/Strata"
RUN="$DIR/run-iq3_s.sh"
# LAN access: listen on all interfaces WITH a required API key (docs/AI_SETUP.md ground rule).
# The key lives in .strata-api-key (chmod 600), read at start - never hardcode it here.
HOST="0.0.0.0"
API_KEY="$(cat "$DIR/.strata-api-key" 2>/dev/null)"
[ -z "$API_KEY" ] && echo "ERROR: $DIR/.strata-api-key missing" && exit 1

port_busy() { curl -s --max-time 2 "http://127.0.0.1:$PORT/health" > /dev/null 2>&1; }

wait_port_free() {
    i=0
    while port_busy; do
        i=$((i + 1))
        [ "$i" -gt 60 ] && echo "ERROR: port $PORT still busy after 60 s" && return 1
        sleep 1
    done
}

case "$1" in
start)
    if port_busy; then
        echo "Strata already running: http://127.0.0.1:$PORT"
        exit 0
    fi
    cd "$DIR" || exit 1
    setsid nohup .venv/bin/python serve/server.py --engine strata \
        --config "$DIR/strata-iq3_s.json" --port "$PORT" \
        --host "$HOST" --api-key "$API_KEY" \
        > "$DIR/strata-server.out" 2>&1 < /dev/null &
    i=0
    until curl -s --max-time 2 "http://127.0.0.1:$PORT/health" | grep -q '"loaded": true'; do
        i=$((i + 1))
        [ "$i" -gt 60 ] && echo "still loading... (first start takes longer)" && i=0
        sleep 5
    done
    echo "ready: http://127.0.0.1:$PORT"
    ;;
stop)
    pkill -f "serve/server[.]py" 2>/dev/null
    pkill -f "engine-cuda12/strat[a]" 2>/dev/null
    wait_port_free && echo "stopped"
    ;;
restart)
    "$0" stop && "$0" start
    ;;
status)
    if port_busy; then
        echo "running: $(curl -s --max-time 2 http://127.0.0.1:$PORT/health)"
    else
        echo "not running"
    fi
    ;;
*)
    echo "usage: $0 start|stop|restart|status"
    exit 2
    ;;
esac

它解决的问题:

  • 重启端口竞态 :旧引擎释放 55GB 锁页内存要数秒,stop 后立刻 start 会撞 "port 8080 already in use"。脚本里等待端口释放(最多 60s)再启动。
  • pkill 自匹配 (踩了两次):pkill -f "setup.py --yes" 会匹配到自己这条 bash 命令的命令行,把自己的 shell 杀掉、静默无输出。模式里加字符类:pkill -f "serve/server[.]py"。
  • setup 重写配置 :任何 setup --setup 之后都要复查 spec/context 是否还在。
  • server.py 会自动重启静默引擎(#481):想真正停机要连 server.py 一起停,只杀引擎会被拉起来。

10. 最终部署情况

项 值
模型 Qwen3.8-Flash-Next GSQ-RCO IQ3_S(全 512 专家)
引擎 Strata 0.1.42,CUDA 12.8 本地编译(sm_86,驱动 570 免升级)
布局 双 3090 层切分 K=20,专家常驻"显存+锁页内存",SSD 零读取
上下文 262144(原生),KV int8 留显存
草稿 MTP,--spec 6
多模态 开启(mmproj + CUDA 视觉编码器)
服务 0.0.0.0:8080 + 强制 API key(600 权限密钥文件)
成绩 短上下文 113-122 tok/s;100K→70;213K→66.6;预填充 ~3,500 tok/s
相关推荐
神奇的代码在哪里1 天前
没有独显也能跑多模态:MiniCPM-V 4.7-1B 本地 OCR 实测(附源码)
ocr·多模态大模型·llama.cpp·gguf·minicpm-v
xiangshangdemayi10 天前
Strata教程:让GISer真正实现了token自由
ai·gis·教程·token·strata·自由
n1121211 天前
4G 显存老显卡怎么跑模型:llama.cpp 加 GGUF 配置
llama·llama.cpp·本地大模型·gguf
guwentian2 个月前
不用显卡也能跑大模型?GGUF 量化与 llama.cpp 本地推理实战
大模型·token·gguf
weixin_471383032 个月前
26 GGUF 量化
gguf
uncle_ll2 个月前
大模型落地选型GGUF 量化与 Ollama 部署指南
人工智能·大模型·llm·ollama·gguf
尽兴-4 个月前
6.1 模型优化:量化 INT4/INT8、GPTQ、AWQ、GGUF
人工智能·gptq·awq·gguf·int4/int8
若苗瞬4 个月前
谷歌发布了 Gemma 4 QAT (Quantization-Aware Training) 模型
google·gemma·qat·gguf·unsloth
belldeep6 个月前
AI: ggml llama.cpp 与 BitNet 模型介绍
人工智能·llama.cpp·bitnet·gguf·ggml