训练营简介 2025年昇腾CANN训练营第二季,基于CANN开源开放全场景,推出0基础入门系列、码力全开特辑、开发者案例等专题课程,助力不同阶段开发者快速提升算子开发技能。获得Ascend C算子中级认证,即可领取精美证书,完成社区任务更有机会赢取华为手机,平板、开发板等大奖。

报名链接:https://www.hiascend.com/developer/activities/cann20252#cann-camp-2502-intro

前言

在 AI 模型 Profiling 中,新手开发者常会感到困惑: “为什么我的 MatMul 算子优化到了极致,但整个模型跑起来还是很慢?”

打开 Timeline 一看,发现大量的 TransposePermuteReshape 算子占据了 30% 甚至 50% 的时间。这些算子不进行任何加减乘除,只负责把数据从内存的一个地方搬到另一个地方,并改变排列顺序。

对于 AI Core 这种专为连续访存优化的硬件来说,乱序搬运(Random Access)简直是噩梦。 本期我们将深入底层,解析如何通过地址步长(Stride)和分块搬运,将“乱序”转化为“有序”,实现高效的数据重排。

一、 核心图解:仓库大重组

我们可以把 Global Memory 想象成一个巨大的仓库,数据箱子按顺序摆放(Row-Major)。 Transpose 的任务是:把“横着放”的箱子变成“竖着放”。

  • 笨办法:把箱子一个个搬出来,再一个个插空放回去。这叫离散访存,效率极低。

  • 聪明办法:开一辆侧面开口的叉车(MTE),一次性铲起一列箱子,直接横过来放。这叫跨步搬运(Strided Copy)

二、 核心武器:DataCopy 的 Stride 机制

Ascend C 的 DataCopy 接口远比我们想象的强大。它不仅仅能做 memcpy,还能在搬运过程中做数据整形

API 原型(简化版):

// DataCopyParams { blockCount, blockLen, srcStride, dstStride }
DataCopy(dstLocal, srcGlobal, params);
  • blockLen: 每次连续搬运的长度(连续的一小段)。

  • srcStride: 搬完一段后,源地址跳过多少才开始搬下一段。

  • dstStride: 搬完一段后,目标地址跳过多少才开始写下一段。

2.1 案例:矩阵转置 (Matrix Transpose)

假设我们要转置一个 $32 \times 32$ 的 FP16 矩阵。

  • Src: 32 行,每行 32 个元素(64 Bytes)。

  • Dst: 转置后的 32 行。

策略:分块转置 (Tiled Transpose) 由于 UB 大小有限,我们通常把大矩阵切分成 $16 \times 16$ 的小块(Tile)。 利用 UB 进行局部转置

  1. Load: 从 GM 读取一个 $16 \times 16$ 的块到 UB。这里利用 srcStride 可以直接读出原矩阵的子块。

  2. Transpose: 在 UB 内部进行转置。这需要 Vector 单元的特殊指令或者 MTE3 的写回能力。

  3. Store: 将转置好的块写回 GM。

但在 Ascend C 中,最简单的转置其实是利用 MTE3 的能力或者Vector 的 Scatter/Gather

如果仅仅是二维矩阵转置,Ascend C 提供了专门的指令(视具体芯片而定,如 Transpose 或通过 ConfusionTranspose 配合)。但在通用编程中,我们更常用 CopyIn (带 Stride) + Vector Shuffle 的组合。

三、 实战:实现二维矩阵转置

为了通用性,我们这里演示一个利用 UB 中转 + 跨步读写 的逻辑。

场景:将 $(H, W)$ 矩阵转置为 $(W, H)$。

3.1 Kernel 逻辑

