第 2 章 系统总览

在进入任何具体优化之前,先把这套系统的结构讲清楚:它由哪几层构成,每一层负责什么,数据怎么在它们之间流动。


2.1 四层架构

整个系统可以按职责分成四层,从上到下依次是:

Host 机器(i9-13900K + RTX 5090)
Python 模型主线
DeepSeek-V3.2 / Qwen3 等模型逻辑  ·  Attention + lm_head 在 GPU 上执行  ·  Flattened decode runtime / CUDA Graph
Rust Orchestration 层
管理 duck 的权重加载、prepare、forward 生命周期  ·  通过 FFI 调用 C++ DPDK 层  ·  Buffer 管理、weight cache、timing 统计
C++ DPDK Workers
每张 NIC 对应一个 worker 线程,绑定独立 lcore  ·  轮询 RX/TX descriptor ring,零拷贝收发 UDP 包  ·  Hot path 完全在 userspace,不经过 kernel 调度
↕ UDP 广播 / 单播 4 × 25GbE(host)↔ 32 × 2.5GbE(ducks)
远端 Duck 节点(32 × Intel N100)
JudgeDuck-OS + fp9_kernels.elf
通过网络 PXE 启动  ·  载入 fp9_kernels.elf,执行 fp9 GEMV 计算  ·  persistent judge 会话,多次 forward 复用同一会话

每一层的边界是清晰的:Python 只做模型计算和调度决策;Rust 做 duck 编排和数据组织;C++ DPDK 做 NIC 热路径;JudgeDuck-OS 做远端 kernel 执行。Python 不直接接触网络包,C++ DPDK 不感知模型结构,这种分层让每部分的职责可以独立优化。


2.2 Python 模型主线

Python 层是整个系统的模型逻辑入口,包含两个平行的代码路径:

GPU 侧计算(在 RTX 5090 上执行):

  • Attention(MLA / 多头注意力)
  • lm_head(从 hidden state 到 token logits)
  • 前几层 dense 层(v32_first_k_dense_cuda=True,对 DeepSeek-V3.2 前 3 层)
  • RMSNorm 等归一化算子

Duck 侧计算(通过 Rust 层分发到远端):

  • FFN/MoE 专家层的 GEMV
  • DeepSeek-V3.2 中,每 token 激活 8 个专家(topk=8),共 256 个 MoE 专家层

Flattened decode runtime 是 Python 层的一个关键结构。默认的 eager 路径在每次 decode step 都需要多次 host-GPU 同步和碎片 kernel 调度,固定成本很高。Flattened runtime 把 decode step 内的计算展平成一个连续的 CUDA Graph,一次 graph launch 完成一个完整的 decode forward,把 host 侧的开销从多次 kernel launch 压缩成一次。这在 2026 年 3 月 14 日落地,对 30B tp24 的 decode 速度,从 eager 的约 18 tok/s 提到了约 24 tok/s。

Python 层也负责 MTP1(Multi-Token Prediction) 的在线调度。DeepSeek-V3.2 自带一个 draft head,可以在每次 forward 中同时预测当前 token 和下一个 token。系统把两次 forward(draft decode + verify decode)组织成 dual-lane overlap:GPU 和 duck 这两条计算流水线尽量并行,减少串行等待时间。


2.3 Rust Orchestration 层

Rust 层是 Python 和 DPDK 之间的桥梁,通过 PyO3 暴露接口给 Python,通过 FFI 调用 C++ DPDK 库。它承担以下职责:

Duck 生命周期管理:每个 duck 的 IP、所在广播域、当前状态(是否已初始化、权重是否加载)都由 Rust 层维护。dpdk_ducks.rs 负责维护 duck 列表,weight_cache.rs 负责权重缓存状态的跟踪,避免重复下发权重。

Forward 的各阶段编排:一次 MoE forward 从 Rust 视角可以拆成 prepare → send → judge_wait → fetch → reduce 这几个阶段。Rust 层负责构造每个阶段的请求包、触发 DPDK 发包、等待响应、从响应包里组合 duck 的计算结果。从实际日志里可以看到一次 MoE forward 的典型分解:

MoE forward e2e time (Rust): 1.001ms
  prepare=4.852µs
  send=61.325µs
  judge_wait=807.3µs
  fetch=90.977µs
  reduce=20ns
  writeback=507ns

duck time-ns stats: p50=727.395µs, p90=732.148µs, max=733.071µs
judge_gap=74.229µs

