cann/cann-recipes-infer:DeepSeek-V4-Flash单卡NPU+K920 CPU混合推理 设计说明DeepSeek-V4-Flash 单卡 NPU K920 CPU 混合推理【免费下载链接】cann-recipes-infer本项目针对LLM与多模态模型推理业务中的典型模型、加速算法提供基于CANN平台的优化样例项目地址: https://gitcode.com/cann/cann-recipes-infer操作步骤见 dsv4_flash_single_card_inference_guide.md。本文只讲设计原理、当前状态和后续规划。一、设计原理总体思路DeepSeek-V4-Flash 是 43 层、全 MoE 的模型first_k_dense_replace0每层都是 MoE每层 256 个路由专家、每个 token 激活 6 个top-k6外加 1 个共享专家。完整放进单张 NPU 放不下于是做了一个混合切分attention、共享专家、router以及一小部分最热的路由专家放在 Atlas 910B64 GB HBM上跑其余路由专家 offload 到 Kunpeng-920 CPU1.5 TB 内存、192 核用 kt-kernel 直接吃官方权重转出的 GGUF。关于权重格式先说清楚免得误会官方发布的权重本身就是 MXFP4。offload 到 CPU 的专家直接沿用这份原生 MXFP4——kt-kernel 能直接吃转 GGUF 只是 bit 级无损 repack不重新量化放在 NPU 上的部分则因为 NPU 只支持 int8用的是转出来的 int8W8A8权重。所以这里并不存在「为了提速去选某种量化」CPU 用 MXFP4 是因为它就是原生格式NPU 用 int8 是被硬件限制。这套混合推理真正的工作量和瓶颈都在CPU offload 这条路上而且是内存带宽、不是算力。batch size 为 1 时每个 token 主要花在把当轮激活到的专家权重从 DRAM 搬一遍——算术强度只有约 0.94 MAC/byte远低于 roofline 平衡点约 21牢牢落在内存受限区算力是富余的。所以本项目的工程重点是 CPU 侧的带宽利用率kernel 怎么搬得快、线程/NUMA 怎么摆、热专家怎么常驻在 NPU 上少落 CPU。NPU 上的算子本 PR 不动。生产实测 decode 约 13–16 tok/s。模型与硬件模型其余规格hidden_size4096、moe_intermediate_size2048、head_dim512、num_attention_heads64、num_key_value_heads1attention 为 MLA NSA Lightning Indexerindex_topk512。MTPnum_nextn_predict_layers1本项目禁用accept_len 不划算且 sglang 的 NPU NEXTN 另有坑。硬件是单张 Atlas 910B64 GB HBM只用一张 Kunpeng-920 CPU4 socket × 48 192 物理核8 NUMA每 NUMA 约 24 核 / 192 GB共 1.5 TB DRAM。CANN 8.5.0由开源镜像lmsysorg/sglang:deepseek-v4-npu-910b自带。内存带宽是全局瓶颈这里给个量级K920 每 NUMA 3/4 通道 DDR4-3200理论 spec 约 614 GB/s清净独占下实测聚合约 442 GB/s。CPU 侧有一条硬约束要先记住K920 没有 SVE / BF16 / I8MM只有 ARMv8.2-A NEON dotprodasimddp即 SDOT和 FP16asimdhp。任何 SVE/BF16/I8MM 指令svemarch、__bf16、smmla/usdot/ptrue在这块 CPU 上会直接 SIGILL。所以编译 march 固定-marcharmv8.2-afp16dotprodMXFP4 kernel 只用vqtbl1q_s8查表和vdotq_s32SDOT两条指令。后面想换更激进的量化 kernel先过这一关。两份权重因为 NPU 只支持 int8需要两份权重缺一不可NPU 侧用 W8A8 safetensorsint8 fp32 per-channel scale承载 attention、共享专家、router以及常驻 NPU 的那批热专家启动时MODEL_PATH指向它。CPU 侧的转换源是官方原生 MXFP4expert_dtype:fp4E2M1 nibble ue8m0 scale转成 GGUF 后喂给 CPU offload 的专家。原生 MXFP4 专家张量是layers.{L}.ffn.experts.{i}.w1/w3/w2.weightI8K 维 nibble-packed 成 K/2加.scaleF8_E8M0K/32 分组每层专家独占一个 safetensors shard。注意 router gate 和共享专家始终留在 NPU、绝不 offload——它们直接关系到路由和精度放到 CPU 上既无收益又会引入额外往返。系统架构与数据流单卡Atlas 910B (64 GB HBM) K920 (1.5 TB DRAM, 192 核, 8 NUMA) input → [NPU: embedding / RoPE / MLANSAIndexer attention] → [NPU: MoE router gate → topk_ids, topk_weights (k6)] → ┌──────────────────────────┬──────────────────────────────┐ │ NPU experts (默认 N32) │ CPU experts (默认 224) │ │ W8A8 safetensors │ kt-kernel LLAMAFILE GGUF │ │ shared experts (常驻) │ 原生 MXFP4 │ └──────────────────────────┴──────────────────────────────┘ → merge → linear residual → 下一层NPU 端attention 走 SGLang 的--attention-backend ascendMLANSALightning IndexerNPU 上的 MoE 用fused_experts_npuW8A8承载前 N 个路由专家、共享专家和 router top-kKV cache 放 HBM。CPU 端kt-kernelbackend 是 LLAMAFILEkt-kernel/operators/llamafile/moe.hpp。这条路对量化类型是泛化的——buffer 尺寸、激活量化、NUMA 张量并行、加载加速、graph callback 全部经 ggml 的type_traits走换成 MXFP4 不用改这条主线。线程池是 8 个 NUMA worker pool默认--kt-cpuinfer 128 --kt-threadpool-count 8每 NUMA 16 线程留 8 核/NUMA 给 NPU host 和 scheduler。128 是甜点再往上到 192 满核会 thrash 直接崩太低如 24又喂不满带宽。MXFP4 的 GEMV kernel 是ggml_vec_dot_mxfp4_q8_0llama.cpp patch 0002vqtbl1q_s8查 E2M1 表vdotq_s32做 SDOT再乘 e8m0 scale。这里有个对 K920 很关键的优化——行内__builtin_prefetch(512B)加双float32x4FMA 累加链TSV110 的硬件预取器跟不上 MXFP4 这种低密度 load 流手工行内软预取把单核 GEMV 从约 0.9 提到约 3.2 GB/s/核整个 kernel 约 2.4×。激活在线量化到 Q8_0vec_dot_typeQ8_0是整条路径唯一的数值损失源。NPU↔CPU 桥kt-kernel/cpu_backend/ascend_callback_worker.{cpp,h}起一个后台线程做aclrtSubscribeReport 循环aclrtProcessReport把 CPU MoE 的 submit/flush 接进 NPU graph 的 host callback。这里有个坑ACL 的aclrtLaunchCallback不会自动触发必须有专用 poller 线程去 subscribe process否则会卡在 sync、NPU 空转。SGLang 集成核心是 per-layer 的KTMoEWrapper…/layers/moe/kt_ep_wrapper.py负责mask_cpu_expert_routing、prefill/decode 分化、graph 走 host callback设备抽象在…/utils/kt_accel.py。当 triton 与 ascend 版本错配时会自动探测、回退到纯 PyTorch 的等价实现数值等价不需要任何开关。集成方式是在 sglang DSv4 基线上加分支/继承不 fork 整个模型实现这样后续升级子模块不容易被破坏。量化CPU 侧原生 MXFP4MXFP4 是官方发布的量化训练侧已经对齐转成 GGUF 全程是 bit 级无损 repack不是再量化一次。所以 CPU 专家用 MXFP4、NPU 专家用 W8A8 混用没有问题——各专家独立近似同一份母权重。离线对账 cosine 0.999939、max_rel 1.12%唯一的损失来自激活在线量化到 Q8。几个量级数字每元素 0.53125 字节17 B / 32 元素单个专家gateupdown13.4 MB最坏情况一层 top-6 全落 CPU 是 80 MB43 层全部常驻 DRAM 约 137 GiB。转换里有一个核心雷区是nibble 序官方 ckpt 是 consecutive 排布byte i 存第 2i / 2i1 个 nibble而上游 GGUF 是 half-block 排布qs[j]存第 j / j16 个。转换器必须逐 32-group 重排 nibble不能直接 byte copye8m0 scale 字节则原样直存。转换器和 kernel 必须用同一套约定错了不会报错、只会数值不对——所以verify_mxfp4_layer.py的 bit-exact 对账是裁判改这条路径前后都要跑。二、当前状态跑通了什么v1.0 开源即带单卡整网已经 HTTP 200、输出连贯编译期 NPU 适配main_repo/0001单卡整网 wiringSGLang CPU MoE offloadsglang/0002NPU graph ACL callback worker 闭合main_repo/0001sglang/0002CPU 权重加载加速zero-copy mmap 并行重排43 层约 47smain_repo/0001、0002graph decode 提速kt-cpuinfer 24→128 GEMV 行内预取main_repo/0002CPU MoE 直接吃原生 MXFP4无损 repack、不重新量化 kernel 行内预取 2.4×decode 约 13–16 tok/smain_repo/0002llama_cpp/0002triton×ascend 自动回退 torch 等价KV / MoE无需 envsglang/0001静态热专家放置前 32 个常驻 NPU--kt-num-gpu-experts 32接住约 13% 的激活sglang/0002吞吐与影响因素场景decode清净独占NPU 空卡 CPU 无邻居争抢kt-cpuinfer 128graph-on约 16 tok/s中等争抢共享机有邻居吃 DRAM 带宽约 13–14 tok/seager--disable-cuda-graph仅排障/对照约 1.6 tok/s以上是单发、--max-running-requests 1、短上下文下的稳态 decode首 token 含约 2–3.5 min 加载热 cache 后。会让吞吐掉下来的情况按常见程度共享机邻居争抢 DRAM 带宽最常见decode 内存受限邻居吃带宽会直接抬高cpu_moe_wall8-NUMA 取 max 又放大尾延迟表现为 median−min 抖动独占约 16 → 争抢约 13–14。NPU 卡被别的容器/session 占用拉服务前先npu-smi info选一张空卡。KT_CPUINFER设错192 满核 thrash 会崩太低喂不满带宽128 是甜点。并发多发--max-running-requests 1会撞 NPU 争抢窗口runtime 失稳甚至崩。上下文变长NPU attention 随序列增长变慢长对话 decode 会逐步降速。冷盘首启page cache 没热时加载和首 token 慢但不影响稳态 decode。路由偏斜某个 token 的 top-6 恰好全落 CPU 的层会多搬字节见下面均值 vs 最坏的差别。Roofline为什么是带宽、差多少decodebs1是内存受限问题每 token 的开销主要是把激活到的专家权重从 DRAM 搬一遍。每 token CPU 要搬的字节大致是top-6 × (1 − 静态热专家命中率) × 单专家 13.4 MB × 43 层 ≈ 6 × (1 − ~13%) × 13.4 MB × 43 ≈ ~3.0 GB / token 交叉验证每 token 读约 224 个专家 ÷ 9632 常驻 × 137 GiB ≈ 3.2 GB拿这个字节量对带宽算 roofline再和实测比量值每 token CPU 字节~3.0 GBDRAM 带宽清净聚合 ~442 GB/s 真 spec 614 GB/s理想 cpu_moeroofline~6.8 ms442 ~4.9 ms614实测cpu_moe_wallmin ~17 msmedian ~22–27 msgap~2.5–3.5×gap 来自激活在线 Q8 量化 merge submit/sync 开销 NUMA 负载不均max-of-8 的尾巴 共享机带宽低于 spec。算力这边算术强度 0.94 MAC/byte 远低于平衡点 21FLOPs 不是墙所以 CPU 侧的杠杆在带宽利用率、以及让更少的专家落到 CPU见 roadmap而不是算力。三、Roadmap主线device-offload让更多命中在 NPU 上算权重字节由原生 MXFP4 格式定死CPU 直接吃、不再量化所以压cpu_moe_wall的杠杆是两个减少落到 CPU 的专家数、提高 CPU 侧带宽利用率。这条主线讲前者——让更多命中的专家常驻/计算在 NPU 上落到 CPU 的就更少。它分三步是同一条线的不同侧面静态热专家常驻已带 v1.0前 32 个专家用--kt-num-gpu-experts 32常驻 NPU接住约 13% 的激活。动态热专家常驻 / EPLB在做机制已验证未并入 v1.0按 activation 频次取最热的专家常驻。动态 top-K 比静态 prefix-32 大约多接 3× 的激活精度正确预计能拿到约 2× 提速。这里踩过一个坑real-topK 输出乱码的根因是常驻权重 gather 走了 host 的 NZ 池切片host 切片 format-unaware、字节错乱改成设备切片后修复。overlap 专家预测 / H2D 预取后续提前一个 token / 一层预测会命中的 expert用当前 token/层的计算时间异步把它的权重 H2Dhost→NPU HBM预取让搬运被计算掩盖同时让 CPU MoE 与 NPU 重叠。命中动态前移到 device落 CPU 的专家更少、cpu_moe_wall的阻塞更小。其它在做 / 计划长序列 prefill 流式加载另一条线已满配跑通约 8×未并入 v1.04096 prefill 约 14s对比约 137s搬的同样是 MXFP4 GGUF。kernel 预取距离扫描 / NUMA 负载均衡后续用来缩小cpu_moe_wall相对内存 roofline 的 2.5–3.5× gap。【免费下载链接】cann-recipes-infer本项目针对LLM与多模态模型推理业务中的典型模型、加速算法提供基于CANN平台的优化样例项目地址: https://gitcode.com/cann/cann-recipes-infer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考