Skip to content

perf: optimize causal conv1d and mega chunk gdn for qwen3.5.#1999

Open
Sinle4Cat wants to merge 3 commits into
xLLM-AI:mainfrom
Sinle4Cat:prefill_opt_conv1d_gdn
Open

perf: optimize causal conv1d and mega chunk gdn for qwen3.5.#1999
Sinle4Cat wants to merge 3 commits into
xLLM-AI:mainfrom
Sinle4Cat:prefill_opt_conv1d_gdn

Conversation

@Sinle4Cat

@Sinle4Cat Sinle4Cat commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

背景与目标

Qwen3.5 Gated DeltaNet 的 chunk prefill 路径原先按以下阶段执行:

CausalConv1d
-> reshape / split / QK L2Norm / pack QKV
-> MegaChunkGdn(FP16)

该路径在 Conv1d 与 MegaChunkGdn 之间产生额外的张量变换、Q/K 归一化、dtype 转换和中间张量读写;MegaChunkGdn 内部部分完全覆写的输出也使用了 zeros 初始化。

本 PR 将 CausalConv1d 的 prefill 输出直接整理成 MegaChunkGdn 所需的 packed Q/K/V 布局,并为 MegaChunkGdn 增加 BF16 计算路径,减少中间算子和无效初始化。所有优化均带有运行时 contract 检查,不满足条件时继续走原始实现。

接入方案

1. CausalConv1dQkv direct-layout 输出

  • 在 NPU kernel wrapper 中接入 aclnnCausalConv1dQkv
  • Conv1d kernel 同时完成 SiLU、Q/K L2Norm 和 Q/K/V packed 输出。
  • 使用 narrow + view 将 packed buffer 零拷贝包装为 [1, T, H, 128],避免额外 split、concat 和 pack。
  • Conv cache 更新语义保持不变。

2. 通用 prefill 路由

  • 支持普通 prefill、chunk prefill 和 ragged sequence metadata。
  • 根据本地 head 数、序列 metadata 和实际 tensor contract 判断是否准入。
  • 保留 kernel 的真实约束:BF16、head dimension 128、Conv width 4、cache shape 和 contiguous layout。
  • 任一 shape、dtype、metadata 或 layout 条件不满足时,回退到原始 CausalConv1d + process_mixed_qkv 路径。

3. MegaChunkGdn BF16 specialization

  • 当 Q/K/V 均为 BF16 时,MegaChunkGdn 直接使用 BF16 计算,不再强制转换为 FP16。
  • 分别缓存 FP16/BF16 的 minus_identity mask,保证输入 dtype 与 kernel contract 一致。
  • direct-layout 路径已在 Conv kernel 内完成 Q/K L2Norm,因此关闭 MegaChunkGdn 内重复的 Q/K L2Norm。

4. 跳过无效输出初始化

  • 将 kernel 会完整覆写的 aa_invhfinal_state 等中间/输出张量由 zeros 改为 empty
  • 未提供 initial state 时仍保留必要的零初始化,不改变状态语义。

定位过程中遇到的问题与解决方法

代码改动规模与分布

当前 PR 相对目标分支共修改 5 个文件,新增 222 行、删除 53 行,净增 169 行

模块 文件 主要内容
xLLM NPU kernel wrapper npu_causal_conv1d.cppnpu_ops_api.h CausalConv1dQkv ACL 调用及 packed Q/K/V 零拷贝 view
Qwen3.5 GDN 模型路径 qwen3_gated_delta_net_base.cpp direct-layout 准入、chunk/ragged metadata、fallback 和 MegaChunkGdn 接入
MegaChunkGdn wrapper npu_mega_chunk_gdn.cpp BF16 specialization、dtype 对应 mask、跳过无效初始化
算子依赖 third_party/xllm_ops 更新到包含 CausalConv1dQkv 与 MegaChunkGdn BF16 支持的版本

依赖关系

  • xllm-ops #15,已合入;本 PR 使用提交 98a8b346a8b545ca1d21f50d465b5ff6c4a4c63a

验证环境

项目 配置
模型 Qwen3.5-27B
并行配置 TP2,MTP speculative tokens=3
dtype BF16
Prefill chunked prefill enabled,chunk size 1024
Decode graph true
测试分支 prefill_opt_conv1d_gdn
PR HEAD b21f02f25ae95ea07dcae7fc5fef6155bd3b9dbc
xllm-ops 98a8b346a8b545ca1d21f50d465b5ff6c4a4c63a

验证结果

当前 PR HEAD 构建与测试

  • python setup.py build:通过。
  • CTest:837 passed、0 failed、29 disabled,共 866 项;仓库测试脚本最终输出 All tests passed!
  • clang-format hook:通过。

算子精度与 shape 覆盖

  • Qwen3.5 0.8B、9B、27B、35B-A3B 与 TP 1/2/4/8/16 交叉矩阵:20/20 通过。
  • 覆盖三条 ragged sequence(长度 5/7/9)、非连续 cache index 和 mixed initial-state flags。
  • packed 输出与原始路径最大绝对误差为 0.0;conv cache 最大绝对误差为 0.0
  • 覆盖最大 local width 10240(27B TP1)和最小 local width 384(0.8B TP16)。

性能

ATK 大 shape BF16 对比中,优化路径均快于原路径:

Case E2E Device
Qwen3.5-27B TP1, S=2048 283.741 us -> 280.890 us(-1.00%) 277.005 us -> 272.991 us(-1.45%)
Qwen3.5-27B TP1, S=4096 544.008 us -> 538.563 us(-1.00%) 536.747 us -> 531.185 us(-1.04%)

模型

输入长度 校正 baseline TTFT 校正 candidate TTFT 节省 收益 95% CI
2048 475.62 ms 470.04 ms 5.58 ms 1.20% 1.01%~1.49%
4096 888.91 ms 856.05 ms 32.86 ms 3.71% 3.65%~3.80%

模型 C-Eval

同一优化分支在 rebase 前的等价实现完成 Qwen3.5-27B TP2 MTP 全量 C-Eval:

  • 52 个子集,1346/1346 样本完成,API 请求 1347 成功、0 失败。
  • micro accuracy 0.9138,macro accuracy 0.9163
  • 模型启动、生成冒烟和服务运行均通过,未发现 kernel/runtime 错误。

PR 范围

本 PR 只包含 Qwen3.5 NPU prefill 的 CausalConv1dQkv direct-layout 接入、MegaChunkGdn BF16 specialization、无效初始化消除及对应 fallback。Decode 路径和不满足 contract 的输入仍使用原始实现。

Change Type

  • Bug fix
  • New feature
  • Performance improvement
  • Refactor
  • Documentation
  • Test
  • Build or CI

@Sinle4Cat Sinle4Cat changed the title Prefill opt conv1d gdn perf: optimize causal conv1d and mega chunk gdn for qwen3.5. Jul 21, 2026
auto h = torch::zeros({num_matrices, K, V}, opts_fp16);
auto v_new = torch::empty({B, T, H, V}, opts_fp16);
auto beta_t = torch::empty({H, T}, opts_compute);
auto a = torch::empty({B, T, H, kMegaChunkSize}, opts_compute);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zero 和 empty 没区别是吗?都是安全的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants