社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

TileLang × xLLM:Python DSL 驱动 Qwen3.5 昇腾算子适配

昇腾CANN • 1 周前 • 49 次点击  
图片

背景介绍

Qwen3.5 是通义千问系列大语言模型,其架构引入了 Gated Delta Network(GDN)等创新模块,在提升模型表达能力的同时,也带来了 causal_conv1dfused_sigmoid_gating_delta_rulechunk_gated_delta_rule_fwd_h 等一批计算密集、逻辑复杂的定制算子。这些算子在推理过程中被高频调用,其执行效率直接决定了模型的整体推理性能。

xLLM 框架此前已接入这些算子,但其在昇腾 NPU 上的运行性能未达预期。与此同时,xLLM 已具备 TileLang 算子接入能力——开发者用 Python 编写算子 Kernel,再由编译器将其编译为针对昇腾 NPU 优化的 C++ 代码。为此,我们基于 TileLang-Ascend 路线对 Qwen3.5 的关键算子进行了重写,在保持开发效率的同时,显著提升算子在昇腾 NPU 上的执行性能。

整体架构

┌─────────────────────────────────────────────────────────────────┐
│                        xLLM 推理引擎                             │
│                                                                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                    C++ Wrapper 层                         │  │
│  │   Tensor 校验 → 构建 Specialization → 查找 Kernel → 调用     │  │
│  └───────────────────────┬───────────────────────────────────┘  │
│                          │ 运行时调度                            │
│  ┌───────────────────────▼───────────────────────────────────┐  │
│  │              TileLang 编译产物 (.so)                       │  │
│  │   causal_conv1d │ fused_sigmoid_gating │ chunk_gated_delta│  │
│  └───────────────────────▲───────────────────────────────────┘  │
│                          │ AOT 编译                              │
│  ┌───────────────────────┴───────────────────────────────────┐  │
│  │              TileLang Python Kernel                       │  │
│  │   @T.prim_func → generate_source() → Ascend C 源码         │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

xLLM三步接入 TileLang 算子

xLLM 接入一个新 TileLang 算子只需三步:写 Python Kernel → 编译 → 注册

  Step 1                Step 2                Step 3
┌──────────┐    ┌────────────────┐    ┌───────────────┐
│ Python   │    │ AOT 编译       │    │ C++ Wrapper    │
│ Kernel   │───▶│ Ascend C 源码  │───▶│ + CMake 注册   │
│ 编写      │    │ → .so 动态库   │    │ → 集成验证      │
└──────────┘    └────────────────┘    └───────────────┘

Step 1:Python Kernel 编写

以 chunk_gated_delta_rule_fwd_h 为例,使用 TileLang DSL 定义算子计算逻辑 (以下为简化后的概念性伪代码,主要展示 TileLang 的表达逻辑):

@T.prim_func
def chunk_gated_delta_rule_fwd_h_kernel(
    h: T.Tensor, k: T.Tensor, v: T.Tensor,
    w: T.Tensor, g: T.Tensor, h0: T.Tensor, ht: T.Tensor, ...
)
:

    with T.Kernel(total_tasks, is_npu=Trueas (cid, vid):
        # 分配片上存储:UB(向量计算)、L1/L0C(矩阵计算)
        h_state_ub = T.alloc_ub([2, K // 2, V_half], "bfloat16")
        h_state_float = T.alloc_ub([2, K // 2, V_half], "float32")
        k_chunk_l1 = T.alloc_L1([2, bt, K], "bfloat16")
        wh_frag = T.alloc_L0C([2, bt, V_half], "float32")

        for pair_idx in T.serial(num_pairs):
            # 加载初始状态(float32 精度)
            T.copy(h0[i_n, i_h, ...], h_state_float)

            for i in T.serial(NT_i):
                # Cube 核心:矩阵乘法 w @ h、k @ v_new
                with T.Scope("C"):
                    T.gemm_v0(w_chunk_l1, h_state_l1, wh_frag)
                    T.gemm_v0(k_chunk_l1, v_new_l1, hupd_frag, transpose_A=True)

                # Vector 核心:逐元素计算 delta rule 状态更新
                with T.Scope("V"):
                    # v_new = v - w @ h
                    T.tile.sub(v_chunk_float, v_chunk_float, wh_float)
                    # h *= exp(g_last),v_new *= exp(g_last - g)
                    T.tile.mul(h_state_float, h_state_float, g_last_scalar)
                    T.tile.mul(v_chunk_float, v_chunk_float, g_exp_broc)
                    # h += k @ v_new
                    T.tile.add(h_state_float, h_state_float, hupd_float)

            # 写回最终状态(float32 精度)
            T.copy(h_state_float, ht[i_n, i_h, ...])

核心要点:

  • Cube/Vector 双核协作T.Scope("C") 调度矩阵乘法(T.gemm_v0),T.Scope("V") 调度逐元素向量计算(T.tile.sub/mul/add/exp
  • Double Buffer 流水线:得益于底层自动映射双缓冲,实现数据搬运与计算的高效重叠,屏蔽底层信号量同步细节。
  • 精度控制:SSM 状态 h_state_float 全程以  float32 存储和更新,确保递推计算的数值稳定性

Step 2:AOT 编译

TileLang 编译器负责将 Python 表达的 Tiling 逻辑和计算流水线,自动映射为 Ascend 的标准指令,并自动计算和填入对应硬件的 Tiling 参数,从而避免了手动计算 L1/UB 偏移量的繁琐工作。通过 generate_source(...) 将 Python Kernel 自动降级为 Ascend C 源码,再由毕昇编译器编译为动态库:

@register_kernel
class ChunkGatedDeltaRuleFwdHKernel(TilelangKernel):
    SPECIALIZATIONS = [
        {"variant_key""H32_Hg8_D256_bf16",
         "num_heads"32"num_groups"8"head_dim"256"dtype""bf16"},
    ]

    @staticmethod
    def generate_source(num_heads, num_groups, head_dim, dtype) -> str:
        kernel = build_chunk_gated_delta_rule_fwd_h_kernel(...)
        kernel = tilelang.engine.lower(kernel)
        return kernel.kernel_source

编译命令一行搞定:

python xllm/compiler/tilelang_launcher.py compile-kernels \
    --target ascend --device a3 \
    --kernels chunk_gated_delta_rule_fwd_h

Step 3:C++ Wrapper 注册

编写 Wrapper 桥接 xLLM 运行时与编译产物,并在 CMake 中一行注册:

const auto* entry = find_chunk_gated_delta_rule_fwd_h_kernel_entry(specialization);
entry->fn(q_ptr, k_ptr, v_ptr, g_ptr, h0_ptr, h_out_ptr, stream);
tilelang_register_runtime_kernel(
    NAME chunk_gated_delta_rule_fwd_h
    WRAPPER_SRCS chunk_gated_delta_rule_fwd_h_wrapper.cpp
)

Qwen3.5 关键算子适配

Qwen3.5 的 GDN 架构逻辑复杂且访存密集,若采用传统 Ascend C 原生开发,开发者需要耗费大量精力处理底层的 UB/L1 内存管理、Cube/Vector 硬件同步及寄存器分配。通过引入 TileLang,我们将底层硬件细节交由编译器处理,使开发者能专注于算法逻辑,大幅缩短了核心算子的开发与验证周期。

以下是三个核心算子的适配细节与收益:

causal_conv1d:因果一维卷积

该算子负责对输入序列执行因果卷积(仅依赖当前及历史 token),并维护卷积状态(conv_state)以支持增量 Decode。在长序列场景下,访存与计算的重叠是性能优化的关键。

  • Token-Block 自动流水线:为实现计算与访存的高效重叠,我们需要将序列按核数切分为连续的 token 块,并在块内串行计算 4 个 token。得益于 TileLang 的双缓冲(Double Buffer)原语,开发者只需使用简单的 T.Pipelined,编译器即可自动生成 Ascend 架构下的软硬件同步指令(如 DataCopy 异步搬运),免去了手动编写繁琐 Pipeline 同步信号的痛苦。
  • 灵活的状态管理:通过 cache_indices 指定 batch 对应的 cache line,实现 prefix 缓存复用。TileLang 的 Python DSL 允许我们通过标准的控制流(if initial_state_mode == ...)快速定义状态加载逻辑,底层自动映射为高效的向量化读写指令。
  • 开发周期:借助自动流水线生成,原先在 Ascend C 中需要反复调优内存 offset 和流水线节拍的逻辑,仅用 3 周即完成了从 Python Kernel 编写、精度对齐到性能调优的全流程。

chunk_gated_delta_rule_fwd_h:分块门控 Delta Rule 前向

该算子实现 Chunk 级别的分块递推计算,是 Prefill 阶段的核心算子,难点在于矩阵乘(Cube)与状态递推(Vector)的异构协同,以及极高的精度要求。

  • Cube/Vector 双核无缝协作:TileLang 提供了极简的 T.Scope("C") 和 T.Scope("V") 抽象。我们将 Chunk 内的矩阵乘(w @ hk @ v_new)映射到 Cube,将 h *= exp(g_last) 等逐元素计算映射到 Vector。TileLang 编译器会自动完成两者之间的内存排布转换与执行调度,彻底屏蔽了 Ascend 底层矩阵格式(如 Fractal_NZ)转换的复杂性。
  • 零负担的精度控制:状态矩阵 h 需全程以  float32 更新以避免精度坍塌。在 TileLang 表达中,开发者只需在分配 UB 内存时显式指定 T.alloc_ub(..., "float32"),编译器就会自动插入必要的 Cast 指令,实现 bfloat16 计算与 float32 状态存储的无缝衔接。
  • 开发周期:由于 TileLang 自动计算并填入了复杂的 Tiling 参数(避免了手动计算 L1/L0C 内存上限的灾难),这一逻辑极为复杂的递推算子,核心开发仅耗时 2 周,便实现了 1.72x 的性能提升。

fused_sigmoid_gating_delta_rule:融合 Sigmoid 门控 Delta Rule

该算子主要在 Decode 阶段逐 token 执行,将 sigmoid 激活、门控计算和 Delta Rule 状态更新融合为单一 Kernel,是 GDN 层的绝对算力瓶颈。

  • AOT 解决多算子融合的寄存器分配难题:在处理此类复杂融合算子时,将 A_log(衰减率)、querykey valuebeta 等数十个输入输出融合在一个 Kernel 内,面临极易溢出的寄存器分配(Register Spilling)与内存对齐问题。TileLang 的编译器基于强大的底层 IR 分析能力,对 Python 编写的一系列 T.tile.exp/add/mul 数学表达式进行了自动的算子融合与生命周期管理,确保中间结果直接在寄存器或 UB 中流转,无需写回全局内存,极致降低了访存开销。
  • 变长序列自动处理:通过传入 cu_seqlens 等动态 shape 信息,TileLang Kernel 在运行时自动处理 batch 内不同长度序列的独立状态指针偏移,开发者在 Python 侧仍可按照规则的稠密张量思维进行逻辑表达。
  • 开发周期:原本需底层专家耗时数月进行汇编级优化的超级融合大算子,借助 DSL 的数学表达优势和编译器的自动优化,仅需 3 周即完成了开发上线,大幅加速了 Qwen3.5 整体模型在昇腾上的落地进程。

Qwen3.5 + TileLang 收益

算子
Baseline(us)
TileLang(us)
性能提升比
causal_conv1d(Decode)
11173
4518
2.47x
chunk_gated_delta_rule_fwd_h
4702
2727
1.72x
fused_sigmoid_gating_delta_rule(Decode)
126
104
1.21x
(注:上表耗时数据基于特定测试条件获取。其中,causal_conv1d 呈现的为 1200 次调用的总累计耗时。)



总结

TileLang + xLLM 为 Qwen3.5 在昇腾 NPU 上的算子适配提供了一条高效路线:

  • 性能高: 关键算子最高实现 2.47x 加速,显著优于 Baseline 实现。
  • 接入简单:Python 写 Kernel,AOT 自动编译,新增算子只需遵循三步模板。
  • 可复用:标准化接入框架不仅适用于 Qwen3.5,也为后续新架构模型的快速部署奠定基础。
图片

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/198399