跳转至内容
  • 版块
  • 最新
  • 标签
  • 热门
  • 用户
  • 群组
皮肤
  • 浅色
  • 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 广场
  1. 主页
  2. 版块
  3. AI硬件
  4. X99 不能P2P让我很难受 。 买Pcie switch(260926死缓改判无罪,交换机完美实现P2P 9月27:TP2 Qwen3.8 Q8 60t/s)

X99 不能P2P让我很难受 。 买Pcie switch(260926死缓改判无罪,交换机完美实现P2P 9月27:TP2 Qwen3.8 Q8 60t/s)

已定时 已固定 已锁定 已移动 精华 热议 AI硬件
x99ai-pro-r9700多卡部署
118 帖子 23 发布者 3.7k 浏览 3 关注中
  • 从旧到新
  • 从新到旧
  • 最多赞同
回复
  • 在新帖中回复
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • 张光璞张 张光璞

    @laobenxiong 说:

    @张光璞 说:
    明天才到上海,到了我就开始折腾。

    好奇: 显卡将来插到那块板子上, 打算咋固定? 还是有配套机箱?

    我先打个测试架,后期我还在规划,计划放在一个10寸的开放式机架上。

    1f838bb4b8deafda331ea4cf145d1c93.jpg

    terryT
    terryT
    terry
    超级版主
    编写于 最后由 terry 编辑
    #90

    @张光璞 我弟你玩的挺深。
    新帖我分割到了:https://lcz.me/topic/1941

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

    1 条回复 最后回复
    0
    • ,terryT terry 分叉了 此主题
    • stakiraS
      stakiraS
      stakira
      德高望重
      编写于 最后由 编辑
      #91

      我也为自己的 x99 主板研究过。有些是可以通过自行修改 BIOS 解决的,而且确实有人玩这个,比如:https://winraid.level1techs.com/t/request-ga-x99-ud4p-bios-with-above-4g-decoding/104550

      张光璞张 1 条回复 最后回复
      0
      • suboyangS suboyang

        客服传话:
        加入了resize BAR功能的是这些型号:X99-4MF 4MT 4MTPLUS 4MFPLUS
        8D3 8D4 AD3 AD4 BD3 BD4
        F8D T8D F8DPLUS M-F M-T
        P4T P4F PD3 PD4

        最新带ARGB的TF T8 也支持ReBAR

        alan.lgv60A
        alan.lgv60A
        alan.lgv60
        编写于 最后由 编辑
        #92

        @suboyang 所以不是所有 X99-TF 都支援 ReBAR 吗?

        suboyangS 1 条回复 最后回复
        0
        • jenaflexJ jenaflex

          我有DELL T7920 (一代、二代Xeon Scalable),它理论上是支持Rebar的,(因为7920 Rack的BIOS里有rebar size选项),但工作站T7920的最新BIOS仍然只有Above4G Decoding,没有Rebar size设置。

          我和你一样,用Linux 命令行看到的bar size是256M

          网上有破解办法:
          https://github.com/xCuri0/ReBarUEFI/discussions/331
          我还没来得及试。

          L
          L
          laobenxiong
          德高望重 劳动模范
          编写于 最后由 编辑
          #93

          @jenaflex 说:

          我有DELL T7920 (一代、二代Xeon Scalable),它理论上是支持Rebar的,(因为7920 Rack的BIOS里有rebar size选项),但工作站T7920的最新BIOS仍然只有Above4G Decoding,没有Rebar size设置。

          我和你一样,用Linux 命令行看到的bar size是256M

          网上有破解办法:
          https://github.com/xCuri0/ReBarUEFI/discussions/331
          我还没来得及试。

          如果有 above 4g, 只是 rebar 不成功, 风险较高的做法是魔改 bios, 风险低(其实就是无风险)的做法是给 kernel 打 patch (把问题描述给 AI, 它帮你搞定). 这里我假定 above 4g以后, /proc/iomem 上看给 prefetchable space留的空间是足够大的 (这个假定在工作站主板上基本是成立的, 至少256G起吧).

          张光璞张 1 条回复 最后回复
          0
          • alan.lgv60A alan.lgv60

            @suboyang 所以不是所有 X99-TF 都支援 ReBAR 吗?

            suboyangS
            suboyangS
            suboyang
            劳动模范
            编写于 最后由 编辑
            #94

            @alan.lgv60

            问客服啊,我是问的华南金牌X79/X99主板,他们打了补丁。

            1 条回复 最后回复
            0
            • kos orK kos or

              這樣一張PCIe外接版 2380元也蠻省的 不用再額外購買一台電腦 就能一個PCIe x 16槽 外接放上四張顯示卡 (x4/x4/x4/x4)
              而且空間夠大 不用擔心顯卡放不下 , 散熱也更好

              L
              L
              laobenxiong
              德高望重 劳动模范
              编写于 最后由 编辑
              #95

              @kos-or 说:

              這樣一張PCIe外接版 2380元也蠻省的 不用再額外購買一台電腦 就能一個PCIe x 16槽 外接放上四張顯示卡 (x4/x4/x4/x4)
              而且空間夠大 不用擔心顯卡放不下 , 散熱也更好

              pex 88096 应该可以支持到四个下游槽都是 gen4x16, 不知道问什么你说x4x4x4x4: "外接放上四張顯示卡 (x4/x4/x4/x4)".

              张光璞张 1 条回复 最后回复
              0
              • Misakiyu XmilkM Misakiyu Xmilk

                所以我能理解成花了兩千五不到只要那東西至少有一槽PCIE就能不管甚麼垃圾板就能跑4卡p2p?

                张光璞张
                张光璞张
                张光璞
                技术大牛 劳动模范
                编写于 最后由 编辑
                #96

                @Misakiyu-Xmilk 说:

                所以我能理解成花了兩千五不到只要那東西至少有一槽PCIE就能不管甚麼垃圾板就能跑4卡p2p?

                主板一定要支持,不然拿不到大BAR

                1 条回复 最后回复
                0
                • stakiraS stakira

                  我也为自己的 x99 主板研究过。有些是可以通过自行修改 BIOS 解决的,而且确实有人玩这个,比如:https://winraid.level1techs.com/t/request-ga-x99-ud4p-bios-with-above-4g-decoding/104550

                  张光璞张
                  张光璞张
                  张光璞
                  技术大牛 劳动模范
                  编写于 最后由 编辑
                  #97

                  @stakira 说:

                  我也为自己的 x99 主板研究过。有些是可以通过自行修改 BIOS 解决的,而且确实有人玩这个,比如:https://winraid.level1techs.com/t/request-ga-x99-ud4p-bios-with-above-4g-decoding/104550

                  DELL 的不行,限制太多了。 华南金牌的那些魔改反而成优势了

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

                    @kos-or 说:

                    這樣一張PCIe外接版 2380元也蠻省的 不用再額外購買一台電腦 就能一個PCIe x 16槽 外接放上四張顯示卡 (x4/x4/x4/x4)
                    而且空間夠大 不用擔心顯卡放不下 , 散熱也更好

                    pex 88096 应该可以支持到四个下游槽都是 gen4x16, 不知道问什么你说x4x4x4x4: "外接放上四張顯示卡 (x4/x4/x4/x4)".

                    张光璞张
                    张光璞张
                    张光璞
                    技术大牛 劳动模范
                    编写于 最后由 编辑
                    #98

                    @laobenxiong 说:

                    @kos-or 说:

                    這樣一張PCIe外接版 2380元也蠻省的 不用再額外購買一台電腦 就能一個PCIe x 16槽 外接放上四張顯示卡 (x4/x4/x4/x4)
                    而且空間夠大 不用擔心顯卡放不下 , 散熱也更好

                    pex 88096 应该可以支持到四个下游槽都是 gen4x16, 不知道问什么你说x4x4x4x4: "外接放上四張顯示卡 (x4/x4/x4/x4)".

                    有pcie 交换机,理论上来讲就不需要那种单槽分割的互联办法 。

                    1 条回复 最后回复
                    0
                    • 张光璞张
                      张光璞张
                      张光璞
                      技术大牛 劳动模范
                      编写于 最后由 编辑
                      #99

                      现在看pcie switch 还是为数不多的多A卡TP 方案。
                      e00a3c00-9879-4871-84dd-384df2196e06.jpeg

                      1 条回复 最后回复
                      0
                      • jenaflexJ jenaflex

                        我有DELL T7920 (一代、二代Xeon Scalable),它理论上是支持Rebar的,(因为7920 Rack的BIOS里有rebar size选项),但工作站T7920的最新BIOS仍然只有Above4G Decoding,没有Rebar size设置。

                        我和你一样,用Linux 命令行看到的bar size是256M

                        网上有破解办法:
                        https://github.com/xCuri0/ReBarUEFI/discussions/331
                        我还没来得及试。

                        张光璞张
                        张光璞张
                        张光璞
                        技术大牛 劳动模范
                        编写于 最后由 编辑
                        #100

                        @jenaflex 说:

                        我有DELL T7920 (一代、二代Xeon Scalable),它理论上是支持Rebar的,(因为7920 Rack的BIOS里有rebar size选项),但工作站T7920的最新BIOS仍然只有Above4G Decoding,没有Rebar size设置。

                        我和你一样,用Linux 命令行看到的bar size是256M

                        网上有破解办法:
                        https://github.com/xCuri0/ReBarUEFI/discussions/331
                        我还没来得及试。

                        有戏我的朋友,刚刚试过了。正在测试。

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

                          @jenaflex 说:

                          我有DELL T7920 (一代、二代Xeon Scalable),它理论上是支持Rebar的,(因为7920 Rack的BIOS里有rebar size选项),但工作站T7920的最新BIOS仍然只有Above4G Decoding,没有Rebar size设置。

                          我和你一样,用Linux 命令行看到的bar size是256M

                          网上有破解办法:
                          https://github.com/xCuri0/ReBarUEFI/discussions/331
                          我还没来得及试。

                          如果有 above 4g, 只是 rebar 不成功, 风险较高的做法是魔改 bios, 风险低(其实就是无风险)的做法是给 kernel 打 patch (把问题描述给 AI, 它帮你搞定). 这里我假定 above 4g以后, /proc/iomem 上看给 prefetchable space留的空间是足够大的 (这个假定在工作站主板上基本是成立的, 至少256G起吧).

                          张光璞张
                          张光璞张
                          张光璞
                          技术大牛 劳动模范
                          编写于 最后由 编辑
                          #101

                          @laobenxiong 说:

                          @jenaflex 说:

                          我有DELL T7920 (一代、二代Xeon Scalable),它理论上是支持Rebar的,(因为7920 Rack的BIOS里有rebar size选项),但工作站T7920的最新BIOS仍然只有Above4G Decoding,没有Rebar size设置。

                          我和你一样,用Linux 命令行看到的bar size是256M

                          网上有破解办法:
                          https://github.com/xCuri0/ReBarUEFI/discussions/331
                          我还没来得及试。

                          如果有 above 4g, 只是 rebar 不成功, 风险较高的做法是魔改 bios, 风险低(其实就是无风险)的做法是给 kernel 打 patch (把问题描述给 AI, 它帮你搞定). 这里我假定 above 4g以后, /proc/iomem 上看给 prefetchable space留的空间是足够大的 (这个假定在工作站主板上基本是成立的, 至少256G起吧).

                          正在测试

                          1 条回复 最后回复
                          0
                          • 张光璞张
                            张光璞张
                            张光璞
                            技术大牛 劳动模范
                            编写于 最后由 编辑
                            #102

                            T7910 PCIE SWITCH 双卡推理续篇:自编 ReBAR 内核吃下 32G 大 BAR、7900XTX 解锁 2000MHz,附一整天的实测数据

                            上篇:《双 7900XTX + PCIe 交换机 + SGLang TP=2 实测与掉卡复盘》《T7910 双卡 AI 推理全记录:从 7900XTX 掉总线到 R9700 打通 P2P》。
                            本篇只写增量:内核层的大 BAR 补丁、限频的真实代价、通信栈为什么吃不到收益、以及一批带口径的速度/时间数据。平台、装机、采购价上篇讲过了,不再重复。

                            鸣谢:
                            @PhoenixRise2026
                            X99 + 双 AMD Radeon AI PRO R9700 跑通跨 Root Port P2P:从内核、RCCL 到 SGLang Qwen3.8 实战

                            @jenaflex 说:
                            网上有破解办法:
                            https://github.com/xCuri0/ReBarUEFI/discussions/331
                            我还没来得及试。

                            @laobenxiong

                            如果有 above 4g, 只是 rebar 不成功, 风险较高的做法是魔改 bios, 风险低(其实就是无风险)的做法是给 kernel 打 patch (把问题描述给 AI, 它帮你搞定). 这里我假> 定 above 4g以后, /proc/iomem 上看给 prefetchable space留的空间是足够大的 (这个假定在工作站主板上基本是成立的, 至少256G起吧).

                            @flyer666
                            魔改SGLANG支持7900XTX 双卡TP TTFT <1s 平均TG 80-100! 4并发TG200/sec
                            https://lcz.me/topic/1532

                            TL;DR

                            事项 结果 一句话
                            上游 ReBAR headroom 补丁自行编进内核 ✅ 成了 双卡同处一台交换机时,两条下游链各拿到精确 32 GiB,P2P 从 False 变 True
                            7900XTX 限频档位 1500 → 2000 MHz ✅ 成了,且收益最大 长文(≈3 万 token)55.85s → 38.39s(−31%),单流稳态 decode 40~52 → 54~75 tok/s
                            amdsmi 补进推理环境 ⚠️ 装上了,但没吃到收益 报错从「amdsmi 未定义」前进到「sgl_kernel 缺算子」,环境层障碍已清、算子层仍缺
                            symm-mem / multimem 通信优化 ❌ 此路不通 SGLang 在 world_size=2 时主动禁用 multimem,启动参数默认全 False
                            上游 PR #34058(prefill/decode 公平性)回移 ✅ 移植成功、参数生效 同一个短请求的最大断流间隔 47.58s → 5.06s(−89.4%):默认配置下,一个 4.8 万 token 的 prefill 会让已经解码起来的请求整段停摆
                            nvtop 显存看板 ✅ 修好了 3.0.2 的 fdinfo 断言 bug(同一 client_id 被两个 fd 引用),换新版解决
                            跨根口 P2P(R9700 ↔ 7900XTX) ❌ 硬件限制 can_device_access_peer=False,新旧内核同值,与 BAR 无关
                            交换机路径带宽 ⚠️ 有天花板 卡↔交换机是 Gen4 x16,但内部部分跳只跑 Gen2 x8

                            给赶时间的人:这一天里唯一"一改就见效、幅度又大"的是限频档位(−31% 长文时延);最费劲、看起来最"高级"的大 BAR / 自定义 All-Reduce,收益要么体现在"能不能跑"、要么干脆吃不到。先量再改。


                            一、今天想解决的三件事(思路,不是结论)

                            1. 大 BAR 与 P2P 是两件事

                            上篇的结论是"P2P 通了",但当时有个没解释的怪现象:卡片报的 BAR 只有 256M,而 P2P 时灵时不灵。这里要分三层看:

                            • 能不能寻址到对方显存(P2P):由芯片组/拓扑/内核白名单决定;
                            • BAR 窗口够不够大(ReBAR):决定大块 DMA 能不能一次性映射过去,还是被切成一堆小块来回搬运;
                            • 内核愿不愿意在启动时把预留窗口让出来:这是最容易忽略的一层——如果 PCI 子系统在枚举时就把 prefetchable 窗口"分干净"了,后面再想要 32G 就没有余地了。

                            → 所以先做了对照:同一张卡,旧内核 256M + P2P False;新内核 32G + P2P True。同一硬件、同一槽位,只换内核,这就是判据。

                            2. "防跳闸"的限频,代价是整整 1.9 倍

                            上篇里有一条限功率服务,注释写得很清楚:压缩瞬态电流尖峰、防市电跳闸。它把 SCLK 压到 1500MHz。于是出现了本文最容易被误判的一段:"同一个服务,性能只有上篇的一半"——一度怀疑是交换机、大 BAR、P2P 没生效。

                            真凶是限频:负载中实测 sclk = 1233 MHz(默认 boost 2371MHz ⇒ 1.9× 差距),与"100 → 50 tok/s"精确对应。方向选错,会把一小时耗在错误的地方。

                            3. 通信优化的顺序:环境 → 算子 → 拓扑

                            一厢情愿的想法是"装上 amdsmi + aiter,打开 symm-mem,就能吃 PCIe All-Reduce 的收益"。实际打下来是三层:

                            1. 环境层(可以自己解决):缺 amdsmi / aiter;
                            2. 算子层(要重编):fork 编译出来的 sgl_kernel 扩展里没有对应算子;
                            3. 拓扑层(改不了):SGLang 在 world_size=2 时主动关掉 multimem。

                            三层里只有第一层能自己搞定——这就是"装了却没收益"的原因。


                            二、速度与时间总账

                            统一口径(论坛上最容易被挑的就是裸数字):

                            • 量化 gptq、dtype bfloat16、--attention-backend triton、TP=2(两张 7900XTX,同挂一台 PCIe 交换机);
                            • 投机解码 开:EAGLE,num-steps 3、num-draft-tokens 4;
                            • 上下文上限 196,608 token,KV 池 242,854 token,mem-fraction-static 0.91,max-running-requests 4;
                            • 显卡限频档:SCLK ≤ 2000MHz / VDDGFX −100mV(本文新常态);
                            • 每次测量前 POST /flush_cache,且每条 prompt 用独立前缀构造 ⇒ 全部是冷 prefill,不含前缀缓存命中;
                            • prefill 速度口径 = prompt token 数 ÷ TTFT(服务空闲,TTFT ≈ prefill 时间);
                            • decode 速度口径 = (输出 token 数 −1) ÷ (末 token 时刻 − 首 token 时刻),稳态解码,不含 TTFT;
                            • ⚠️ decode 那一列是「整段输出的平均」(每条约 100~200 个输出 token),含最初几十个 token 的爬坡,所以比服务端自己报的瞬时值低:服务端 gen throughput 中位 52.1、峰值 75.5 tok/s(见 2.4),而同一台机整段平均出来是 28.7(1K)→ 15.2(128K)。两个数都对,口径不同;
                            • 并发测试每条 prompt 独立,且强制长输出(否则模型早退 EOS,"总 token ÷ 墙钟"算出的聚合吞吐会被输出长度污染)。

                            2.1 冷启动

                            口径:从 journalctl 的 Started 事件(秒级时间戳),到引擎日志打出 ready to roll 的时间差。不用"起服务后轮询健康检查"计时——旧进程还在退出时健康检查可能已返回 200,会给出"20 秒就绪"这种假读数(本文实测踩到过)。

                            今天 5 次真实重启(含改动不同配置):

                            次 Started → ready 冷启动
                            1 13:50:14 → 13:51:27 73 s
                            2 13:57:11 → 13:58:25 74 s
                            3 14:05:57 → 14:07:10 73 s
                            4 14:13:49 → 14:15:01 72 s
                            5 14:18:57 → 14:20:10 73 s

                            结论:稳定在 72~74 秒(同一台机、同一模型、同一配置族,波动仅 ±1 秒——说明启动时间是被固定阶段的串行耗时决定的)。

                            其中一次的阶段拆解(日志原文时间戳):

                            阶段 耗时 说明
                            权重加载(主模型) 7.01 s Load weight end. elapsed=7.01 s, quant=gptq, bits=4
                            权重加载(TP 分片) 0.99 s 第二片
                            捕获 target verify CUDA graph 9.03 s bs=[1,2,4]、num_tokens_per_req=4
                            捕获 draft decode CUDA graph 3.95 s 投机解码的草稿图
                            其余(导入 / 分布式初始化 / 编译) ≈ 50 s 占大头,且与配置无关

                            注意:权重加载只要 ~8 秒、图捕获 ~13 秒,冷启动的大头是框架自身的导入与分布式初始化(≈50 秒)。也就是说换更快的盘对冷启动帮助有限。

                            2.2 上下文长度 → TTFT / prefill / decode(单流)

                            目标长度 实测 prompt token TTFT (s) prefill (tok/s) decode (tok/s) 实测输出 token
                            1K 1,023 0.853 1,199 28.71 184
                            4K 4,095 2.968 1,380 28.18 179
                            8K 8,191 6.138 1,334 27.51 200
                            16K 16,383 13.142 1,247 26.02 172
                            32K 32,767 29.586 1,108 23.55 179
                            64K 65,535 72.401 905 19.73 171
                            128K 127,999 191.216 669 15.19 167

                            怎么读这张表:

                            • prefill 峰值出现在 4K 附近(1,380 tok/s),到 128K 掉到 669 tok/s(−52%)——上下文越长,单块的注意力开销越大;
                            • TTFT 与上下文近似线性:1K 用 0.85 s,128K 要 191 s(≈3 分 11 秒)——这是「长上下文首字延迟」最直观的一个数;
                            • decode 随上下文单调下滑:28.7 → 15.2 tok/s(几乎腰斩),这是 KV 越长、每步注意力越贵的必然结果;
                            • 重复性:本节与首轮独立测量(首轮输出长度仅 ~33 token、含较多爬坡)的 TTFT 几乎逐点重合——0.823/0.853、2.970/2.968、6.131/6.138、29.583/29.586、72.420/72.401、191.308/191.216,差在千分位。说明这台机上的 prefill 时间是可复现的确定性量。

                            ⚠️ decode 口径说明:本节 decode = 「整段输出的平均」((n−1)÷(末−首)),含最初 ~30 个 token 的爬坡,因此低于服务端稳态读数。服务端 gen throughput 的同期中位是 52.1 tok/s、最大 75.5 tok/s(见 2.4)。两个口径都对,只是一个含爬坡、一个是稳态瞬时值,别混着比。

                            ![上下文长度对 TTFT 与 prefill/decode 速度的影响]
                            fig1-上下文扫描.jpeg

                            2.3 多并发

                            并发数 墙钟 (s) 聚合吞吐 (tok/s) TTFT 中位 (s) TTFT 最大 (s) 单流 decode 中位 (tok/s)
                            1 8.21 23.4 1.53 1.53 28.59
                            2 11.59 32.6 2.22 2.96 20.23
                            4 17.72 45.3 4.41 5.91 15.34
                            8 33.39 45.0 11.97 22.57 14.48

                            怎么读这张表:

                            • 聚合吞吐 23.4 → 32.6 → 45.3,到 8 并发时卡在 45.0 不再增长——因为 max-running-requests=4:第 5~8 个请求只能排队;
                            • 排队的代价写在时延列:TTFT 中位 1.53 s → 11.97 s,最大 22.57 s,而吞吐一点没多;
                            • 单流 decode 随并发下降(28.6 → 14.5),是权重带宽被多流瓜分的正常结果;
                            • 结论:这台双卡机的甜点就是 4 并发——再多只涨延迟不涨吞吐。

                            ⚠️ 本节踩坑(值得单独记一笔):第一版并发测试用「填充文本 + 让它随便续写」,结果模型很快 EOS、每次只出 ~33 个 token,反推出来的聚合吞吐只有 12~17 tok/s(比单流稳态 52 还低,明显不合理)⇒ 整张表作废。改成「只输出数字、从 1 数到 512」强制长输出后重测,才得到上表。测聚合吞吐,输出长度必须可控,否则你测的是 EOS 什么时候来。

                            ![并发扩展]
                            fig2-并发扩展.jpeg

                            2.4 投机解码(EAGLE)实况

                            指标 数值
                            accept len 中位 2.35
                            accept rate 中位 0.45
                            样本数 33 条 decode 日志
                            服务端 gen throughput 中位 / 最大 52.1 / 75.5 tok/s

                            怎么读:accept len = 每次投机步平均被接受几个 token(上限是 draft 4 个),accept rate = 被接受的比例。中位 2.35 / 0.45 说明还有余量——理论上把接受率从 0.45 提到 0.6,decode 还能再上台阶。另外注意 2.5 节里 SCLK 2000 档的「稳态 54~75 tok/s」与此处的服务端读数互相印证。

                            2.5 限频档位对照(同一服务、同一基准)

                            档位 短文 长文(≈3 万 token) 稳态 decode 峰值功耗 峰值结温
                            SCLK ≤ 1500 / −100mV 4.51s 55.85s 40~52 tok/s 179 W —
                            SCLK ≤ 2000 / −100mV 3.57s 38.39s 54~75 tok/s 242 W 77 °C
                            SCLK ≤ 2000(持续满载压测,896 条采样) — — — 250 / 253 W 84 / 87 °C

                            读法:短促的基准把结温顶到 77 °C,但把上下文扫描+并发连续压满之后,结温会到 84~87 °C(两张卡分别 84 与 87)。离我在遥测里设的 95 °C 急停线只剩 8 °C——这说明"限频防跳闸"之外,散热余量也该被当成一个正经指标来测,光看一两轮短基会是会误判的。
                            读写口径:峰值来自 0.5 秒间隔的 sysfs 遥测(本轮 896 条采样),毫秒级瞬态尖峰看不到——所以"这轮没跳闸"不能推出"尖峰不存在"。

                            ![限频档位对照]
                            fig3-限频对照.jpeg


                            三、三个反直觉的发现

                            1. 大 BAR 的收益不在带宽,在"能不能分配"

                            把补丁前后的数值摆在一起就看清了:

                            测量项(双卡同交换机) 旧内核 新内核
                            XTX 的 BAR Region 0 256M 32 GiB
                            can_device_access_peer False True
                            AllReduce 256MB(RCCL) 2.33 GB/s 7.04 GB/s
                            对等拷贝 1.65 GB/s 3.44 GB/s
                            H2D / D2H 6.04 / 6.82 GB/s 6.04 / 6.82 GB/s(不变)

                            两处最能说明问题:H2D 完全不随内核变(它走的是卡↔交换机最后一跳,Gen2 内部跳,与 BAR 无关);而 P2P 的 AllReduce 直接翻了 3 倍。⇒ 大 BAR 解决的是"能不能把大块一次性映射过去",不是"把带宽硬件加宽"。

                            2. "排队 TTFT" 与 "解码被饿死" 是两码事

                            做公平性实验时踩了个坑,值得写下来:

                            • 先发一条 2.5 万 token 的大 prefill,再发一条短请求 → 短请求 TTFT = 21.5 秒;
                            • 这时去看它的 token 间隔,只有 0.045 秒。

                            原因是短请求还没轮到自己的 prefill,它在排队——而 prefill/decode 公平性参数(--max-consecutive-prefill-batches)管的是已经在解码的请求别被连续 prefill 饿死(has_runnable_decode 为真时才介入)。指标选错,会得出"参数没用"的错误结论。正确的测法:让短请求先解码起来,再注入冷的巨大 prefill,然后量它的 token 间隔。

                            3. 断流对照实测:把「这个参数到底管什么」直接量出来

                            实验设计:先让一条短请求解码起来(数数字),发出 8 个 token 后,注入一条 48,000 token 的冷 prefill(每次测量前清缓存),然后量短请求流式输出里的 token 间隔。

                            配置 超过 1 秒的间隔 最大间隔
                            N=0(SGLang 默认,prefill 优先) 1 个:47.58 s 47.58 s
                            N=1(连续 1 批 prefill 后强制一次 decode) 12 个:3.17~5.06 s(全部有界) 5.06 s

                            最大间隔 47.58 s → 5.06 s(−89.4%)。lcz 帖同期独立实测是 47.23 s → 4.04 s(−91.4%)——同向、同量级,两边都是同一类机器、同一机制。

                            ★ 为什么这份数据可信:机制在数字里自洽

                            • 大 prefill 用量 = 48,000 token ÷ 单块 chunk 4,096 = 11.7 块;
                            • N=1 实测恰好 12 个「>1 秒」的间隔——间隔个数与 chunk 个数吻合;
                            • 每个间隔 3.17~5.06 s,而该轮实测 prefill 48.58 s ÷ 11.7 块 ≈ 4.15 s/块——间隔长度与单块耗时吻合(N=0 那轮 prefill 是 48.07 s,两块数字各自独立取,不混用)。

                            ⇒ 这个参数做的事不是「提速」,而是在每块 prefill 之后让出一次 decode 窗口。数量级、节奏、边界全部对得上,不是跑分抖动。

                            ![断流对照:N=0 vs N=1]
                            fig4-断流对照.jpeg

                            ⚠️ 口径提醒(很容易被误读):上表里两种配置的短请求 TTFT 几乎一样(0.339 s)——因为它是先解码起来的那个请求。如果反过来「大请求先发」,短请求 TTFT 会飙到 21.5 s:那是排队等自己的 prefill,与「解码被饿死」是两个不同机制。--max-consecutive-prefill-batches 的判据是 has_runnable_decode 为真才介入,只保护已经在解码的请求。指标选错,就会得出「参数没用」的假结论。


                            四、还没打通的墙(含推测标注)

                            墙 证据 判断
                            跨根口 P2P(R9700 ↔ XTX) can_device_access_peer=False,新旧内核同值 硬件/芯片组限制,与 BAR 无关(已证实)
                            交换机内部跳数 部分跳 Gen2 5GT/s / x8,H2D 仅 6.0/6.8 GB/s 推测:交换芯片内部降速,非配置问题;待换直连槽对照验证
                            自定义 All-Reduce Setup Custom allreduce failed: '_OpNamespace' 'sgl_kernel' object has no attribute 'meta_size' fork 算子层缺口(需重编 sgl-kernel);amdsmi 已就位
                            multimem multimem all-gather disabled (no multicast for world_size=2) 推测:既受 world_size=2 闸门限制,也可能与 RDNA3 无 multicast 有关;待以 world_size>2 或换卡验证
                            掉电史 撤独立供电后曾多次整机掉电 未根除,靠限频+遥测+看门狗长期观察积累信心

                            五、给后来人的清单

                            买之前

                            • 想跑多卡 TP:先确认
                              主板/交换机的 PCIe 拓扑与 BAR 能力,别只看卡;
                            • 要 P2P,优先
                              直连根口;经交换机能通,但带宽和 BAR 都会打折。

                            装机时

                            • 大 BAR 不是"打开一个选项"就行,
                              内核的 prefetchable 窗口预留策略才是关键;
                            • 动卡前断电,开机顺序:交换机 + 显卡独立供电先上电,再开主机。

                            调优时

                            • 先量再改:任何"性能只有一半"的结论,先去读负载中的实际频率/功耗;
                            • 限频/降压可以用,但要清楚它压的是
                              瞬态电流尖峰,代价可能远超预期;
                            • 测卡对卡带宽
                              只用 RCCL,PyTorch 的 copy_ + Event 计时在 ROCm 上给的是荒谬数;
                            • 每次基准前清缓存(
                              /flush_cache)+ 每条 prompt 独立前缀,否则测的是缓存命中不是 prefill。

                            排障时

                            • 只用一个设备可见性变量(多变量叠加会二次过滤,直接变成 0 张卡);
                            • 就绪判据只认健康检查 200,别 grep 日志字符串(会变);
                            • 看板崩了先怀疑看板本身(本文的 nvtop 就是它自己的 bug,不是卡出问题)。

                            附:给 AI / 爬虫的结构化数据段

                            本节面向自动化抓取与二次分析,字段尽量保持稳定的键值格式。

                            A1. 平台与软件栈

                            machine_model      : Dell Precision Tower 7910
                            bios               : A34 (2020-10-19)
                            cpu                : 2 × Intel Xeon E5-2683 v4 @ 2.10GHz
                            ram                : 125 GiB
                            gpu_a              : 2 × AMD Radeon AI PRO R9700 (gfx1201 / RDNA4, 32624 MB, 直连根口)
                            gpu_b              : 2 × AMD Radeon RX 7900 XTX (Navi 31 / gfx1100, 24560 MB, 同挂一台 PCIe 交换机)
                            rocm               : 7.2.0
                            kernel_running     : 7.0.14-rebar(自编,= 7.0.14-p2pdma + 上游 ReBAR headroom 补丁)
                            secure_boot        : disabled
                            switch             : Broadcom PEX880xx 系列,独立供电(台达 1500W)
                            

                            A2. 内核补丁(关键)

                            patch_title        : PCI: Reserve prefetchable window headroom for Resizable BARs
                            author             : Geramy Loveless
                            patchwork_series   : 520756 (v2 2026-08-28 / v3 2026-08-31)
                            size               : 5 files, +198 / -0
                            new_kernel_options : pci=no_rebar_reserve  (默认开), pci=no_bar_demote (默认开)
                            upstream_test_env  : ASUS K14PG-D24 / 2× EPYC 9354 / 8× Radeon AI PRO R9700 / Broadcom PEX890xx
                            手工修补           : hunk #4(7.0.14 把 pci_min_window_alignment 改名为 window_alignment)
                            

                            A3. BAR / P2P 实测(决定性对照)

                            # 同一硬件、同一槽位,仅换内核
                            旧内核 (7.0.14-p2pdma):
                              xtx_bar_region0           : 256M
                              root_bridge_a0:02.0       : 770M
                              upstream_a1:00.0          : 258M
                              xtx_p2p (can_access_peer) : false
                              allreduce_256MB_rccl      : 2.33 GB/s
                              peer_copy                 : 1.65 GB/s
                            新内核 (7.0.14-rebar):
                              xtx_bar_region0           : 32 GiB
                              root_bridge_a0:02.0       : 64 GiB
                              upstream_a1:00.0          : 64 GiB
                              下游链 ×2                 : 各精确 32 GiB
                              xtx_p2p                   : true
                              allreduce_256MB_rccl      : 7.04 GB/s
                              peer_copy                 : 3.44 GB/s
                              h2d / d2h                 : 6.04 / 6.82 GB/s(两版相同)
                            双 R9700(直连根口,rebar 内核):
                              allreduce_256MB_rccl      : 7.186 GB/s
                              peer_copy                 : 6.19 GB/s
                              h2d / d2h                 : 12.47 / 13.27 GB/s
                              can_device_access_peer    : true / true
                            跨根口:
                              R9700 <-> 7900XTX         : false(新旧内核同值;not supported by the chipset ×8)
                            

                            A4. 服务启动参数(原文,路径已占位)

                            python -m sglang.launch_server
                              --max-consecutive-prefill-batches 1        # 本篇从上游 PR #34058 回移的新参数
                              --model-path /models/qwen3.8-27b-mtp-fixed
                              --host 0.0.0.0 --port 8082
                              --served-model-name qwen3.8-27b-p2p
                              --tp-size 2
                              --quantization gptq
                              --dtype bfloat16
                              --mamba-ssm-dtype bfloat16
                              --kv-cache-dtype auto
                              --attention-backend triton
                              --context-length 196608                    # 192K
                              --mem-fraction-static 0.91
                              --max-running-requests 4
                              --max-mamba-cache-size 20
                              --speculative-algorithm EAGLE
                              --speculative-draft-model-path /models/qwen3.8-27b-mtp-fixed   # 同权重当草稿模型(MTP 头)
                              --speculative-num-steps 3
                              --speculative-eagle-topk 1
                              --speculative-num-draft-tokens 4
                              --cuda-graph-bs-decode 1 2 4
                              --triton-attention-num-kv-splits 16
                            

                            由 systemd 承载(路径占位):

                            [Unit]
                            Description=SGLang (2× 7900 XTX, TP=2) :8082
                            Wants=gpu-clk-limit.service          # 必须先让限功率生效,再加载模型
                            After=gpu-clk-limit.service
                            
                            [Service]
                            Type=simple
                            User=<USER>
                            WorkingDirectory=<SGLANG_DIR>
                            ExecStart=<SGLANG_DIR>/start_server.sh
                            Restart=on-failure
                            RestartSec=15
                            TimeoutStartSec=1800
                            StandardOutput=append:<SGLANG_DIR>/service.log
                            StandardError=append:<SGLANG_DIR>/service.log
                            LimitMEMLOCK=infinity
                            
                            [Install]
                            WantedBy=multi-user.target
                            

                            A5. 环境变量(启动脚本内 export 全量)

                            export PYTHONUNBUFFERED=1
                            export NCCL_DEBUG=WARN
                            export SGL_DTYPE=bfloat16
                            export SGL_PHASE_TIMING=0
                            export USE_HUB_KERNELS=0
                            # RDNA3 fork 相关开关(下方带前缀的成对出现,是 fork 里两套命名都要给)
                            export SGLANG_OPT_USE_CUSTOM_ALL_REDUCE_V2=1
                            export SGLANG_RDNA_CUSTOM_AR=0
                            export SGL_RDNA_CUSTOM_AR=0
                            export SGLANG_RDNA_NO_FUSED=1
                            export SGLANG_RDNA_GEMMA_TRITON=1
                            export SGLANG_RDNA_VLLM_VERIFY=1
                            export SGL_RDNA_NO_FUSED=1
                            export SGL_RDNA_GEMMA_TRITON=1
                            export SGL_RDNA_VLLM_VERIFY=1
                            # kfd event-age 修复 shim(fork 自带,用 LD_PRELOAD 注入)
                            SHIM=<SGLANG_DIR>/scripts/rdna_ar/vendor/kfd_event_age_fix.so
                            [ -f "$SHIM" ] && export LD_PRELOAD="$SHIM:${LD_PRELOAD:-}"
                            

                            说明:SGLANG_RDNA_CUSTOM_AR=0 是主动关掉作者那套 PCIe All-Reduce 的开关——本篇实测证明它在当前环境下起不来(缺算子),关掉可避免误导性告警。

                            A6. 限频/OD 设置

                            gpu_clk_limit_service : SCLK <= 2000 MHz, VDDGFX -100mV(两张 7900XTX)
                            dropin                : gpu-clk-limit.service.d/sclk2000.conf  (ExecStart override)
                            回退                  : rm sclk2000.conf && systemctl daemon-reload
                            R9700 稳定性服务       : 210 W / SCLK-500MHz / VDD-100mV
                            读回校验               : OD_SCLK: 2000Mhz, OD_VDDGFX_OFFSET: -100mV
                            

                            A7. 基准原始数据

                            断流对照(冷 48,000 token prefill 注入,短请求先解码起来):

                            {
                              "E_starvation_E0": {
                                "big_prompt_tokens": 48000, "big_prefill_s": 48.07, "small_ttft_s": 0.339,
                                "small_n_out": 29, "max_gap_s": 47.58, "median_gap_s": 0.034, "gaps_over_1s": [47.58]
                              },
                              "E_starvation_E1": {
                                "big_prompt_tokens": 48000, "big_prefill_s": 48.58, "small_ttft_s": 0.34,
                                "small_n_out": 28, "max_gap_s": 5.062, "median_gap_s": 0.137,
                                "gaps_over_1s": [3.364, 3.173, 3.392, 3.610, 3.817, 4.025, 4.230,
                                                 4.439, 4.648, 4.858, 5.062, 3.517]
                              }
                            }
                            

                            上下文扫描与并发(强制输出 512 数字后重跑,格式同上;本段由脚本直接产出 JSON):

                            {
                              "B2_context_sweep": [
                                {"target": 1024,   "prompt_tokens": 1023,   "ttft_s": 0.853,   "prefill_tps": 1199, "decode_tps": 28.71, "n_out": 184},
                                {"target": 4096,   "prompt_tokens": 4095,   "ttft_s": 2.968,   "prefill_tps": 1380, "decode_tps": 28.18, "n_out": 179},
                                {"target": 8192,   "prompt_tokens": 8191,   "ttft_s": 6.138,   "prefill_tps": 1334, "decode_tps": 27.51, "n_out": 200},
                                {"target": 16384,  "prompt_tokens": 16383,  "ttft_s": 13.142,  "prefill_tps": 1247, "decode_tps": 26.02, "n_out": 172},
                                {"target": 32768,  "prompt_tokens": 32767,  "ttft_s": 29.586,  "prefill_tps": 1108, "decode_tps": 23.55, "n_out": 179},
                                {"target": 65536,  "prompt_tokens": 65535,  "ttft_s": 72.401,  "prefill_tps": 905,  "decode_tps": 19.73, "n_out": 171},
                                {"target": 128000, "prompt_tokens": 127999, "ttft_s": 191.216, "prefill_tps": 669,  "decode_tps": 15.19, "n_out": 167}
                              ],
                              "C2_concurrency": [
                                {"level": 1, "wall_s": 8.21,  "agg_tps": 23.4, "ttft_med": 1.53,  "ttft_max": 1.53,  "decode_med": 28.59},
                                {"level": 2, "wall_s": 11.59, "agg_tps": 32.6, "ttft_med": 2.22,  "ttft_max": 2.96,  "decode_med": 20.23},
                                {"level": 4, "wall_s": 17.72, "agg_tps": 45.3, "ttft_med": 4.41,  "ttft_max": 5.91,  "decode_med": 15.34},
                                {"level": 8, "wall_s": 33.39, "agg_tps": 45.0, "ttft_med": 11.97, "ttft_max": 22.57, "decode_med": 14.48}
                              ],
                              "D_speculative": {"samples": 33, "accept_len_median": 2.35, "accept_rate_median": 0.45,
                                                "gen_throughput_median": 52.1, "gen_throughput_max": 75.5},
                              "peak_telemetry": {"samples": 896, "power_w": [250.0, 253.0], "junction_c": [84.0, 87.0]}
                            }
                            

                            关于本节的 JSON:B2 那一段是从脚本的标准输出逐列重建的(数值即运行时打印值)。原因是采集脚本每完成一段就覆写整个结果文件,跑到最后只剩最后一段——这是"测量脚本必须合并写入而非覆写"的现成反例,已记进我自己的踩坑清单。C2 / D / 遥测三段是脚本直接落盘的原始 JSON。

                            冷启动(journalctl Started → 引擎 ready to roll,5 次):

                            13:50:14->13:51:27 = 73s
                            13:57:11->13:58:25 = 74s
                            14:05:57->14:07:10 = 73s
                            14:13:49->14:15:01 = 72s
                            14:18:57->14:20:10 = 73s
                            

                            A8. 日志关键行(原文)

                            # 通信路径(本篇的核心结论之一)
                            [AR] All-reduce call path: NCCL (custom AR disabled)
                            [AR] Aiter custom all-reduce not available; falling back to sglang CustomAllreduce. Details: No module named 'aiter'
                            Setup Custom allreduce failed with '_OpNamespace' 'sgl_kernel' object has no attribute 'meta_size'. To silence this warning, specify --disable-custom-all-reduce explicitly.
                            multimem all-gather disabled (no multicast for world_size=2)
                            
                            # 启动阶段(时间戳见 A7)
                            Load weight end. elapsed=7.01 s, type=Qwen3_5ForConditionalGeneration, quant=gptq, bits=4, avail mem=14.43 GB, mem usage=9.12 GB.
                            Load weight end. elapsed=0.99 s, type=Qwen3_5ForCausalLMMTP, quant=gptq, bits=4, avail mem=11.59 GB, mem usage=2.82 GB.
                            Capture target verify CUDA graph begin. backend=full, num_tokens_per_req=4, bs=[1, 2, 4], avail mem=4.50 GB
                            Capture target verify CUDA graph end. elapsed=9.03 s, mem usage=0.44 GB
                            Capture draft decode CUDA graph end. elapsed=3.95 s, mem usage=0.29 GB
                            max_total_num_tokens=242854, chunked_prefill_size=4096, max_prefill_tokens=16384, max_running_requests=4, context_len=196608, available_gpu_mem=3.76 GB
                            The server is fired up and ready to roll!
                            
                            # 运行期(吞吐与投机解码)
                            Prefill batch, #new-seq: 1, #new-token: 4096, #cached-token: 0, ..., input throughput (token/s): 621.40
                            Decode batch, #running-req: 1, #full token: 484, ..., accept len: 2.45, accept rate: 0.48, cuda graph: True, gen throughput (token/s): 71.28
                            

                            注:Setup Custom allreduce failed with ... meta_size 这一行在装上 amdsmi 之后才出现——装之前报的是 name 'amdsmi_shut_down' is not defined。报错换了一行,说明卡点从环境层推进到了算子层,这是本篇判断"收益吃不到"的直接证据。

                            A9. 复现命令

                            # 冷启动计时
                            time (systemctl restart sglang-p2p.service; until curl -sf http://127.0.0.1:8082/health; do sleep 1; done)
                            
                            # 单流上下文扫描(每次清缓存,避免命中 radix 前缀缓存)
                            curl -s -X POST http://127.0.0.1:8082/flush_cache -d '{}' -H 'Content-Type: application/json'
                            curl -s http://127.0.0.1:8082/v1/completions -H 'Content-Type: application/json' \
                              -d '{"model":"<served-name>","prompt":"<unique-prefix text>","max_tokens":128,"stream":true}'
                            
                            # 卡间带宽(只用 RCCL)
                            torchrun --nproc_per_node=2 allreduce_bench.py --size-mb 256
                            

                            A10. 本轮移植的上游 PR

                            pr                 : sgl-project/sglang#34058
                            title              : scheduler: bound consecutive prefills to protect decode
                            author             : atbe
                            status             : OPEN(未合并)
                            new_flag           : --max-consecutive-prefill-batches N   (默认 0 = 保持 prefill 优先)
                            semantics          : 仅在 has_runnable_decode 为真时介入;连续 N 个 prefill 批次后强制一个 decode 轮
                            fixes              : issue #32549(持续 chunked-prefill 下 decode 被饿到每 24s 才 1 个 batch)
                            移植要点            : fork 的 server_args 已重构为 arg_groups/{fields,validation_hook,field_order},
                                                 PR 的 hunk 完全对不上;新增文件可原样落地,其余按 fork 自身写法手工落点
                            
                            L 1 条回复 最后回复
                            1
                            • 张光璞张
                              张光璞张
                              张光璞
                              技术大牛 劳动模范
                              编写于 最后由 编辑
                              #103

                              测试环境

                              aed3d331e9215360e3ab3946773aaec8.jpg

                              BunseiB 1 条回复 最后回复
                              0
                              • 张光璞张 张光璞

                                测试环境

                                aed3d331e9215360e3ab3946773aaec8.jpg

                                BunseiB
                                BunseiB
                                Bunsei
                                技术大牛 劳动模范
                                编写于 最后由 编辑
                                #104

                                @张光璞 说:

                                测试环境

                                aed3d331e9215360e3ab3946773aaec8.jpg

                                看到我最期待的环节了~ 不过说实话,我决定后面要换平台了。从am5换成老的epic。

                                张光璞张 1 条回复 最后回复
                                0
                                • 张光璞张 张光璞

                                  T7910 PCIE SWITCH 双卡推理续篇:自编 ReBAR 内核吃下 32G 大 BAR、7900XTX 解锁 2000MHz,附一整天的实测数据

                                  上篇:《双 7900XTX + PCIe 交换机 + SGLang TP=2 实测与掉卡复盘》《T7910 双卡 AI 推理全记录:从 7900XTX 掉总线到 R9700 打通 P2P》。
                                  本篇只写增量:内核层的大 BAR 补丁、限频的真实代价、通信栈为什么吃不到收益、以及一批带口径的速度/时间数据。平台、装机、采购价上篇讲过了,不再重复。

                                  鸣谢:
                                  @PhoenixRise2026
                                  X99 + 双 AMD Radeon AI PRO R9700 跑通跨 Root Port P2P:从内核、RCCL 到 SGLang Qwen3.8 实战

                                  @jenaflex 说:
                                  网上有破解办法:
                                  https://github.com/xCuri0/ReBarUEFI/discussions/331
                                  我还没来得及试。

                                  @laobenxiong

                                  如果有 above 4g, 只是 rebar 不成功, 风险较高的做法是魔改 bios, 风险低(其实就是无风险)的做法是给 kernel 打 patch (把问题描述给 AI, 它帮你搞定). 这里我假> 定 above 4g以后, /proc/iomem 上看给 prefetchable space留的空间是足够大的 (这个假定在工作站主板上基本是成立的, 至少256G起吧).

                                  @flyer666
                                  魔改SGLANG支持7900XTX 双卡TP TTFT <1s 平均TG 80-100! 4并发TG200/sec
                                  https://lcz.me/topic/1532

                                  TL;DR

                                  事项 结果 一句话
                                  上游 ReBAR headroom 补丁自行编进内核 ✅ 成了 双卡同处一台交换机时,两条下游链各拿到精确 32 GiB,P2P 从 False 变 True
                                  7900XTX 限频档位 1500 → 2000 MHz ✅ 成了,且收益最大 长文(≈3 万 token)55.85s → 38.39s(−31%),单流稳态 decode 40~52 → 54~75 tok/s
                                  amdsmi 补进推理环境 ⚠️ 装上了,但没吃到收益 报错从「amdsmi 未定义」前进到「sgl_kernel 缺算子」,环境层障碍已清、算子层仍缺
                                  symm-mem / multimem 通信优化 ❌ 此路不通 SGLang 在 world_size=2 时主动禁用 multimem,启动参数默认全 False
                                  上游 PR #34058(prefill/decode 公平性)回移 ✅ 移植成功、参数生效 同一个短请求的最大断流间隔 47.58s → 5.06s(−89.4%):默认配置下,一个 4.8 万 token 的 prefill 会让已经解码起来的请求整段停摆
                                  nvtop 显存看板 ✅ 修好了 3.0.2 的 fdinfo 断言 bug(同一 client_id 被两个 fd 引用),换新版解决
                                  跨根口 P2P(R9700 ↔ 7900XTX) ❌ 硬件限制 can_device_access_peer=False,新旧内核同值,与 BAR 无关
                                  交换机路径带宽 ⚠️ 有天花板 卡↔交换机是 Gen4 x16,但内部部分跳只跑 Gen2 x8

                                  给赶时间的人:这一天里唯一"一改就见效、幅度又大"的是限频档位(−31% 长文时延);最费劲、看起来最"高级"的大 BAR / 自定义 All-Reduce,收益要么体现在"能不能跑"、要么干脆吃不到。先量再改。


                                  一、今天想解决的三件事(思路,不是结论)

                                  1. 大 BAR 与 P2P 是两件事

                                  上篇的结论是"P2P 通了",但当时有个没解释的怪现象:卡片报的 BAR 只有 256M,而 P2P 时灵时不灵。这里要分三层看:

                                  • 能不能寻址到对方显存(P2P):由芯片组/拓扑/内核白名单决定;
                                  • BAR 窗口够不够大(ReBAR):决定大块 DMA 能不能一次性映射过去,还是被切成一堆小块来回搬运;
                                  • 内核愿不愿意在启动时把预留窗口让出来:这是最容易忽略的一层——如果 PCI 子系统在枚举时就把 prefetchable 窗口"分干净"了,后面再想要 32G 就没有余地了。

                                  → 所以先做了对照:同一张卡,旧内核 256M + P2P False;新内核 32G + P2P True。同一硬件、同一槽位,只换内核,这就是判据。

                                  2. "防跳闸"的限频,代价是整整 1.9 倍

                                  上篇里有一条限功率服务,注释写得很清楚:压缩瞬态电流尖峰、防市电跳闸。它把 SCLK 压到 1500MHz。于是出现了本文最容易被误判的一段:"同一个服务,性能只有上篇的一半"——一度怀疑是交换机、大 BAR、P2P 没生效。

                                  真凶是限频:负载中实测 sclk = 1233 MHz(默认 boost 2371MHz ⇒ 1.9× 差距),与"100 → 50 tok/s"精确对应。方向选错,会把一小时耗在错误的地方。

                                  3. 通信优化的顺序:环境 → 算子 → 拓扑

                                  一厢情愿的想法是"装上 amdsmi + aiter,打开 symm-mem,就能吃 PCIe All-Reduce 的收益"。实际打下来是三层:

                                  1. 环境层(可以自己解决):缺 amdsmi / aiter;
                                  2. 算子层(要重编):fork 编译出来的 sgl_kernel 扩展里没有对应算子;
                                  3. 拓扑层(改不了):SGLang 在 world_size=2 时主动关掉 multimem。

                                  三层里只有第一层能自己搞定——这就是"装了却没收益"的原因。


                                  二、速度与时间总账

                                  统一口径(论坛上最容易被挑的就是裸数字):

                                  • 量化 gptq、dtype bfloat16、--attention-backend triton、TP=2(两张 7900XTX,同挂一台 PCIe 交换机);
                                  • 投机解码 开:EAGLE,num-steps 3、num-draft-tokens 4;
                                  • 上下文上限 196,608 token,KV 池 242,854 token,mem-fraction-static 0.91,max-running-requests 4;
                                  • 显卡限频档:SCLK ≤ 2000MHz / VDDGFX −100mV(本文新常态);
                                  • 每次测量前 POST /flush_cache,且每条 prompt 用独立前缀构造 ⇒ 全部是冷 prefill,不含前缀缓存命中;
                                  • prefill 速度口径 = prompt token 数 ÷ TTFT(服务空闲,TTFT ≈ prefill 时间);
                                  • decode 速度口径 = (输出 token 数 −1) ÷ (末 token 时刻 − 首 token 时刻),稳态解码,不含 TTFT;
                                  • ⚠️ decode 那一列是「整段输出的平均」(每条约 100~200 个输出 token),含最初几十个 token 的爬坡,所以比服务端自己报的瞬时值低:服务端 gen throughput 中位 52.1、峰值 75.5 tok/s(见 2.4),而同一台机整段平均出来是 28.7(1K)→ 15.2(128K)。两个数都对,口径不同;
                                  • 并发测试每条 prompt 独立,且强制长输出(否则模型早退 EOS,"总 token ÷ 墙钟"算出的聚合吞吐会被输出长度污染)。

                                  2.1 冷启动

                                  口径:从 journalctl 的 Started 事件(秒级时间戳),到引擎日志打出 ready to roll 的时间差。不用"起服务后轮询健康检查"计时——旧进程还在退出时健康检查可能已返回 200,会给出"20 秒就绪"这种假读数(本文实测踩到过)。

                                  今天 5 次真实重启(含改动不同配置):

                                  次 Started → ready 冷启动
                                  1 13:50:14 → 13:51:27 73 s
                                  2 13:57:11 → 13:58:25 74 s
                                  3 14:05:57 → 14:07:10 73 s
                                  4 14:13:49 → 14:15:01 72 s
                                  5 14:18:57 → 14:20:10 73 s

                                  结论:稳定在 72~74 秒(同一台机、同一模型、同一配置族,波动仅 ±1 秒——说明启动时间是被固定阶段的串行耗时决定的)。

                                  其中一次的阶段拆解(日志原文时间戳):

                                  阶段 耗时 说明
                                  权重加载(主模型) 7.01 s Load weight end. elapsed=7.01 s, quant=gptq, bits=4
                                  权重加载(TP 分片) 0.99 s 第二片
                                  捕获 target verify CUDA graph 9.03 s bs=[1,2,4]、num_tokens_per_req=4
                                  捕获 draft decode CUDA graph 3.95 s 投机解码的草稿图
                                  其余(导入 / 分布式初始化 / 编译) ≈ 50 s 占大头,且与配置无关

                                  注意:权重加载只要 ~8 秒、图捕获 ~13 秒,冷启动的大头是框架自身的导入与分布式初始化(≈50 秒)。也就是说换更快的盘对冷启动帮助有限。

                                  2.2 上下文长度 → TTFT / prefill / decode(单流)

                                  目标长度 实测 prompt token TTFT (s) prefill (tok/s) decode (tok/s) 实测输出 token
                                  1K 1,023 0.853 1,199 28.71 184
                                  4K 4,095 2.968 1,380 28.18 179
                                  8K 8,191 6.138 1,334 27.51 200
                                  16K 16,383 13.142 1,247 26.02 172
                                  32K 32,767 29.586 1,108 23.55 179
                                  64K 65,535 72.401 905 19.73 171
                                  128K 127,999 191.216 669 15.19 167

                                  怎么读这张表:

                                  • prefill 峰值出现在 4K 附近(1,380 tok/s),到 128K 掉到 669 tok/s(−52%)——上下文越长,单块的注意力开销越大;
                                  • TTFT 与上下文近似线性:1K 用 0.85 s,128K 要 191 s(≈3 分 11 秒)——这是「长上下文首字延迟」最直观的一个数;
                                  • decode 随上下文单调下滑:28.7 → 15.2 tok/s(几乎腰斩),这是 KV 越长、每步注意力越贵的必然结果;
                                  • 重复性:本节与首轮独立测量(首轮输出长度仅 ~33 token、含较多爬坡)的 TTFT 几乎逐点重合——0.823/0.853、2.970/2.968、6.131/6.138、29.583/29.586、72.420/72.401、191.308/191.216,差在千分位。说明这台机上的 prefill 时间是可复现的确定性量。

                                  ⚠️ decode 口径说明:本节 decode = 「整段输出的平均」((n−1)÷(末−首)),含最初 ~30 个 token 的爬坡,因此低于服务端稳态读数。服务端 gen throughput 的同期中位是 52.1 tok/s、最大 75.5 tok/s(见 2.4)。两个口径都对,只是一个含爬坡、一个是稳态瞬时值,别混着比。

                                  ![上下文长度对 TTFT 与 prefill/decode 速度的影响]
                                  fig1-上下文扫描.jpeg

                                  2.3 多并发

                                  并发数 墙钟 (s) 聚合吞吐 (tok/s) TTFT 中位 (s) TTFT 最大 (s) 单流 decode 中位 (tok/s)
                                  1 8.21 23.4 1.53 1.53 28.59
                                  2 11.59 32.6 2.22 2.96 20.23
                                  4 17.72 45.3 4.41 5.91 15.34
                                  8 33.39 45.0 11.97 22.57 14.48

                                  怎么读这张表:

                                  • 聚合吞吐 23.4 → 32.6 → 45.3,到 8 并发时卡在 45.0 不再增长——因为 max-running-requests=4:第 5~8 个请求只能排队;
                                  • 排队的代价写在时延列:TTFT 中位 1.53 s → 11.97 s,最大 22.57 s,而吞吐一点没多;
                                  • 单流 decode 随并发下降(28.6 → 14.5),是权重带宽被多流瓜分的正常结果;
                                  • 结论:这台双卡机的甜点就是 4 并发——再多只涨延迟不涨吞吐。

                                  ⚠️ 本节踩坑(值得单独记一笔):第一版并发测试用「填充文本 + 让它随便续写」,结果模型很快 EOS、每次只出 ~33 个 token,反推出来的聚合吞吐只有 12~17 tok/s(比单流稳态 52 还低,明显不合理)⇒ 整张表作废。改成「只输出数字、从 1 数到 512」强制长输出后重测,才得到上表。测聚合吞吐,输出长度必须可控,否则你测的是 EOS 什么时候来。

                                  ![并发扩展]
                                  fig2-并发扩展.jpeg

                                  2.4 投机解码(EAGLE)实况

                                  指标 数值
                                  accept len 中位 2.35
                                  accept rate 中位 0.45
                                  样本数 33 条 decode 日志
                                  服务端 gen throughput 中位 / 最大 52.1 / 75.5 tok/s

                                  怎么读:accept len = 每次投机步平均被接受几个 token(上限是 draft 4 个),accept rate = 被接受的比例。中位 2.35 / 0.45 说明还有余量——理论上把接受率从 0.45 提到 0.6,decode 还能再上台阶。另外注意 2.5 节里 SCLK 2000 档的「稳态 54~75 tok/s」与此处的服务端读数互相印证。

                                  2.5 限频档位对照(同一服务、同一基准)

                                  档位 短文 长文(≈3 万 token) 稳态 decode 峰值功耗 峰值结温
                                  SCLK ≤ 1500 / −100mV 4.51s 55.85s 40~52 tok/s 179 W —
                                  SCLK ≤ 2000 / −100mV 3.57s 38.39s 54~75 tok/s 242 W 77 °C
                                  SCLK ≤ 2000(持续满载压测,896 条采样) — — — 250 / 253 W 84 / 87 °C

                                  读法:短促的基准把结温顶到 77 °C,但把上下文扫描+并发连续压满之后,结温会到 84~87 °C(两张卡分别 84 与 87)。离我在遥测里设的 95 °C 急停线只剩 8 °C——这说明"限频防跳闸"之外,散热余量也该被当成一个正经指标来测,光看一两轮短基会是会误判的。
                                  读写口径:峰值来自 0.5 秒间隔的 sysfs 遥测(本轮 896 条采样),毫秒级瞬态尖峰看不到——所以"这轮没跳闸"不能推出"尖峰不存在"。

                                  ![限频档位对照]
                                  fig3-限频对照.jpeg


                                  三、三个反直觉的发现

                                  1. 大 BAR 的收益不在带宽,在"能不能分配"

                                  把补丁前后的数值摆在一起就看清了:

                                  测量项(双卡同交换机) 旧内核 新内核
                                  XTX 的 BAR Region 0 256M 32 GiB
                                  can_device_access_peer False True
                                  AllReduce 256MB(RCCL) 2.33 GB/s 7.04 GB/s
                                  对等拷贝 1.65 GB/s 3.44 GB/s
                                  H2D / D2H 6.04 / 6.82 GB/s 6.04 / 6.82 GB/s(不变)

                                  两处最能说明问题:H2D 完全不随内核变(它走的是卡↔交换机最后一跳,Gen2 内部跳,与 BAR 无关);而 P2P 的 AllReduce 直接翻了 3 倍。⇒ 大 BAR 解决的是"能不能把大块一次性映射过去",不是"把带宽硬件加宽"。

                                  2. "排队 TTFT" 与 "解码被饿死" 是两码事

                                  做公平性实验时踩了个坑,值得写下来:

                                  • 先发一条 2.5 万 token 的大 prefill,再发一条短请求 → 短请求 TTFT = 21.5 秒;
                                  • 这时去看它的 token 间隔,只有 0.045 秒。

                                  原因是短请求还没轮到自己的 prefill,它在排队——而 prefill/decode 公平性参数(--max-consecutive-prefill-batches)管的是已经在解码的请求别被连续 prefill 饿死(has_runnable_decode 为真时才介入)。指标选错,会得出"参数没用"的错误结论。正确的测法:让短请求先解码起来,再注入冷的巨大 prefill,然后量它的 token 间隔。

                                  3. 断流对照实测:把「这个参数到底管什么」直接量出来

                                  实验设计:先让一条短请求解码起来(数数字),发出 8 个 token 后,注入一条 48,000 token 的冷 prefill(每次测量前清缓存),然后量短请求流式输出里的 token 间隔。

                                  配置 超过 1 秒的间隔 最大间隔
                                  N=0(SGLang 默认,prefill 优先) 1 个:47.58 s 47.58 s
                                  N=1(连续 1 批 prefill 后强制一次 decode) 12 个:3.17~5.06 s(全部有界) 5.06 s

                                  最大间隔 47.58 s → 5.06 s(−89.4%)。lcz 帖同期独立实测是 47.23 s → 4.04 s(−91.4%)——同向、同量级,两边都是同一类机器、同一机制。

                                  ★ 为什么这份数据可信:机制在数字里自洽

                                  • 大 prefill 用量 = 48,000 token ÷ 单块 chunk 4,096 = 11.7 块;
                                  • N=1 实测恰好 12 个「>1 秒」的间隔——间隔个数与 chunk 个数吻合;
                                  • 每个间隔 3.17~5.06 s,而该轮实测 prefill 48.58 s ÷ 11.7 块 ≈ 4.15 s/块——间隔长度与单块耗时吻合(N=0 那轮 prefill 是 48.07 s,两块数字各自独立取,不混用)。

                                  ⇒ 这个参数做的事不是「提速」,而是在每块 prefill 之后让出一次 decode 窗口。数量级、节奏、边界全部对得上,不是跑分抖动。

                                  ![断流对照:N=0 vs N=1]
                                  fig4-断流对照.jpeg

                                  ⚠️ 口径提醒(很容易被误读):上表里两种配置的短请求 TTFT 几乎一样(0.339 s)——因为它是先解码起来的那个请求。如果反过来「大请求先发」,短请求 TTFT 会飙到 21.5 s:那是排队等自己的 prefill,与「解码被饿死」是两个不同机制。--max-consecutive-prefill-batches 的判据是 has_runnable_decode 为真才介入,只保护已经在解码的请求。指标选错,就会得出「参数没用」的假结论。


                                  四、还没打通的墙(含推测标注)

                                  墙 证据 判断
                                  跨根口 P2P(R9700 ↔ XTX) can_device_access_peer=False,新旧内核同值 硬件/芯片组限制,与 BAR 无关(已证实)
                                  交换机内部跳数 部分跳 Gen2 5GT/s / x8,H2D 仅 6.0/6.8 GB/s 推测:交换芯片内部降速,非配置问题;待换直连槽对照验证
                                  自定义 All-Reduce Setup Custom allreduce failed: '_OpNamespace' 'sgl_kernel' object has no attribute 'meta_size' fork 算子层缺口(需重编 sgl-kernel);amdsmi 已就位
                                  multimem multimem all-gather disabled (no multicast for world_size=2) 推测:既受 world_size=2 闸门限制,也可能与 RDNA3 无 multicast 有关;待以 world_size>2 或换卡验证
                                  掉电史 撤独立供电后曾多次整机掉电 未根除,靠限频+遥测+看门狗长期观察积累信心

                                  五、给后来人的清单

                                  买之前

                                  • 想跑多卡 TP:先确认
                                    主板/交换机的 PCIe 拓扑与 BAR 能力,别只看卡;
                                  • 要 P2P,优先
                                    直连根口;经交换机能通,但带宽和 BAR 都会打折。

                                  装机时

                                  • 大 BAR 不是"打开一个选项"就行,
                                    内核的 prefetchable 窗口预留策略才是关键;
                                  • 动卡前断电,开机顺序:交换机 + 显卡独立供电先上电,再开主机。

                                  调优时

                                  • 先量再改:任何"性能只有一半"的结论,先去读负载中的实际频率/功耗;
                                  • 限频/降压可以用,但要清楚它压的是
                                    瞬态电流尖峰,代价可能远超预期;
                                  • 测卡对卡带宽
                                    只用 RCCL,PyTorch 的 copy_ + Event 计时在 ROCm 上给的是荒谬数;
                                  • 每次基准前清缓存(
                                    /flush_cache)+ 每条 prompt 独立前缀,否则测的是缓存命中不是 prefill。

                                  排障时

                                  • 只用一个设备可见性变量(多变量叠加会二次过滤,直接变成 0 张卡);
                                  • 就绪判据只认健康检查 200,别 grep 日志字符串(会变);
                                  • 看板崩了先怀疑看板本身(本文的 nvtop 就是它自己的 bug,不是卡出问题)。

                                  附:给 AI / 爬虫的结构化数据段

                                  本节面向自动化抓取与二次分析,字段尽量保持稳定的键值格式。

                                  A1. 平台与软件栈

                                  machine_model      : Dell Precision Tower 7910
                                  bios               : A34 (2020-10-19)
                                  cpu                : 2 × Intel Xeon E5-2683 v4 @ 2.10GHz
                                  ram                : 125 GiB
                                  gpu_a              : 2 × AMD Radeon AI PRO R9700 (gfx1201 / RDNA4, 32624 MB, 直连根口)
                                  gpu_b              : 2 × AMD Radeon RX 7900 XTX (Navi 31 / gfx1100, 24560 MB, 同挂一台 PCIe 交换机)
                                  rocm               : 7.2.0
                                  kernel_running     : 7.0.14-rebar(自编,= 7.0.14-p2pdma + 上游 ReBAR headroom 补丁)
                                  secure_boot        : disabled
                                  switch             : Broadcom PEX880xx 系列,独立供电(台达 1500W)
                                  

                                  A2. 内核补丁(关键)

                                  patch_title        : PCI: Reserve prefetchable window headroom for Resizable BARs
                                  author             : Geramy Loveless
                                  patchwork_series   : 520756 (v2 2026-08-28 / v3 2026-08-31)
                                  size               : 5 files, +198 / -0
                                  new_kernel_options : pci=no_rebar_reserve  (默认开), pci=no_bar_demote (默认开)
                                  upstream_test_env  : ASUS K14PG-D24 / 2× EPYC 9354 / 8× Radeon AI PRO R9700 / Broadcom PEX890xx
                                  手工修补           : hunk #4(7.0.14 把 pci_min_window_alignment 改名为 window_alignment)
                                  

                                  A3. BAR / P2P 实测(决定性对照)

                                  # 同一硬件、同一槽位,仅换内核
                                  旧内核 (7.0.14-p2pdma):
                                    xtx_bar_region0           : 256M
                                    root_bridge_a0:02.0       : 770M
                                    upstream_a1:00.0          : 258M
                                    xtx_p2p (can_access_peer) : false
                                    allreduce_256MB_rccl      : 2.33 GB/s
                                    peer_copy                 : 1.65 GB/s
                                  新内核 (7.0.14-rebar):
                                    xtx_bar_region0           : 32 GiB
                                    root_bridge_a0:02.0       : 64 GiB
                                    upstream_a1:00.0          : 64 GiB
                                    下游链 ×2                 : 各精确 32 GiB
                                    xtx_p2p                   : true
                                    allreduce_256MB_rccl      : 7.04 GB/s
                                    peer_copy                 : 3.44 GB/s
                                    h2d / d2h                 : 6.04 / 6.82 GB/s(两版相同)
                                  双 R9700(直连根口,rebar 内核):
                                    allreduce_256MB_rccl      : 7.186 GB/s
                                    peer_copy                 : 6.19 GB/s
                                    h2d / d2h                 : 12.47 / 13.27 GB/s
                                    can_device_access_peer    : true / true
                                  跨根口:
                                    R9700 <-> 7900XTX         : false(新旧内核同值;not supported by the chipset ×8)
                                  

                                  A4. 服务启动参数(原文,路径已占位)

                                  python -m sglang.launch_server
                                    --max-consecutive-prefill-batches 1        # 本篇从上游 PR #34058 回移的新参数
                                    --model-path /models/qwen3.8-27b-mtp-fixed
                                    --host 0.0.0.0 --port 8082
                                    --served-model-name qwen3.8-27b-p2p
                                    --tp-size 2
                                    --quantization gptq
                                    --dtype bfloat16
                                    --mamba-ssm-dtype bfloat16
                                    --kv-cache-dtype auto
                                    --attention-backend triton
                                    --context-length 196608                    # 192K
                                    --mem-fraction-static 0.91
                                    --max-running-requests 4
                                    --max-mamba-cache-size 20
                                    --speculative-algorithm EAGLE
                                    --speculative-draft-model-path /models/qwen3.8-27b-mtp-fixed   # 同权重当草稿模型(MTP 头)
                                    --speculative-num-steps 3
                                    --speculative-eagle-topk 1
                                    --speculative-num-draft-tokens 4
                                    --cuda-graph-bs-decode 1 2 4
                                    --triton-attention-num-kv-splits 16
                                  

                                  由 systemd 承载(路径占位):

                                  [Unit]
                                  Description=SGLang (2× 7900 XTX, TP=2) :8082
                                  Wants=gpu-clk-limit.service          # 必须先让限功率生效,再加载模型
                                  After=gpu-clk-limit.service
                                  
                                  [Service]
                                  Type=simple
                                  User=<USER>
                                  WorkingDirectory=<SGLANG_DIR>
                                  ExecStart=<SGLANG_DIR>/start_server.sh
                                  Restart=on-failure
                                  RestartSec=15
                                  TimeoutStartSec=1800
                                  StandardOutput=append:<SGLANG_DIR>/service.log
                                  StandardError=append:<SGLANG_DIR>/service.log
                                  LimitMEMLOCK=infinity
                                  
                                  [Install]
                                  WantedBy=multi-user.target
                                  

                                  A5. 环境变量(启动脚本内 export 全量)

                                  export PYTHONUNBUFFERED=1
                                  export NCCL_DEBUG=WARN
                                  export SGL_DTYPE=bfloat16
                                  export SGL_PHASE_TIMING=0
                                  export USE_HUB_KERNELS=0
                                  # RDNA3 fork 相关开关(下方带前缀的成对出现,是 fork 里两套命名都要给)
                                  export SGLANG_OPT_USE_CUSTOM_ALL_REDUCE_V2=1
                                  export SGLANG_RDNA_CUSTOM_AR=0
                                  export SGL_RDNA_CUSTOM_AR=0
                                  export SGLANG_RDNA_NO_FUSED=1
                                  export SGLANG_RDNA_GEMMA_TRITON=1
                                  export SGLANG_RDNA_VLLM_VERIFY=1
                                  export SGL_RDNA_NO_FUSED=1
                                  export SGL_RDNA_GEMMA_TRITON=1
                                  export SGL_RDNA_VLLM_VERIFY=1
                                  # kfd event-age 修复 shim(fork 自带,用 LD_PRELOAD 注入)
                                  SHIM=<SGLANG_DIR>/scripts/rdna_ar/vendor/kfd_event_age_fix.so
                                  [ -f "$SHIM" ] && export LD_PRELOAD="$SHIM:${LD_PRELOAD:-}"
                                  

                                  说明:SGLANG_RDNA_CUSTOM_AR=0 是主动关掉作者那套 PCIe All-Reduce 的开关——本篇实测证明它在当前环境下起不来(缺算子),关掉可避免误导性告警。

                                  A6. 限频/OD 设置

                                  gpu_clk_limit_service : SCLK <= 2000 MHz, VDDGFX -100mV(两张 7900XTX)
                                  dropin                : gpu-clk-limit.service.d/sclk2000.conf  (ExecStart override)
                                  回退                  : rm sclk2000.conf && systemctl daemon-reload
                                  R9700 稳定性服务       : 210 W / SCLK-500MHz / VDD-100mV
                                  读回校验               : OD_SCLK: 2000Mhz, OD_VDDGFX_OFFSET: -100mV
                                  

                                  A7. 基准原始数据

                                  断流对照(冷 48,000 token prefill 注入,短请求先解码起来):

                                  {
                                    "E_starvation_E0": {
                                      "big_prompt_tokens": 48000, "big_prefill_s": 48.07, "small_ttft_s": 0.339,
                                      "small_n_out": 29, "max_gap_s": 47.58, "median_gap_s": 0.034, "gaps_over_1s": [47.58]
                                    },
                                    "E_starvation_E1": {
                                      "big_prompt_tokens": 48000, "big_prefill_s": 48.58, "small_ttft_s": 0.34,
                                      "small_n_out": 28, "max_gap_s": 5.062, "median_gap_s": 0.137,
                                      "gaps_over_1s": [3.364, 3.173, 3.392, 3.610, 3.817, 4.025, 4.230,
                                                       4.439, 4.648, 4.858, 5.062, 3.517]
                                    }
                                  }
                                  

                                  上下文扫描与并发(强制输出 512 数字后重跑,格式同上;本段由脚本直接产出 JSON):

                                  {
                                    "B2_context_sweep": [
                                      {"target": 1024,   "prompt_tokens": 1023,   "ttft_s": 0.853,   "prefill_tps": 1199, "decode_tps": 28.71, "n_out": 184},
                                      {"target": 4096,   "prompt_tokens": 4095,   "ttft_s": 2.968,   "prefill_tps": 1380, "decode_tps": 28.18, "n_out": 179},
                                      {"target": 8192,   "prompt_tokens": 8191,   "ttft_s": 6.138,   "prefill_tps": 1334, "decode_tps": 27.51, "n_out": 200},
                                      {"target": 16384,  "prompt_tokens": 16383,  "ttft_s": 13.142,  "prefill_tps": 1247, "decode_tps": 26.02, "n_out": 172},
                                      {"target": 32768,  "prompt_tokens": 32767,  "ttft_s": 29.586,  "prefill_tps": 1108, "decode_tps": 23.55, "n_out": 179},
                                      {"target": 65536,  "prompt_tokens": 65535,  "ttft_s": 72.401,  "prefill_tps": 905,  "decode_tps": 19.73, "n_out": 171},
                                      {"target": 128000, "prompt_tokens": 127999, "ttft_s": 191.216, "prefill_tps": 669,  "decode_tps": 15.19, "n_out": 167}
                                    ],
                                    "C2_concurrency": [
                                      {"level": 1, "wall_s": 8.21,  "agg_tps": 23.4, "ttft_med": 1.53,  "ttft_max": 1.53,  "decode_med": 28.59},
                                      {"level": 2, "wall_s": 11.59, "agg_tps": 32.6, "ttft_med": 2.22,  "ttft_max": 2.96,  "decode_med": 20.23},
                                      {"level": 4, "wall_s": 17.72, "agg_tps": 45.3, "ttft_med": 4.41,  "ttft_max": 5.91,  "decode_med": 15.34},
                                      {"level": 8, "wall_s": 33.39, "agg_tps": 45.0, "ttft_med": 11.97, "ttft_max": 22.57, "decode_med": 14.48}
                                    ],
                                    "D_speculative": {"samples": 33, "accept_len_median": 2.35, "accept_rate_median": 0.45,
                                                      "gen_throughput_median": 52.1, "gen_throughput_max": 75.5},
                                    "peak_telemetry": {"samples": 896, "power_w": [250.0, 253.0], "junction_c": [84.0, 87.0]}
                                  }
                                  

                                  关于本节的 JSON:B2 那一段是从脚本的标准输出逐列重建的(数值即运行时打印值)。原因是采集脚本每完成一段就覆写整个结果文件,跑到最后只剩最后一段——这是"测量脚本必须合并写入而非覆写"的现成反例,已记进我自己的踩坑清单。C2 / D / 遥测三段是脚本直接落盘的原始 JSON。

                                  冷启动(journalctl Started → 引擎 ready to roll,5 次):

                                  13:50:14->13:51:27 = 73s
                                  13:57:11->13:58:25 = 74s
                                  14:05:57->14:07:10 = 73s
                                  14:13:49->14:15:01 = 72s
                                  14:18:57->14:20:10 = 73s
                                  

                                  A8. 日志关键行(原文)

                                  # 通信路径(本篇的核心结论之一)
                                  [AR] All-reduce call path: NCCL (custom AR disabled)
                                  [AR] Aiter custom all-reduce not available; falling back to sglang CustomAllreduce. Details: No module named 'aiter'
                                  Setup Custom allreduce failed with '_OpNamespace' 'sgl_kernel' object has no attribute 'meta_size'. To silence this warning, specify --disable-custom-all-reduce explicitly.
                                  multimem all-gather disabled (no multicast for world_size=2)
                                  
                                  # 启动阶段(时间戳见 A7)
                                  Load weight end. elapsed=7.01 s, type=Qwen3_5ForConditionalGeneration, quant=gptq, bits=4, avail mem=14.43 GB, mem usage=9.12 GB.
                                  Load weight end. elapsed=0.99 s, type=Qwen3_5ForCausalLMMTP, quant=gptq, bits=4, avail mem=11.59 GB, mem usage=2.82 GB.
                                  Capture target verify CUDA graph begin. backend=full, num_tokens_per_req=4, bs=[1, 2, 4], avail mem=4.50 GB
                                  Capture target verify CUDA graph end. elapsed=9.03 s, mem usage=0.44 GB
                                  Capture draft decode CUDA graph end. elapsed=3.95 s, mem usage=0.29 GB
                                  max_total_num_tokens=242854, chunked_prefill_size=4096, max_prefill_tokens=16384, max_running_requests=4, context_len=196608, available_gpu_mem=3.76 GB
                                  The server is fired up and ready to roll!
                                  
                                  # 运行期(吞吐与投机解码)
                                  Prefill batch, #new-seq: 1, #new-token: 4096, #cached-token: 0, ..., input throughput (token/s): 621.40
                                  Decode batch, #running-req: 1, #full token: 484, ..., accept len: 2.45, accept rate: 0.48, cuda graph: True, gen throughput (token/s): 71.28
                                  

                                  注:Setup Custom allreduce failed with ... meta_size 这一行在装上 amdsmi 之后才出现——装之前报的是 name 'amdsmi_shut_down' is not defined。报错换了一行,说明卡点从环境层推进到了算子层,这是本篇判断"收益吃不到"的直接证据。

                                  A9. 复现命令

                                  # 冷启动计时
                                  time (systemctl restart sglang-p2p.service; until curl -sf http://127.0.0.1:8082/health; do sleep 1; done)
                                  
                                  # 单流上下文扫描(每次清缓存,避免命中 radix 前缀缓存)
                                  curl -s -X POST http://127.0.0.1:8082/flush_cache -d '{}' -H 'Content-Type: application/json'
                                  curl -s http://127.0.0.1:8082/v1/completions -H 'Content-Type: application/json' \
                                    -d '{"model":"<served-name>","prompt":"<unique-prefix text>","max_tokens":128,"stream":true}'
                                  
                                  # 卡间带宽(只用 RCCL)
                                  torchrun --nproc_per_node=2 allreduce_bench.py --size-mb 256
                                  

                                  A10. 本轮移植的上游 PR

                                  pr                 : sgl-project/sglang#34058
                                  title              : scheduler: bound consecutive prefills to protect decode
                                  author             : atbe
                                  status             : OPEN(未合并)
                                  new_flag           : --max-consecutive-prefill-batches N   (默认 0 = 保持 prefill 优先)
                                  semantics          : 仅在 has_runnable_decode 为真时介入;连续 N 个 prefill 批次后强制一个 decode 轮
                                  fixes              : issue #32549(持续 chunked-prefill 下 decode 被饿到每 24s 才 1 个 batch)
                                  移植要点            : fork 的 server_args 已重构为 arg_groups/{fields,validation_hook,field_order},
                                                       PR 的 hunk 完全对不上;新增文件可原样落地,其余按 fork 自身写法手工落点
                                  
                                  L
                                  L
                                  laobenxiong
                                  德高望重 劳动模范
                                  编写于 最后由 编辑
                                  #105

                                  @张光璞 说:

                                  T7910 PCIE SWITCH 双卡推理续篇:自编 ReBAR 内核吃下 32G 大 BAR、7900XTX 解锁 2000MHz,附一整天的实测数据

                                  期待对 switch 的继续测试. 我感觉 "内部 gen2" 很可疑啊... lspci -tv 看看 topology 到底长什么样? 链路的 bottlenect 在哪里?

                                  张光璞张 1 条回复 最后回复
                                  0
                                  • BunseiB Bunsei

                                    @张光璞 说:

                                    测试环境

                                    aed3d331e9215360e3ab3946773aaec8.jpg

                                    看到我最期待的环节了~ 不过说实话,我决定后面要换平台了。从am5换成老的epic。

                                    张光璞张
                                    张光璞张
                                    张光璞
                                    技术大牛 劳动模范
                                    编写于 最后由 编辑
                                    #106

                                    @Bunsei 说:

                                    @张光璞 说:

                                    测试环境

                                    aed3d331e9215360e3ab3946773aaec8.jpg

                                    看到我最期待的环节了~ 不过说实话,我决定后面要换平台了。从am5换成老的epic。

                                    我觉得epyc 平台也做不到4卡全是x16 互联吧。

                                    A 1 条回复 最后回复
                                    0
                                    • 张光璞张 张光璞

                                      @Bunsei 说:

                                      @张光璞 说:

                                      测试环境

                                      aed3d331e9215360e3ab3946773aaec8.jpg

                                      看到我最期待的环节了~ 不过说实话,我决定后面要换平台了。从am5换成老的epic。

                                      我觉得epyc 平台也做不到4卡全是x16 互联吧。

                                      A
                                      A
                                      applejuice
                                      技术大牛 劳动模范
                                      编写于 最后由 编辑
                                      #107

                                      @张光璞 可以 4x pcie4x16

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

                                        @张光璞 说:

                                        T7910 PCIE SWITCH 双卡推理续篇:自编 ReBAR 内核吃下 32G 大 BAR、7900XTX 解锁 2000MHz,附一整天的实测数据

                                        期待对 switch 的继续测试. 我感觉 "内部 gen2" 很可疑啊... lspci -tv 看看 topology 到底长什么样? 链路的 bottlenect 在哪里?

                                        张光璞张
                                        张光璞张
                                        张光璞
                                        技术大牛 劳动模范
                                        编写于 最后由 编辑
                                        #108

                                        @laobenxiong 说:

                                        @张光璞 说:

                                        T7910 PCIE SWITCH 双卡推理续篇:自编 ReBAR 内核吃下 32G 大 BAR、7900XTX 解锁 2000MHz,附一整天的实测数据

                                        期待对 switch 的继续测试. 我感觉 "内部 gen2" 很可疑啊... lspci -tv 看看 topology 到底长什么样? 链路的 bottlenect 在哪里?

                                        把每一跳的 LnkCtl2(Target Link Speed)和 LnkSta 都 dump 了一遍,结论是**"内部 Gen2"这个说法是我读错了**:

                                        PEX880xx 会把自己一堆内部虚拟端口(Port #225/#233/#247)也枚举出来,那些行 Target=5GT/s、EqualizationComplete-(没做过均衡)—— 它们只是下行真实端口的镜像值,不能当证据。

                                        真凶是交换机下行口 ↔ 显卡上游口那一跳:两端 LnkCap 16GT/s x16、Target 16GT/s,但 LnkSta = 5GT/s x16 (downgraded) ⇒ 要求 Gen4、训练后自己回落,属物理层,不是配置。

                                        宽度全程 x16 没降,所以原文"x8"也写错了,已更正。
                                        交叉验证:该跳单卡 H2D/D2H = 6.04/6.82 GB/s(Gen2 x16 理论上限 8.0,效率 7585%);直插根口那对是 12.47/13.27(Gen3 x16 上限 15.75,效率 7984%)——效率一致、比值 2.1× ≈ 带宽比 2.0×。

                                        还不止:两张卡共用一个根口(直插那对是各自独占),所以这段上行本身是 Gen3 x16 共享。按实测单卡 6.0 GB/s,两卡满跑 12 < 15.75,当前瓶颈仍是那个 Gen2 跳。
                                        下一步按成本排:换线/转接板重插 → 换交换机下行口 → pcie_aspm=off(现在全链路 ASPM L1 开着)→ 最后才考虑 setpci 强制重训(边际链路可能直接掉卡,且绝不能在跑推理时做)。
                                        卡对卡 P2P 我没下结论:AllReduce 7.04 vs 直连那对 7.19 几乎一样,更像被 RCCL 开销卡住而非 Gen2,要 rocm-bandwidth-test 横幅才定。

                                        ══ 0000:05:00.0 ★ R9700(直插槽)
                                        0000:00:02.0 Intel 根口 (Xeon E7v4)
                                        0000:03:00.0 Navi31 上行口 (卡内) ← 上行链路 Gen3 x16 (能力 Gen5 x16) ✓ 正常:上游平台只到 Gen3
                                        0000:04:00.0 Navi31 下行口 (卡内) ← 上行链路 Gen5 x16 (能力 Gen5 x16)
                                        0000:05:00.0 ★ R9700 ← 上行链路 Gen5 x16 (能力 Gen5 x16)

                                        ══ 0000:08:00.0 ★ R9700(直插槽)
                                        0000:00:03.0 Intel 根口 (Xeon E7v4)
                                        0000:06:00.0 Navi31 上行口 (卡内) ← 上行链路 Gen3 x16 (能力 Gen5 x16) ✓ 正常
                                        0000:07:00.0 Navi31 下行口 (卡内) ← 上行链路 Gen5 x16 (能力 Gen5 x16)
                                        0000:08:00.0 ★ R9700 ← 上行链路 Gen5 x16 (能力 Gen5 x16)

                                        ══ 0000:aa:00.0 ★ 7900 XTX(走交换机)
                                        0000:a0:02.0 Intel 根口 (Xeon E7v4)
                                        0000:a1:00.0 交换机上游口 ← 上行链路 Gen3 x16 (能力 Gen4 x16) ✓ 正常
                                        0000:a2:04.0 交换机端口 [内] ← 上行链路 Gen2 x16 (能力 Gen4 x16) [镜像值,非物理链路]
                                        0000:a6:00.0 交换机端口 [内] ← 上行链路 Gen2 x16 (能力 Gen4 x16) [镜像值,非物理链路]
                                        0000:a7:00.0 交换机下端口 ← 上行链路 Gen2 x16 (能力 Gen4 x16) ✗✗ 真实物理链路降速
                                        0000:a8:00.0 卡上行口 ← 上行链路 Gen2 x16 (能力 Gen4 x16) ✗✗ 同一跳的另一端
                                        0000:a9:00.0 卡下行口 ← 上行链路 Gen4 x16 (能力 Gen4 x16)
                                        0000:aa:00.0 ★ 7900 XTX ← 上行链路 Gen4 x16 (能力 Gen4 x16)

                                        ══ 0000:b0:00.0 ★ 7900 XTX(走交换机)
                                        0000:a0:02.0 Intel 根口 (Xeon E7v4)
                                        0000:a1:00.0 交换机上游口 ← 上行链路 Gen3 x16 (能力 Gen4 x16) ✓ 正常
                                        0000:a2:08.0 交换机端口 [内] ← 上行链路 Gen2 x16 (能力 Gen4 x16) [镜像值,非物理链路]
                                        0000:ac:00.0 交换机端口 [内] ← 上行链路 Gen2 x16 (能力 Gen4 x16) [镜像值,非物理链路]
                                        0000:ad:00.0 交换机下端口 ← 上行链路 Gen2 x16 (能力 Gen4 x16) ✗✗ 真实物理链路降速
                                        0000:ae:00.0 卡上行口 ← 上行链路 Gen2 x16 (能力 Gen4 x16) ✗✗ 同一跳的另一端
                                        0000:af:00.0 卡下行口 ← 上行链路 Gen4 x16 (能力 Gen4 x16)
                                        0000:b0:00.0 ★ 7900 XTX ← 上行链路 Gen4 x16 (能力 Gen4 x16)

                                        L 1 条回复 最后回复
                                        0
                                        • Grayson RenG
                                          Grayson RenG
                                          Grayson Ren
                                          编写于 最后由 编辑
                                          #109

                                          继续加油 持续关注中

                                          1 条回复 最后回复
                                          0

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

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

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

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


                                          • 登录

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