这里 judge_wait 占了绝大部分(约 807µs),这是 duck 侧在执行 fp9 GEMV 计算的时间。judge_gap 是 persistent judge 会话里两次 step 之间 duck 侧的等待间隙,维持在 70~95 µs 量级,说明 persistent judge 机制已经很好地消除了每次 forward 重新启动 ELF 的开销。

Buffer 管理buffer_manager.rsbyte_buf.rs 负责管理 Rust 和 C++ DPDK 层之间共享的 buffer 池,避免频繁分配释放。

Timing 与 trace:Rust 层打出的时间戳日志,是系统调试和性能分析的主要手段。包括每次 forward 的 breakdown、每个 duck 的响应时间分布、MTP1 的 overlap 收益等,都通过 Rust 层的日志输出。


2.4 C++ DPDK Workers

DPDK(Data Plane Development Kit)在这套系统里负责解决一个具体问题:当有 32 只鸭子同时需要收发 UDP 包时,走 Linux 内核的网络栈会引入不可预测的调度延迟,系统表现出所谓的“duck stampede”(鸭群踩踏)——host 侧收包时间远比 duck 侧计算时间更分散。

DPDK 的核心机制是让用户态程序直接轮询 NIC 的 RX/TX descriptor ring,完全绕过内核中断和调度器。每个 NIC 对应一个 C++ worker 线程,线程通过 rte_eth_rx_burst / rte_eth_tx_burst 以轮询方式读写网卡队列,不依赖 recvmsg / sendmsg 这类系统调用。

系统初始化日志片段(可以直接看到 4 个 worker 如何启动):

[2026-03-29 23:47:42.626240 INFO dpdk_workers] Starting worker #0:
  (bcast_ip: 10.21.1.255, port_id: 0, lcore_id: 2, host_ip: 10.21.1.1)
[2026-03-29 23:47:42.626279 INFO dpdk_workers] Initializing worker port 0 on lcore 2...
[2026-03-29 23:47:42.627780 INFO dpdk_workers] Starting worker #1:
  (bcast_ip: 10.21.2.255, port_id: 1, lcore_id: 4, host_ip: 10.21.2.1)
...
ICE_DRIVER: ice_set_rx_function(): Using Vector AVX2 (port 0).

每个 worker 绑定在独立的 lcore 上(lcore 2、4、6、8),不与 Rust master 线程(lcore 0)竞争 CPU 时间。worker 间也相互独立,不共享 NIC queue,消除了锁争用。

通信方式:每次 duck forward 使用 UDP 广播包。host 把输入激活值打成 UDP 包,广播到对应子网的 bcast_ip(如 10.21.1.255),子网里的 8 只鸭子都会收到同一份数据;计算完成后各自单播回 host 的 ip(如 10.21.1.1)。这种广播-单播模式避免了逐一向 32 只鸭发送独立包的开销。

架构边界:C++ 层只处理 packet 的收发和格式化,不感知模型结构或 duck 的业务状态;Rust 层通过 FFI 调用 C++ 接口(duck_llm_init_workerworker_process_request 等),由 Rust 维护 duck 状态和请求上下文。这种分层使得 DPDK 的热路径可以保持最简,而复杂的业务逻辑留在 Rust 侧。


2.5 JudgeDuck-OS 与 fp9_kernels.elf

每台 duck 运行的操作系统是 JudgeDuck-OS,一个最小化的 x86-64 内核,通过 PXE 网络启动。它的原始用途是在线评测(给定一个可执行文件,运行并计时),在这个项目里被改造成了远端 kernel launch runtime。

duck 上不运行通用 OS,没有 Linux 内核调度器,没有 glibc,也没有常规进程。每次 forward 之前,fp9_kernels.elf 被载入 duck 的内存,然后由 JudgeDuck-OS 以 bare-metal 方式直接执行。

关键增量改造(相对原始 JudgeDuck-OS origin/master 的新增提交):

提交改动
Fast ELF loadingELF 装载时只处理与上次不同的页范围,避免整段内存重刷;从 load 5ms 压到可忽略
Load IB by memory-map输入 buffer(IB)按页对齐时直接 mmap,不再 memcpy;消除每次 forward 的 3ms 复制开销
Skip wbinvd去掉 syscall return 路上的 wbinvd 指令,消除约 0.4ms 的缓存刷新开销
Update scheduler configidle 超时从 1ms 改为 10ms,避免 duck 在高频 launch 期间误入短睡眠
Support RTL8125 NIC支持 duck 上的 2.5GbE 网卡
same-ELF SMP多核并行执行同一个 ELF,phase 1-3 逐步带到真机
smp64+persistent open/step定义 persistent judge 会话协议,支持多次 step 复用同一个 judge 实例

