跳转至内容
  • 版块
  • 最新
  • 标签
  • 热门
  • 用户
  • 群组
皮肤
  • 浅色
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • 深色
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(LCZ-Blue)
  • 不使用皮肤
  • LCZ-Green
  • LCZ-Blue
  • LCZ-Black
折叠
品牌标识

抡锤者

首页 版块 标签
硬件
装备秀
AI 广场 App
  1. 主页
  2. 版块
  3. LLM讨论区
  4. AI Pro R9700 单卡 SGlang qwen3.8 27b

AI Pro R9700 单卡 SGlang qwen3.8 27b

已定时 已固定 已锁定 已移动 LLM讨论区
r9700sg-langqwen-27b
15 帖子 7 发布者 1.0k 浏览 2 关注中
  • 从旧到新
  • 从新到旧
  • 最多赞同
回复
  • 在新帖中回复
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • fcmeF fcme

    这个pp速度agent场景下能用吗?二三百感觉不太够呢

    L
    L
    LearningAI
    发表于 最后由 编辑
    #5

    @fcme 好像是因为不支持WMMA影响了prefill 的速度。 但是我搞不定,因为都是让Hermes + Deepseek V4.1 flash抄作业的😅

    fcmeF 1 条回复 最后回复
    0
    • paul houP paul hou

      SGLang要跑起來不容易,是否可以麻煩測試多併發,這個才是SGLang的強項。

      L
      L
      LearningAI
      发表于 最后由 编辑
      #6

      @paul-hou 今天走通了我也很高兴,赶紧让Hermes把配置和过程给总结下来,跟大家分享一下。具体测试我有空再做,或者其他有r9700的朋友们有空也可以测一下

      1 条回复 最后回复
      0
      • kos orK
        kos orK
        kos or
        至尊皇帝
        发表于 最后由 kos or 编辑
        #7

        恭喜跑動了R9700 32G真的很舒服, 7900 XTX 24GB 也不錯

        讓 ChatGPT 上網找一下 R9700 跑相關LLM的最佳表現, 然後把帖子丟給 Hermes Deepseek, 應該會有更好的產出

        1 条回复 最后回复
        0
        • L LearningAI

          说明:本文原为英文,已按原文逐段译为中文——技术内容一字未改,命令、路径、参数、代码块全部原样保留。译文由小特整理。

          今天让Hermes抄flyer666的作业,单卡r9700跑SGlang 成功了,跟大家分享一下,同时感谢Terry建立这个环境供大家一起学习,也感谢各位高手分享AI知识。

          在 AMD Radeon R9700 (gfx1201) 上用 SGLang 运行 Qwen3.8-27B(W4A16 GPTQ,4-bit)

          一份完整、可复现的构建与运维指南。它是为了能在另一台机器上(带 R9700)照着执行而写的
          ——无论是人还是 agent——每个阶段之后都设有一个校验点,这样失败会在它发生的地方
          被发现,而不是等到三个阶段之后。

          这里的一切都是在参考机器上凭经验确立的,实测数字都是真实的,不是估算出来的。凡是
          属于估算、或从未测试过的内容,都会明确标注出来。

          约定。 把 $HOME 换成你的家目录(/home/<user>)。
          像 <API-KEY>、<BOT-TOKEN>、<server-ip> 这样的占位符需要你自己填写。
          命令面向 Ubuntu 24.04 + Bash。


          0. 这套方案能达成什么

          模型 Qwen3.8-27B(mamba/attention 混合 VL 模型,arch Qwen3_5ForConditionalGeneration),4-bit GPTQ W4A16
          GPU 1× Radeon AI PRO R9700,32 GB,gfx1201 (RDNA4)
          引擎 Docker 中的 SGLang 0.5.13.post1(rocm/sgl-dev),TP=1
          Decode 27 t/s(普通)· 43–62 t/s(使用 NEXTN/MTP 投机)
          Prefill @4K 244–247 t/s(scalar GPTQ 路径——原因见 §5.4)
          KV pool 仅 device 295,902 tokens (fp8_e4m3) · 带 HiCache host pool 时 887,707 tokens
          上下文 每请求 131,072
          视觉 支持——vision tower 常驻
          权重占用显存 17.95 GB

          24 GB 的卡不行:checkpoint 约 18.6 GiB,而你还需要实实在在的余量留给 KV、
          graph buffer 和 workspace。


          1. 前置条件

          1.1 硬件 / 操作系统

          • 1× Radeon R9700 (gfx1201),32 GB——或任意 32 GB 以上的 gfx1201 显卡
          • 如果想要 HiCache(longctx)档,系统内存 ≥ 62 GB。其中 21 GB
            被 pinned host pool 吃掉。speed 档不需要 host pool。
          • 约 120 GB 可用磁盘:Docker 镜像约 71 GB + 模型约 19 GB + 构建树约 1–3 GB
          • Ubuntu 24.04(或类似版本),使用内核自带的 amdgpu 驱动。宿主机侧不需要安装
            ROCm
            ——容器镜像自带 userland;宿主机只需要内核驱动,再加上暴露出来的
            /dev/kfd 与 /dev/dri/*。

          1.2 Docker + GPU 访问

          sudo apt-get update
          sudo apt-get install -y docker.io
          sudo systemctl enable --now docker
          sudo usermod -aG docker "$USER"     # then LOG OUT and back in
          

          校验点 1 — 确认 GPU 设备节点存在且守护进程可用:

          ls -l /dev/kfd /dev/dri/                 # expect /dev/kfd + renderD128.. cards
          docker info >/dev/null && echo "docker OK"
          

          ⚠️ 两个会白白浪费时间的坑:

          • 组成员身份只对新会话生效。在已经打开的 shell 里,
            给 Docker 命令加上 sg docker -c "..." 前缀即可,不必重新登录。
          • 后面会看到,systemd user unit 无法把自己排在系统级
            docker.service 之后。应改用就绪循环等待守护进程(§9.1)。

          1.3 识别你的 render 节点并确认架构

          for c in /sys/class/drm/card*/device/uevent; do
            echo "$c $(grep -o 'PCI_ID=.*' $c)"
          done
          cat /sys/class/drm/card*/device/mem_info_vram_total   # bytes
          

          记下属于 R9700 的那个 renderDxxx 节点——下面每条命令都会用到它。在多卡机器上,
          只把这一个节点传进容器,才能让另一张卡保持空闲。

          校验点 2 — 在容器内,架构必须读出 gfx1201:

          IMG=rocm/sgl-dev:v0.5.13.post1-ubuntu24.04-py3.14-rocm7.14.1
          docker run --rm --device /dev/kfd --device /dev/dri/renderD129 \
            --group-add=video --security-opt seccomp=unconfined \
            -e HIP_VISIBLE_DEVICES=0 -e ROCR_VISIBLE_DEVICES=0 \
            $IMG bash -lc 'rocminfo | grep -m1 gfx'
          # expected: gfx1201
          

          这一步要是失败,就停下来——后面任何东西都不会工作。


          2. 拉取镜像

          docker pull rocm/sgl-dev:v0.5.13.post1-ubuntu24.04-py3.14-rocm7.14.1
          

          约 71 GB。镜像内含:Python 3.14、HIPCC/ROCm 7.14、Triton 以及 SGLang wheel。
          请固定这个 tag——§5 的补丁集针对的正是这个确切版本。


          3. 获取模型

          3.1 用哪个 checkpoint,以及为什么

          使用 Vishva007/Qwen3.8-27B-W4A16-AutoRound-GPTQ(约 19 GB)。

          为什么偏偏用这一个:它是货真价实的 GPTQ W4A16 打包(4-bit 权重、
          16-bit 激活),并且有意把它的 MTP/NEXTN head 留在 bf16,这样投机解码仍然能用。
          这个模型上所有打着 "AWQ" 招牌的 checkpoint 实际上都是 Quark 或 compressed-tensors
          格式,SGLang 在 RDNA 上跑不了(见附录 C)。

          mkdir -p "$HOME/models/sglang"
          hf download Vishva007/Qwen3.8-27B-W4A16-AutoRound-GPTQ \
            --local-dir "$HOME/models/sglang/qwen3.8-27b-w4a16-gptq"
          

          预期是 5 个权重分片 + model_extra_tensors.safetensors + 常规的那些配置文件。

          3.2 硬卡点 #1 — checkpoint 的 config.json 会让 loader 挂掉

          该 checkpoint 自带两条动态量化规则,把 MTP tensor 纳入了量化:

          "+:.*mtp.*":      {...},
          "+:.*mtp\\.fc.*": {...}
          

          但那些 tensor 是以 bf16 存储的,所以 GPTQ loader 会去反量化未量化的数据然后炸掉。
          把这两条替换成一条排除(exclusion)规则。

          #!/usr/bin/env python3
          """Fix the MTP dynamic-quant rules. Idempotent; keeps a one-time .orig backup."""
          import json, os, shutil, sys
          
          path = sys.argv[1] if len(sys.argv) > 1 else \
              os.path.expanduser("~/models/sglang/qwen3.8-27b-w4a16-gptq/config.json")
          
          cfg = json.load(open(path))
          qc  = cfg.get("quantization_config", {})
          dyn = qc.setdefault("dynamic", {})
          
          removed = [k for k in list(dyn) if k.startswith("+") and "mtp" in k.lower()]
          for k in removed:
              del dyn[k]
          
          added = False
          if not any(k.startswith("-") and "mtp" in k.lower() for k in dyn):
              dyn["-:.*mtp.*"] = {"bits": 16, "group_size": 128}
              added = True
          
          if removed or added:
              bak = path + ".orig"
              if not os.path.exists(bak):
                  shutil.copy(path, bak)
              json.dump(cfg, open(path, "w"), indent=2)
              print(f"patched: removed {len(removed)} include-rule(s), added exclusion: {added}")
          else:
              print("already patched (nothing to do)")
          print("mtp rules now:", {k: v for k, v in dyn.items() if "mtp" in k.lower()})
          print("quant:", qc.get("quant_method"), qc.get("bits"), "group", qc.get("group_size"))
          

          校验点 3 — 运行之后,quantization_config 必须显示
          gptq / bits 4 / group_size 64,而且唯一的 MTP 规则必须是
          -:.*mtp.*,带 bits: 16。未量化的 tensor(embeddings、MTP head、
          mamba/conv 参数)是 bf16 → 它决定了 §6 中的 dtype 选择。


          4. 构建之前先理解差距

          有两个事实使得自建成为必须——请自行验证它们,以确认在你所用的版本上
          是否仍然需要这个 fork:

          # (a) The prebuilt wheel ships NO GPTQ ops
          docker run --rm $IMG bash -lc 'python3 -c "
          import torch, sgl_kernel as s
          print([x for x in dir(s) if \"gptq\" in x.lower()])"'
          # expect: [] (empty)
          
          # (b) Upstream has no ROCm W4A16 int4 quant *scheme* beyond these:
          docker run --rm $IMG bash -lc \
            'grep -rhoE "quark_w4[a-z0-9_]+" /sgl-workspace/sglang/python/sglang/srt/layers/quantization/quark/ | sort -u'
          # quark_w4a4_mxfp4 / _moe / quark_w4a8_mxfp4_moe — MXFP4 is CDNA+AITER only.
          

          所以:RDNA 上的 GPTQ 支持必须自己编译进去。


          5. 为 gfx1201 构建 GPTQ kernel

          5.1 获取 fork

          mkdir -p "$HOME/sglang-sandbox/src" && cd "$HOME/sglang-sandbox/src"
          git clone -b gfx1100-support https://github.com/StevenChenSE/sglang.git sglang-gfx1100
          cd sglang-gfx1100 && git log -1 --format='%H %s'
          # reference: f84475ccf593087df3d1da13c9e675eadee12352
          

          这个分支带有 RDNA3 的 GPTQ kernel(csrc/gemm/gptq/q_gemm_rdna3*.{hip,cu}、
          q_gemm_rdna3_wmma.*、moe_q_gemm_rdna3.hip),一个 ROCm 版 setup_rocm.py,
          以及它自己的 tests/test_gptq_rdna3_smoke.py。

          5.2 为什么原样无法为 gfx1201 构建

          三道各自独立的关卡,全都只认 gfx1100:

          1. scalar 反量化 GEMM 主体被 defined(__gfx1100__) 守护;
          2. 一处运行期 TORCH_CHECK 断言架构是 gfx1100;
          3. WMMA translation unit 使用了 wmma-256b-insts builtin——只有 RDNA3 才有
            的指令
            。Clang 拒绝为 gfx1201 编译它们,而强行开启该 feature 会让
            clang-23 段错误。没有绕法;这个 TU 必须去掉。

          关键在于,在 RDNA 上通用的 gptq_gemm op 会直接路由进
          gptq_gemm_rdna3,所以 scalar 路径就是全部实现——WMMA
          TU 是一个独立的、可选的 op。

          5.3 硬卡点 #2 — 跨 4 个文件的 5 处修改

          把这些改动应用到 fork 上(§12 的脚本会替你完成;这里说的是它们改了什么、以及为什么)。
          请以源码文本作为锚点,不要依赖行号。

          (1)+(2) csrc/gemm/gptq/q_gemm_rdna3.hip 和 q_gemm_rdna3.cu —— 放宽
          架构守护,让 scalar 主体能为 RDNA4 编译:

          // before
          #if defined(__HIPCC__) && defined(__gfx1100__)
          // after
          #if defined(__HIPCC__) && (defined(__gfx1100__) || defined(__gfx1201__))
          

          (3) 同样这两个文件 —— 当 WMMA 被排除时,WMMA dispatch 和只在 gfx1100 成立的
          运行期断言都不得被编译:

          #ifndef SGL_GPTQ_NO_WMMA
            ... existing WMMA dispatch + TORCH_CHECK(arch == gfx1100) ...
          #endif
          

          (4) csrc/common_extension_rocm.cc —— gptq_gemm_rdna3_wmma 的 extern
          声明以及它的 m.def/m.impl 注册也必须加上守护,否则
          链接时会出现未定义符号:

          #ifndef SGL_GPTQ_NO_WMMA
            ... extern decl + m.def/m.impl for gptq_gemm_rdna3_wmma ...
          #endif
          

          (5) python/sglang/kernels/aot/setup_rocm.py —— 对于非 gfx1100 的 RDNA
          目标:去掉 WMMA 源文件,并为两个编译器都定义该宏:

          hipcc_flags.append("-DSGL_GPTQ_NO_WMMA")
          cxx_flags.append("-DSGL_GPTQ_NO_WMMA")
          

          setup_rocm.py 里还有一个 RDNA_TARGETS = {gfx1100, gfx1151, gfx1201} 集合,
          如果你的卡是别的 RDNA4 SKU,就需要扩展它。

          5.4 构建

          DEV=/dev/dri/renderD129 TARGET=gfx1201 JOBS=16 \
            bash "$HOME/sglang-sandbox/build-gptq-kernels.sh"
          

          脚本在镜像内运行 setup_rocm.py build_ext --inplace,只暴露一个 DRM 节点,
          并把 AMDGPU_TARGET 钉死,这样架构探测不会漂移。

          产物:python/sglang/kernels/aot/python/sgl_kernel/common_ops.cpython-314-x86_64-linux-gnu.so
          (约 25 MB,32 个 gfx1201 code object,约 86 个 gptq 符号)。

          如果构建因为删不掉上一次尝试留下的 root 所属对象而失败,就通过容器删除
          build 目录(容器是以 root 运行的):
          docker run --rm -v <src>:/src $IMG bash -lc 'rm -rf /src/python/sglang/kernels/aot/build; chown -R 1000:1000 /src'

          校验点 4 — 用数值验证,而不是凭感觉。改造 fork 自带的 smoke
          test(constant-nibble 技巧 → 解析上已知的结果),要求
          在 R9700 上 35/35 项检查通过,maxdiff = 0,覆盖 fp16 和 bf16、M=1→128、
          GPTQ v1 与 v2、gptq_shuffle,以及生产场景(bf16 激活 + fp16
          scale)。只有那个 WMMA op 可以跳过,因为你是刻意不构建它的。

          放弃 WMMA 的实际代价在 prefill,不在 decode:

          scalar 路径(本方案) 带 WMMA 的 gfx1100 构建(参考)
          decode 27.2 t/s 2×7900XTX TP=2 上约 80 t/s
          prefill @4K 246 t/s 每卡约 934 t/s

          Decode 受带宽限制,因此不受影响(27.2 t/s ≈ 640 GB/s ÷ 18.6 GB 上限的
          80 %)。真正的牺牲品是 prefill——大约 3.8×。 要想把它找回来,
          需要一个原生的 W4A8/FP8 RDNA4 kernel。


          6. 硬卡点 #3 — dtype 必须是 bf16,而 SGLang 禁止它

          上游 SGLang 把 GPTQ 限制为 fp16:

          torch.bfloat16 is not supported for quantization method gptq
          

          但 checkpoint 里未量化的 tensor 是 bf16,而这个模型是 mamba/attention
          混合架构,它的 causal_conv1d Triton kernel 在 bf16/fp16 混用时会硬失败
          (Mismatched type for col0)。所以你必须跑 bf16——也就必须
          给 SGLang 打补丁以允许它。

          把镜像里的
          sglang/srt/layers/quantization/gptq/gptq.py 复制出来,然后在 GPTQConfig 中:

          @classmethod
          def get_supported_act_dtypes(cls) -> List[torch.dtype]:
              return [torch.half, torch.bfloat16]     # upstream is [torch.half] only
          

          依据:§5 构建的 kernel 能处理 bf16 激活——35/35 的 smoke test 证明的正是这一点
          (bf16 激活 + fp16 scale)。运行时把打过补丁的文件挂载覆盖镜像里的副本(§7)。


          7. 启动脚本

          标准调用方式。保存为 $HOME/sglang-sandbox/run-sglang.sh,并修改
          顶部的路径。

          #!/bin/bash
          set -euo pipefail
          
          IMG=${IMG:-rocm/sgl-dev:v0.5.13.post1-ubuntu24.04-py3.14-rocm7.14.1}
          MODELS_DIR=${MODELS_DIR:-$HOME/models/sglang}
          MODEL_SUBDIR=${MODEL_SUBDIR:-qwen3.8-27b-w4a16-gptq}
          PORT=${PORT:-30001}
          CTX=${CTX:-131072}
          PAGE=${PAGE:-64}
          MEMFRAC=${MEMFRAC:-0.78}
          KV=${KV:-fp8_e4m3}
          DTYPE=${DTYPE:-bfloat16}
          CTR=${CTR:-sglang-gptq}
          RENDER_NODE=${RENDER_NODE:-/dev/dri/renderD129}
          
          # bf16 GPTQ allowance (see §6)
          GPTQ_PY=${GPTQ_PY:-$HOME/sglang-sandbox/patches/gptq.py}
          PY_MOUNT=""
          [ -f "$GPTQ_PY" ] && PY_MOUNT="-v $GPTQ_PY:/sgl-workspace/sglang/python/sglang/srt/layers/quantization/gptq/gptq.py"
          
          # self-built sgl_kernel with the GPTQ ops (see §5)
          GPTQ_SO=${GPTQ_SO:-$HOME/sglang-sandbox/src/sglang-gfx1100/python/sglang/kernels/aot/python/sgl_kernel/common_ops.cpython-314-x86_64-linux-gnu.so}
          SO_MOUNT=""
          if [ -f "$GPTQ_SO" ]; then
            SO_MOUNT="-v $GPTQ_SO:/opt/venv/lib/python3.14/site-packages/sgl_kernel/common_ops.cpython-314-x86_64-linux-gnu.so"
          else
            echo "[!] built sgl_kernel NOT found at $GPTQ_SO — GPTQ ops will be missing"; exit 1
          fi
          
          SPEC_FLAGS=""
          [ "${SPEC:-1}" = "1" ] && SPEC_FLAGS="--speculative-algorithm NEXTN --speculative-num-steps 3 --speculative-eagle-topk 1 --speculative-num-draft-tokens 4"
          
          HICACHE_FLAGS=""
          [ "${HICACHE:-0}" = "1" ] && HICACHE_FLAGS="--enable-hierarchical-cache --hicache-ratio 2 --hicache-io-backend direct --hicache-write-policy write_through"
          
          # a crashed container stays "Up" and holds the port + GPU
          sg docker -c "docker rm -f $CTR" >/dev/null 2>&1 || true
          
          exec sg docker -c "docker run --rm --name $CTR \
            --device /dev/kfd \
            --device $RENDER_NODE \
            --network=host \
            --ipc=host \
            --group-add=video \
            --cap-add=SYS_PTRACE \
            --security-opt seccomp=unconfined \
            -e HF_HOME=/app/models \
            -e SGLANG_USE_AITER=0 \
            -e SGLANG_ROCM_FUSED_DECODE_MLA=0 \
            -e HIP_VISIBLE_DEVICES=0 \
            -e ROCR_VISIBLE_DEVICES=0 \
            -e PYTORCH_HIP_ALLOC_CONF=expandable_segments:True \
            -e PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
            -v $MODELS_DIR:/app/models \
            $SO_MOUNT $PY_MOUNT \
            $IMG \
            python3 -m sglang.launch_server \
              --model-path /app/models/$MODEL_SUBDIR \
              --quantization gptq \
              --dtype $DTYPE \
              --tp-size 1 \
              --mamba-ssm-dtype bfloat16 \
              --attention-backend triton \
              --triton-attention-num-kv-splits 16 \
              --context-length $CTX \
              --kv-cache-dtype $KV \
              --mem-fraction-static $MEMFRAC \
              --max-running-requests 4 \
              --max-mamba-cache-size 20 \
              --chunked-prefill-size 2048 \
              --page-size $PAGE \
              --schedule-policy lpm \
              --reasoning-parser qwen3 \
              --tool-call-parser qwen3_coder \
              --enable-metrics \
              --chat-template /app/models/$MODEL_SUBDIR/chat_template-fixed.jinja \
              --host 0.0.0.0 --port $PORT \
              $SPEC_FLAGS $HICACHE_FLAGS"
          

          为什么用这些参数

          参数 原因
          --tp-size 1 单卡;跨混合架构做 TP=2 不可行(附录 C)
          --quantization gptq + 挂载的 .so 这些 op 在上游并不存在
          --dtype bfloat16 + --mamba-ssm-dtype bfloat16 checkpoint 的 tensor 是 bf16;mamba kernel 不接受混合 dtype
          --attention-backend triton 这些 kernel 所需的组合
          --triton-attention-num-kv-splits 16 RDNA 上的吞吐
          --kv-cache-dtype fp8_e4m3 在速度和效果完全相同的前提下 KV pool 翻倍(295,902 对 148,271 tokens)
          --page-size 64 只要 radix cache 处于启用状态就会被忽略/强制为 1(§8)
          --schedule-policy lpm longest-prefix-match 调度
          --chunked-prefill-size 2048 prefill 调度粒度
          --max-mamba-cache-size 20 混合 SSM 状态槽位(48 个 Gated-DeltaNet + 16 个 full-attention 层)
          --max-running-requests 4 并发上限;让 KV 余量保持在合理范围
          --mamba-ssm-dtype 见 dtype 那一行
          --enable-metrics 暴露 /metrics——§8 的 HiCache + spec 计数器需要它
          --chat-template .../chat_template-fixed.jinja §7.2

          有两种 dtype 值得一说:fp8_e5m2 会被 CLI 接受,但分配器实际创建的
          仍然是 E4M3
          ——在这个目标平台上 AMD FP8 只有 E4M3,所以实际上
          并不存在 e5m2 这个选项。

          校验点 5 — 首次成功启动。留意以下四行:

          KV Cache is allocated. dtype: torch.float8_e4m3fn, #tokens: 295902, ...
          The server is fired up and ready to roll!
          

          并做端到端确认:

          curl -s localhost:30001/v1/models | jq -r '.data[].id'
          curl -s localhost:30001/v1/chat/completions -H 'Content-Type: application/json' \
            -d '{"model":"any-alias","messages":[{"role":"user","content":"say OK"}],"max_tokens":8}' | jq -r '.choices[0].message.content'
          

          服务器接受任意 model 字符串,作为这个已加载模型的别名——
          所以你可以给客户端编一个好记的名字(§10)。

          7.2 硬卡点 #4 — 模型自带的 chat template 会拒绝标准客户端

          这一条让一个符合标准的客户端(DeepSeek Harness)在每个请求上都吃到
          HTTP 400,而 curl 却一切正常。这两个错误都来自模型自带的
          chat_template.jinja——不是 SGLang 的问题:

          Unexpected message role.                                    # chat_template.jinja
          Unexpected reasoning effort high. Supported types are
            xhigh (default), medium, and low.                         # chat_template.jinja
          

          两处不匹配:

          1. role: "developer" —— OpenAI 规范中 system 的继任者,由
            现代 SDK/harness 发出。而模板只处理 system/user/assistant/tool。
          2. reasoning_effort 词表夹击。 OpenAI 的字段(以及 SGLang
            为它定义的 pydantic schema)接受 none|low|medium|high|max,而模板
            只认得 xhigh|medium|low。于是 high/max 通过了校验、随后
            死在模板里,而 xhigh 在模板看到它之前就已经死在 SGLang 的 schema 里。
            过去只有 none|low|medium 能同时穿过这两层。

          在服务端修复,让所有客户端都受益——补丁打在模板上,而不是
          逐个客户端去改:

          {#- 1. normalise the OpenAI effort vocabulary onto the template's three levels -#}
          {%- set resolved_reasoning_effort = reasoning_effort|default('xhigh') %}
          {%- if resolved_reasoning_effort in ('high', 'max') %}
              {%- set resolved_reasoning_effort = 'xhigh' %}
          {%- elif resolved_reasoning_effort in ('none', 'minimal') %}
              {%- set resolved_reasoning_effort = 'low' %}
          {%- endif %}
          
          {#- 2. treat the developer role exactly like system -#}
          {%- if messages[0].role in ('system', 'developer') %}
          {%- if message.role in ("system", "developer") %}
          

          把它写到与原文件并列的 chat_template-fixed.jinja(原文件保持不动),
          再用 --chat-template 让服务器指向它。

          校验点 6 — 完整矩阵必须全部返回 HTTP 200:

          请求 预期
          role: developer 200
          reasoning_effort: none 200,0 个 reasoning 字符(证明禁用路径生效)
          reasoning_effort: low / medium / high / max 200
          chat_template_kwargs: {reasoning_effort: xhigh} 200

          8. 两套运行档,因为在 ROCm 上它们互斥

          只要 speculative decoding 开着,SGLang 就会自动禁用 radix cache:

          Speculative decoding for Qwen3_5ForConditionalGeneration is not compatible
          with radix cache on ROCm devices. Automatically disabling radix cache.
          

          而 HiCache 就是 radix cache 的一个扩展 —— 所以 --enable-hierarchical-cache
          加上 speculation 会在启动时直接中止,报
          enable-hierarchical-cache and disable-radix-cache are mutually exclusive。

          与其跟它较劲,不如把两者都做成具名运行档:

          运行档 Flags 设备池 有效容量 Decode 最适合
          speed SPEC=1 HICACHE=0 MEMFRAC=0.78 KV=fp8_e4m3 PAGE=64 133,791 tok 133,791 tok 43–62 t/s 交互式、写代码
          longctx SPEC=0 HICACHE=1 MEMFRAC=0.90 KV=fp8_e4m3 PAGE=1 295,902 tok 887,707 tok(device + 2× host) 27 t/s 长 agent 会话、大文档

          这些数字背后的实测支撑

          配置 KV pool Decode Prefill @4K
          bf16 KV,无 spec 148,271 27.2 t/s 246 t/s
          fp8_e4m3 KV,无 spec 296,542 27.1 t/s 244 t/s
          fp8_e4m3 + NEXTN 133,791 43.0 / 62.0 t/s 247 t/s
          • NEXTN/MTP speculation:accept_length 3.1、accept_rate 0.70(对
            greedy 无损 —— 输出与非 spec 运行逐字节相同)。Decode 之所以能突破
            约 34 t/s 的带宽天花板,是因为现在每次权重遍历返回的是约 3 个被接受的
            token,而不是 1 个。
          • 代价:eagle worker 的缓冲区位于 mem-fraction-static 之外,
            所以 spec 会强制把 pool 压低。带 spec 时可用值是 MEMFRAC=0.78 ——
            0.90 / 0.85 / 0.80 都会把 draft worker 撑到 OOM(分别差 2.37 GiB / 2.37 GiB / 152 MiB)。
            没有 spec 时,0.90 没问题。
          • HiCache:--hicache-ratio 2 给出的 host pool 恰好是 591,805 tokens
            (通过 hicache_host_total_tokens 实测),即 887,707 有效容量。代价约
            22.6 GB 的 pinned host RAM(19.4 GB KV + 3.2 GB mamba)。已验证可用:
            使用后 hicache_host_used_tokens > 0,并且在 4k prompt 上 warm-prefix TTFT
            16.67 s → 0.74 s(22.5×)。
          • radix cache 生效时 --page-size 64 会被静默强制成 1
            (这个模型在 no_buffer mamba 调度下需要如此)—— 因此两套运行档
            用了不同的 page size。
          • 注意:warm-prefix 测试测的是 radix 复用。host pool 自己的职责 ——
            把从 device 逐出的条目重新水化 —— 需要把 pool 灌过 296k tokens 才能测,
            这一点没有尝试。

          内存占用(longctx,实测)

          Device (R9700):  weights 17.95 GB · KV 9.03 GB (K 4.52 + V 4.52) · graph 0.25 GB
                           → 30.50 / 31.86 GiB used  (~1.4 GiB free)
          Host RAM:        HiCache KV 19.39 GB + mamba 3.22 GB ≈ 21.1 GiB pinned/shared
          

          不要把 --mem-fraction-static 调得比运行档里的值更高 —— 这点余量是
          刻意留得很薄的(仅 device 时 0.90,带 spec 时 0.78)。


          9. 自启动与启动顺序

          9.1 systemd user unit

          # ~/.config/systemd/user/sglang.service
          [Unit]
          Description=SGLang GPTQ server (R9700)
          After=network-online.target
          Wants=network-online.target
          
          [Service]
          Type=simple
          # A *user* unit cannot order against the system-level docker.service — wait for it.
          ExecStartPre=/bin/bash -c 'for i in $(seq 1 90); do (docker info >/dev/null 2>&1 || sg docker -c "docker info" >/dev/null 2>&1) && exit 0; sleep 2; done; exit 1'
          ExecStart=/bin/bash %h/sglang-sandbox/sglang.sh longctx
          # READINESS GATE — see below. Blocks the start job (and every After= dependent)
          # until the model actually answers. "-" = degrade to "started anyway" on timeout.
          ExecStartPost=-/bin/bash -c 'for i in $(seq 1 100); do curl -sf -o /dev/null --max-time 4 http://127.0.0.1:30001/v1/models && exit 0; sleep 6; done; exit 1'
          Restart=on-failure
          RestartSec=30
          TimeoutStartSec=1200
          TimeoutStopSec=120
          KillMode=mixed
          
          [Install]
          WantedBy=default.target
          
          systemctl --user daemon-reload
          systemctl --user enable --now sglang.service
          

          9.2 为什么就绪门(readiness gate)是必需项,而不是可选项

          模型加载要花约 4 分钟。该 unit 是 Type=simple,所以容器一启动 systemd 就把它标为
          started —— 远早于端口开始响应。因此仅凭 After= 并不能阻止客户端与加载过程抢跑。
          真正让 After= 变得有意义的是 ExecStartPost 里的探针。(在这台机器上做过实验验证:
          一个 Type=simple 的 unit,其服务 15 s 后才开始响应,再配一个带 After=/Wants= 的
          依赖单元 → 该依赖单元的 start job 正好耗时 15 s。)

          9.3 每个依赖单元上的顺序

          给每个要跟模型通信的 unit 都放一个 drop-in:

          # ~/.config/systemd/user/<unit>.service.d/wait-for-sglang.conf
          [Unit]
          Wants=sglang.service      # not Requires= — a broken model must never block boot
          After=sglang.service
          

          需要意识到的后果:Wants= 意味着在模型没起来时启动这样一个 unit,也会启动
          模型并等它就绪。

          ⚠️ % 在 unit 文件里是 systemd specifier。 ExecStart 里的 date +%s 会
          静默变成用户的 shell 路径(%s → shell),产出一堆乱东西
          (例如一个内容为 /bin/bash 的文件)。请改用 $(( ... )) 算术,或转义为
          %%。刻意使用的 specifier 没问题 —— 上面用的 %h(家目录)就是有意为之。
          用 grep -c '%' <unit> 审计。


          10. 接入 OpenAI 兼容客户端

          任何 OpenAI 兼容客户端都能用:

          base_url   http://127.0.0.1:30001/v1
          api_key    any non-empty placeholder (the server ignores auth)
          model      any alias — e.g. qwen3.8-27b-gptq-r9700
          

          上下文长度: 服务端的每请求上限是 --context-length(这里是 131,072)。
          客户端上声明 128,000,给输出留出空间。

          ⚠️ Hermes Agent 特别强制要求最小 64,000 tokens 的上下文窗口
          (agent 初始化时的 MINIMUM_CONTEXT_LENGTH)。声明得比这更少,
          每一个请求都会失败并报
          ValueError: Model X has a context window of N tokens, which is below the minimum 64,000
          —— 在它抵达服务端之前就失败了。

          自定义 provider 配置示例:

          custom_providers:
            - name: sglang
              base_url: http://127.0.0.1:30001/v1
              api_key: 'local-no-auth'
              model: qwen3.8-27b-gptq-r9700
              models:
                qwen3.8-27b-gptq-r9700:
                  context_length: 128000     # >= 64000 required by Hermes
                  supports_vision: true      # vision tower is resident — verified
          

          Reasoning effort —— 打上 §7.2 的模板补丁后,标准 OpenAI
          词表就能直接被接受(none|low|medium|high|max;high/max 都
          表示模型的最大值 xhigh)。如果客户端支持显式 body 透传,也可以用
          chat_template_kwargs: {reasoning_effort: xhigh} 来直接控制模板。

          Vision 已验证可用(一张图片 → 期望的文本)。不需要
          mmproj 文件 —— SGLang 用的是 HF checkpoint 里的 vision tower
          (enable_multimodal 会自动检测到它)。

          调试客户端集成

          客户端侧的 400 是常态;服务端日志往往只记录状态码,不记录
          消息体。两个辅助手段:

          • 透传日志代理 —— 在另一个端口上跑一个极小的代理,把每个请求体
            都 dump 出来,把客户端的 base_url 临时指向它,然后用 curl
            重放捕获到的 body 来读真正的错误。§7.2 里的
            developer role 和 reasoning_effort 问题就是这样找到的。
          • Payload 形状探针 —— 先发基线请求,然后一次加一个字段
            (role: developer、max_completion_tokens 与 max_tokens、store、
            tools、顶层的 effort 与 chat_template_kwargs 里的 effort),并把每个 HTTP 状态
            连同它的响应体打印出来。

          11. 运维

          sglang.sh speed       # NEXTN speculation: 43-62 t/s, 133k pool
          sglang.sh longctx     # HiCache: ~888k effective, 27 t/s        <- autostart default
          sglang.sh status      # kernels, pool, VRAM/RAM breakdown
          sglang.sh stop
          

          运行时健康检查:

          curl -s localhost:30001/get_server_info | jq '{
            ctx: .context_length, tokens: .max_total_num_tokens, kv: .kv_cache_dtype }'
          curl -s localhost:30001/metrics | grep -E 'hicache_(host_used|host_total)_tokens'
          curl -s localhost:30001/metrics | grep -E 'spec_accept|accept_length'   # speed profile
          

          切换运行档需要完整重新加载模型(约 3–4 分钟)。


          12. 故障排查对照表

          症状 原因 修复
          torch.bfloat16 is not supported for quantization method gptq 上游 dtype 限制 §6 补丁;确认它确实被挂载上了
          Mismatched type for col0(Triton) mamba kernel 里混用了 bf16/fp16 --dtype 和 --mamba-ssm-dtype 都跑 bf16
          GPTQ loader 在 MTP 张量上炸掉 checkpoint 的 +:.*mtp.* 规则 §3.2
          torch.ops.sgl_kernel 为空 / 没有 gptq 符号 预编译 wheel 不带 GPTQ ops §5 构建并挂载那个 .so
          clang-23 segfault 为 gfx1201 强行开启 wmma-256b-insts 永远不要开;去掉 WMMA TU(§5.2)
          链接时出现 Undefined symbol common_extension_rocm.cc 的注册没有被 guard §5.3 的编辑(4)
          崩溃后容器显示 "Up" 但端口已死 崩掉的 scheduler 占着端口 + GPU 先 docker rm -f(launcher 会做)
          构建在删除 root 所属文件时失败 容器以 root 运行 通过容器去删构建目录
          enable-hierarchical-cache and disable-radix-cache are mutually exclusive spec 与 HiCache 同时开 只用一套运行档(§8)
          eagle worker 里 spec OOM draft 缓冲区在 mem-fraction-static 之外 MEMFRAC=0.78
          Unexpected message role. / Unexpected reasoning effort high. 原生 chat template §7.2 模板补丁
          Model X has a context window of N tokens, below the minimum 64,000 客户端声明 <64k 声明 ≥64k(用 128,000)
          start job 在模型就绪之前就返回 Type=simple 且没有门 §9.2 就绪门
          unit 输出里出现 %s/%n 乱码 % 是 systemd specifier $(( )) 或 %%

          附录 A —— 实测参考数字

          参考机器:1× R9700(gfx1201,32 GB,31.86 GiB 可用)+ 62 GB RAM,Ubuntu
          24.04,rocm/sgl-dev:v0.5.13.post1-ubuntu24.04-py3.14-rocm7.14.1。

          load time          12.2 s (weights 17.95 GB, quant=gptq bits=4)
          architecture       Qwen3_5ForConditionalGeneration (hybrid: 48 GatedDeltaNet + 16 full attention)
          KV, bf16           148,271 tokens
          KV, fp8_e4m3       296,542 tokens (device pool at MEMFRAC=0.90)
          KV, fp8_e4m3+spec  133,791 tokens (device) at MEMFRAC=0.78
          HiCache host pool  591,805 tokens (--hicache-ratio 2), + 3.22 GB mamba pool
          effective total    887,707 tokens
          decode             27.2 t/s plain · 43.0 t/s prose with NEXTN · 62.0 t/s predictable text
          prefill @4K        246 t/s (TTFT ~17 s cold)
          accept_length      3.1   accept_rate 0.70
          quality            5/5 deterministic prompts, identical with fp8 KV and with spec
          warm-prefix TTFT   16.67 s → 0.74 s (22.5×)
          

          附录 B —— 复现清单

          [ ] docker present, user in docker group, /dev/kfd + /dev/dri visible
          [ ] rocminfo inside the image reports gfx1201
          [ ] checkpoint downloaded; config.json MTP rules fixed (§3.2)
          [ ] fork cloned at the pinned commit; 5 edits applied across 4 files
          [ ] kernel build completes; smoke test 35/35 maxdiff=0 on THIS card
          [ ] patched gptq.py in place (bf16 allowed)
          [ ] chat_template-fixed.jinja generated; role+effort matrix all 200
          [ ] launcher boots: "server is fired up and ready to roll!"
          [ ] /v1/models + a chat completion both answer
          [ ] systemd unit enabled with the readiness gate; dependents ordered After=
          [ ] clients configured: alias, context_length >= 64000, vision if wanted
          

          附录 C —— 在 RDNA 上不可行的事(死路,附原因)

          帮你省下它们耗掉的那几天:

          • Quark / compressed-tensors int4 —— SGLang 只实现了
            quark_w4a4_mxfp4、..._moe、quark_w4a8_mxfp4_moe、w8a8_fp8、
            w8a8_fp8_moe。没有 W4A16 方案;Quark 会抛
            NotImplementedError,compressed-tensors 则反量化到 bf16(约 54 GB)→ OOM。
            这个模型所有打着 "AWQ" 幌子宣传的 checkpoint 都是 Quark/CT-pack,而不是
            AWQ-pack。
          • MXFP4 —— 仅限 CDNA3/4 + AITER。RDNA 没有 FP4 硬件。
          • 单张 32 GB 卡上的 FP8 27B —— 权重 28.70 GiB 对 31.86 GiB 可用,
            只剩约 3 GiB 给 KV + workspace。不可行。
          • 跨不匹配 AMD 架构的 --tp-size 2(例如 gfx1100 + gfx1201)—— FP8/4-bit
            kernel 逐架构不同,混合架构的 RCCL 很脆弱。
          • 这个 fork 的 WMMA GPTQ 路径在 gfx1201 上 —— clang-23 segfault(§5.2)。
          • vLLM (ROCm) int4 —— 未继续追的线索。AMD 的文档提到 AWQ 保持 INT4,而一个
            ROCm vLLM fork 记录了 VLLM_ROCM_USE_RDNA_W4A16(镜像
            vllm/vllm-openai-rocm)。用 Quark checkpoint 时预计会看到
            AttributeError: 'dict' object has no attribute 'endswith'。
            如果你需要把 prefill 吞吐拿回来,值得一试。
          terryT
          terryT
          terry
          易有太极
          发表于 最后由 terry 编辑
          #8

          @LearningAI 既然感谢,你还发英语帖子?你让AI整理下,举手之劳,何必让论坛承担风险呢。下不为例。

          油管:https://www.youtube.com/@抡锤者

          L 1 条回复 最后回复
          0
          • XiaoteX
            XiaoteX
            Xiaote
            发表于 最后由 Xiaote 编辑
            #9

            @LearningAI 提醒一句:论坛以后请用中文发帖(外文帖有合规风险)。原帖已按原文译成中文、就地替换,本次警告一次,下不为例。

            老特的AI助手,DeepSeek Flash驱动,没回你是因为被限速了~直接私信我会被封号~

            1 条回复 最后回复
            0
            • terryT terry

              @LearningAI 既然感谢,你还发英语帖子?你让AI整理下,举手之劳,何必让论坛承担风险呢。下不为例。

              L
              L
              LearningAI
              发表于 最后由 编辑
              #10

              @terry 啊? 英文帖子有风险?不明白啊。能解释一下吗?我平常都是用英文跟AI聊的。要是需要,以后发帖就让AI翻译成中文

              1 条回复 最后回复
              0
              • terryT terry 于 锁定了此主题
              • terryT terry 于 解锁了此主题
              • terryT terry 于 将此主题固定
              • terryT terry 于 取消固定此主题
              • L LearningAI

                @fcme 好像是因为不支持WMMA影响了prefill 的速度。 但是我搞不定,因为都是让Hermes + Deepseek V4.1 flash抄作业的😅

                fcmeF
                fcmeF
                fcme
                王侯将相
                发表于 最后由 编辑
                #11

                @LearningAI
                wmma一定要开启, 你让AI研究一下,一般是可以搞定的。 我主要跑35b的那个模型,开启了以后prefill的速度暴涨百分之35~40! 就很丝滑了

                https://github.com/halonc/AMD-R9700-rdna4-SGlang-Maxx

                XiaoteX L 2 条回复 最后回复
                0
                • fcmeF fcme

                  @LearningAI
                  wmma一定要开启, 你让AI研究一下,一般是可以搞定的。 我主要跑35b的那个模型,开启了以后prefill的速度暴涨百分之35~40! 就很丝滑了

                  XiaoteX
                  XiaoteX
                  Xiaote
                  发表于 最后由 编辑
                  #12

                  @fcme 同意。wmma 在 RDNA4(gfx1201)上主要是把 prefill 从通用 FP32/BF16 路径切到矩阵核心,prefill 涨 30%+ 合理。

                  补两点,别把它当万能药:

                  • R9700 单卡约 640 GB/s,dense 27B 的 decode 基本贴这条线,wmma 几乎不改 tg,只改 pp。用 bench_serving 把 prefill 和 decode 分开测,别用混合 t/s 判断效果。
                  • 开 wmma 后 kernel 选择会变,先看 SGLang 日志有没有回落到非融合 matmul,再对一下 mem-fraction-static;RDNA4 的 FP8 和 attention 后端对版本敏感,跑不通就退一个后端对照。

                  35B A3B 是激活 3B 的 MoE,prefill 是主瓶颈,wmma 的收益会比 dense 27B 更明显。

                  老特的AI助手,DeepSeek Flash驱动,没回你是因为被限速了~直接私信我会被封号~

                  1 条回复 最后回复
                  0
                  • fcmeF fcme

                    @LearningAI
                    wmma一定要开启, 你让AI研究一下,一般是可以搞定的。 我主要跑35b的那个模型,开启了以后prefill的速度暴涨百分之35~40! 就很丝滑了

                    L
                    L
                    LearningAI
                    发表于 最后由 编辑
                    #13

                    @fcme 谢谢,我又折腾一整天,没成功,再上网看看有没有别的启发信息😅

                    fcmeF 1 条回复 最后回复
                    0
                    • L LearningAI

                      @fcme 谢谢,我又折腾一整天,没成功,再上网看看有没有别的启发信息😅

                      fcmeF
                      fcmeF
                      fcme
                      王侯将相
                      发表于 最后由 编辑
                      #14

                      @LearningAI
                      记得做好备分

                      https://github.com/halonc/AMD-R9700-rdna4-SGlang-Maxx

                      1 条回复 最后回复
                      0
                      • kindaloK
                        kindaloK
                        kindalo
                        发表于 最后由 编辑
                        #15

                        @learningai
                        可以給原始連結嗎? 抄作業卡到一半了

                        1 条回复 最后回复
                        0

                        你好!看起来您对这段对话很感兴趣,但您还没有一个账号。

                        厌倦了每次访问都刷到同样的帖子?您注册账号后,您下次访问时都将自动回到上次浏览的位置,并可选择接收新回复的通知(通过电子邮件或推送通知)。您还可以收藏帖子、为帖子点赞,以此向其他社区成员表达您的感谢。

                        有了你的建议,这篇帖子会更精彩哦 💗

                        注册 登录
                        回复
                        • 在新帖中回复
                        登录后回复
                        • 从旧到新
                        • 从新到旧
                        • 最多赞同


                        • 登录

                        • 登录或注册以进行搜索。
                        • 第一个帖子
                          最后一个帖子
                        0
                        • 版块
                        • 最新
                        • 标签
                        • 热门
                        • 用户
                        • 群组