__aicore__ inline void Compute(int32_t i) {
    // 假设我们处理一个 16x16 的小块 (FP16)
    // 16 * 16 * 2B = 512 Bytes
    LocalTensor<half> inputBlock = inQueueX.DeQue<half>();
    LocalTensor<half> outputBlock = outQueueY.AllocTensor<half>();

    // 核心难点:如何在 UB 内做 16x16 转置?
    // Ascend C 提供了这种 Intra-UB 转置指令吗?
    // 答案:通常没有直接的 "Transpose(ub, ub)" 指令用于通用数据。
    // 我们通常的做法是:
    // 1. 如果是特殊格式 (Fractal),有专门指令。
    // 2. 如果是通用 ND 格式,我们利用 "多维搬运" 或者 "Scatter" 指令。
    
    // 这里演示一种利用 MTE2/MTE3 硬件特性的 "伪转置" (利用地址变换)
    // 但更硬核的方法是使用 scatter_store (离散写)
    
    // 1. 设置 Atomic Add (非必须,但 Scatter 常用)
    // 2. 利用 Scatter 指令 (UB -> GM 离散写)
    // 假设我们已经把数据搬到了 inputBlock (按行存)
    // 我们要按列写回 GM
    
    // 构造 offsets: [0, W, 2W, 3W ...]
    // 这需要提前在 UB 准备好一个 offset 向量
    
    // 这是一个非常昂贵的操作。
    // 所以,工业界的标准做法是:
    // 1. 将矩阵分块为 16x16。
    // 2. Load 16x16 到 UB。
    // 3. 在 UB 内利用 VNCHG (Vector Change) 类指令做 Intra-Block Transpose。
    //    或者利用 MTE 的转换能力。
    
    // 简易版:假设有硬件支持的 Transpose 指令 (如 Ascend 910B)
    Transpose(outputBlock, inputBlock); 
    
    // 如果没有,对于特殊 Shape,我们可以通过改变 DataCopy 的 Stride 
    // 来实现 "读取一行,写成一列" (需硬件支持该模式)
    
    outQueueY.EnQue(outputBlock);
}

 修正视点: 实际上,纯软件在 UB 内做乱序转置效率极低。昇腾架构中,高效转置通常依赖 Cube 单元的格式转换硬件 (Format Conversion)。 即:GM(ND) -> L1 -> Cube(Fractal Z) -> UB(ND)。 Cube 单元在搬运过程中,天然支持将 $16 \times 16$ 的小块进行重排。

所以,写 Transpose 算子的最高境界,其实是借用 Matmul! 构造一个单位矩阵 $I$,计算 $A \times I$ 或者 $I \times A$,利用 Matmul 强大的内存重排能力顺便完成转置。

3.2 借力打力:用 Matmul 做 Transpose

这是一个非常有创意的技巧。 目标:$B = A^T$。 构造:$C = A^T \times I$ (这还是 Matmul)。 但在 Ascend C 中,我们可以配置 Matmul 的输入格式为 ND,输出格式为 ND,并在 Tiling 中指定 "Transpose A" 属性(如果 API 支持)。

如果 API 不支持直接 Transpose A,我们通常回归到 UB 级别的 Gather

  1. 生成索引向量 indices = [0, 16, 32, ..., 1, 17, 33...]

  2. Gather(dst, src, indices)

// UB 内部 Gather 实现转置
// src: 16x16 row-major
// dst: 16x16 col-major
// indices: 预计算好的转置索引
Gather(dstLocal, srcLocal, indicesLocal, 256);

四、 进阶:Permute (多维转置)

对于 [B, H, S, D] -> [B, S, H, D]: 这本质上是把 HS 两个维度换了位置。 如果 HD 都很小(比如 $H \times D = 128$ bytes),我们可以把 H-D 看作一个整体搬运。

Tiling 策略: 始终寻找**“最大连续块”**。 在上面的例子中,D 维是连续的。我们可以一次搬运 D 个元素,然后跳过 (S-1)*D 的距离去搬下一个 D

五、 总结

Transpose 和 Permute 是对 MTE 带宽 的极致考验。

  1. 避免算力浪费:不要试图用标量循环去赋值,那是 CPU 干的事。

  2. 利用硬件特性:首选 DataCopystride 参数;其次利用 Gather 指令。

  3. 终极技巧:对于大矩阵转置,考虑将其转化为矩阵乘法(乘单位矩阵),利用 Cube 单元的专用电路进行数据重排。

掌握了乾坤大挪移,你就学会了如何在不消耗算力的情况下,玩弄数据于股掌之间。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