persistent judge 是系统后期最重要的 OS 侧优化。早期每次 forward 都需要重新 load ELF、初始化、执行、退出。persistent judge 使得 duck 可以在首次 open 之后进入等待循环,每次 step 只传递新的输入并直接复用已载入的 ELF 和权重缓存,judge_gap 因此从毫秒级压缩到 70~95 µs。

duck 上跑的计算负载是 fp9_kernels.elf,包含:

  • dense MLP forward(fp9 权重 × fp8 激活)
  • MoE forward(按 topk 选定专家,分别执行 fp9 GEMV)
  • prepare(将权重从 host 传下来的格式转换成 duck 侧可直接计算的布局)
  • prefill 模式(处理长 prompt 的输入激活)
  • 多核 SMP 版本(多个 N100 核心并行分担 GEMV)

fp9 是一个在这套系统里自定义的数值格式,专门为 AVX2 + FMA 的 N100 设计。关于它的动机、结构和性能,第 3 章会详细讲。


2.6 一次 Forward 的完整路径

把以上四层串起来,一次 DeepSeek-V3.2 decode step 的完整路径大致是:

  1. Python:当前 token 输入进入模型,前 3 层 dense MLP 在 GPU 上执行(fp8),随后进入 Attention 层。
  2. Python → Rust:Attention 层计算完毕,输出 hidden state 作为 MoE 层的输入激活。Python 通过 PyO3 接口调用 Rust,触发一次 MoE duck forward。
  3. Rust → DPDK → 网络:Rust 层组装 forward 请求(包含激活值、专家 index 等),交给对应广播域的 DPDK worker。worker 将数据打成 UDP 广播包,发往子网广播地址。
  4. Duck 侧:每组 8 只鸭各自收到广播包,按 fp9 权重执行 GEMV。每只鸭处理分配给它的若干专家,计算结果以 UDP 单播包发回 host。
  5. DPDK → Rust → Python:DPDK worker 收到各 duck 的响应包,交给 Rust 层做 reduce(累加各专家输出,加权合并)。结果写回 Python 可见的 tensor。
  6. Python:继续 lm_head、采样,输出本步 token。

这个循环在每个 decode step 里重复一次。当 MTP1 dual-lane overlap 开启时,GPU 侧的 draft decode forward 和 duck 侧的 verify forward 会被编排成两条并行路径,GPU 和鸭子各跑各的,最后在 verify 阶段汇合,收益是把端到端的串行等待时间缩短 40% 左右。


2.7 系统规模的几个关键数字

以 2026-04-08 02-prime-2k testcase(2037 tokens 输出)的实际状态为基准,系统的基本规模如下:

项目数值
在役 duck 数32
host NIC 数4 × Intel E810-C(25GbE)
广播域数4,每组 8 duck
模型DeepSeek-V3.2(685B,含 MTP head),tp=32
input_len / output_len11 / 2037
TTFT0.861 s
prefill12.775 tok/s
tpot0.062 s
decode16.171 tok/s
MTP1 accept=1 比例1813 / 2036(≈89%)
duck 侧单次 MoE forward 时间约 700~760 µs(p50)
persistent judge_gap约 70~95 µs
dual-lane overlap ratio~0.434(549 step 均值,2026-03-29 的 1024 tokens 长跑)

这组数字是后续所有章节的性能基线。每一章里的优化讨论,最终都要落回到这套系统上,才能说清楚改进从哪里来、还剩多少空间。


项目声明 / Project Disclaimer

本项目为作者以个人身份、利用业余时间推进的个人娱乐项目;除非另有明确说明,它与作者的任何雇主、客户、学校、单位或其他组织均无合作、雇佣、委托、赞助或背书关系,也不代表任何该等主体的立场。除普通个人捐赠外,本项目未获得任何资金支持。

This project is a personal hobby project developed by the author in a personal capacity and in personal time. Unless explicitly stated otherwise, it has no collaboration, employment, commission, sponsorship, endorsement, or institutional affiliation with any employer, client, school, partner organization, or other entity, and it does not represent any such party's views. No funding was received for this project except ordinary personal donations.

许可 / License

除非另有说明,本页原创文字、本站原创图片与本站原创图表采用 CC BY-NC-ND 4.0 发布。
转载时请保留原文标题、署名“JudgeDuck AI”、发布日期与原始链接;禁止商业转载、改写、摘编、翻译或基于原文创作演绎作品。

第三方商标、外部链接内容,以及文中另有标注的材料,不在上述许可范围内。

Unless otherwise noted, the original text, original images, and original figures on this page are licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.