Ascend 950PR (3510) NDDMA 多维搬运参数填写教程

目标芯片:Ascend 950PR / DT(Atlas A5),__NPU_ARCH__ == 3510
API 层级:c_api(asc_ndim_copy_gm2ub)+ basic_api(DataCopy + MultiCopyParams
搬运方向:GM → UB(仅 AIV 核执行;)
支持维度:1 ~ 5 维
支持类型:int8/uint8/int16/uint16/half/bfloat16/int32/uint32/float + fp8(e4m3/e5m2/e8m0) + fp4x2(e1m2/e2m1);basic_api 额外支持 int64/uint64/double(仅常量 0 填充)


1. NDDMA 是什么

NDDMA(N-Dimensional DMA)是 3510 上专用的多维硬件搬运引擎,能在一次调用中完成最多 5 维张量的 GM→UB 搬运,并原生支持:

  • 每维独立 stride(src/dst 分别设置,可做转置、广播、切片)
  • 每维独立 pad(左/右各 0~255 元素)
  • 两种填充模式:常量填充(constant fill)/ 邻边复制(nearest-value replicate)
  • L2 cache 模式控制
  • 32 KB NDDMA DataCache(多核共享 GM 时需 dci 刷新)

相比传统 asc_copy_gm2ub(仅 2D burst 模式),NDDMA 省去了软件嵌套循环与多次 DMA 启动开销。


2. 两套 API 对照

维度 c_api(低层) basic_api(高层 C++)
主搬运函数 asc_ndim_copy_gm2ub(...) DataCopy<T, dim, config>(dst, src, params)
参数传递 有状态:先调 setter 写 SPR 寄存器,再调 copy 无状态:参数全在 MultiCopyParams 里一次性传入
维度声明 隐式(未用到的 loopN_size 填 1) 显式模板参数 dim
填充模式极性 padding_mode=true 表示常量填充 isNearestValueMode=true 表示邻边复制(极性相反
填充值 asc_set_ndim_pad_value(v)(全局 SPR) params.constantValue(按调用传入)
Stride 5 次 asc_set_ndim_loopN_stride 调用 loopInfo.loopSrcStride[N] / loopDstStride[N] 数组
Pad 计数 dim0 在 copy 调用参数;dim1-4 在 asc_set_ndim_pad_count 全部在 loopInfo.loopLpSize[N] / loopRpSize[N]
L2 cache 调用参数 l2_cache_mode GlobalTensor::SetL2CacheHint(mode) 预设
Cache 刷新 asc_ndim_copy_dci() NdDmaDci()
64 位类型 ✗ 不支持 ✓(int64/uint64/double,仅常量 0 填充)

选择建议:新代码用 basic_api(无状态、类型安全、支持 b64);需要极致控制或与 c_api 代码混用时用 c_api。


3. 维度与索引约定

NDDMA 最多 5 维,索引 0 是最内层(lowest)维度,索引 dim-1 是最外层(highest)维度

loop4 (outermost) ─┐
loop3              │
loop2              │  N 维张量
loop1              │
loop0 (innermost) ─┘

对于 2D 场景(如矩阵):loop0 = 行内元素(列方向),loop1 = 行间跳转(行方向)。

规则

  • 实际维度为 N 时,loop{0..N-1}_size 填真实元素数(≥1),loop{N..4}_size1
  • 未使用的 stride setter 也建议显式调用 (0, 0),避免残留 SPR 状态污染。

4. 参数语义总表

4.1 每维参数

参数 单位 范围 含义
loopN_size 元素 [1, 2^20-1] 该维实际搬运元素数(不含 pad)
loopN_src_stride 元素 [0, 2^40-1] GM 中跳到下一元素的距离;0 = 广播(重读同一地址)
loopN_dst_stride 元素 [0, 2^20-1] UB 中跳到下一元素的距离;0 = 覆写同一 UB 位置
loopN_lp_count 元素 [0, 255] 该维左侧 pad 元素数
loopN_rp_count 元素 [0, 255] 该维右侧 pad 元素数

4.2 全局参数

参数 类型 含义
padding_mode / isNearestValueMode bool 填充策略 )
pad_value / constantValue 同 T 常量填充值(仅 padding_mode=true / isNearestValueMode=false 生效)
l2_cache_mode enum L2 缓存策略

4.3 地址对齐约束

  • src 起始地址:按 sizeof(T) 字节对齐
  • dst 起始地址32 字节对齐(硬性要求)
  • 各维地址跨度累加不超过 2^40 bit
  • 相邻维 dst_stride 递增时,各维地址区间不得交错/重叠

5. 填充模式(两种极性,务必注意)

模式 c_api 设置 basic_api 设置 行为
常量填充 padding_mode = true NdDmaConfig{isNearestValueMode = false} pad 区域填入 pad_value / constantValue
邻边复制 padding_mode = false NdDmaConfig{isNearestValueMode = true} pad 区域复制最近的源元素值(edge replicate);pad_value 被忽略

极性陷阱:c_api 的 padding_mode=true 对应 basic_api 的 isNearestValueMode=false。两者取反关系。混用两层 API 时最易出错。

填充值类型要求

  • 类型必须与 dst/src 一致
  • fp8 类型(e4m3/e5m2/e8m0)无专用重载,需先 reinterpret_castint8_t 再调 asc_set_ndim_pad_value
  • basic_api 的 b64 类型(int64/uint64/double)仅允许 constantValue = 0

6. L2 Cache 模式

asc_load_l2_cache_mode 枚举(include/c_api/utils/enum.h):

枚举值 数值 语义 推荐场景
NORMAL_FIRST_VICTIM 0 缓存,标记为高替换优先级 通用默认
NORMAL_LAST_VICTIM 1 缓存,标记为低替换优先级 数据将复用
NORMAL_PERSISTENT 2 缓存,持久(仅被其他持久行替换) 长期复用
NOTALLOC_KEEP 4 不缓存,已有行不动 流式数据,避免污染 L2
NOTALLOC_CLEAN 5 不缓存,同地址行标记 Clean 流式 + 主动清理
NOTALLOC_DROP 6 不缓存,同地址行立即丢弃 一次性读取

basic_api 中通过 GlobalTensor::SetL2CacheHint(mode) 预设,DataCopy 内部自动提取并转发。


7. basic_api 参数填写工作流

┌─ 1. 确定维度 N (1~5),作为模板参数 dim
├─ 2. 构建 NdDmaLoopInfo<dim>(5 个数组:srcStride, dstStride, size, lpSize, rpSize)
├─ 3. 构建 NdDmaConfig(编译期 constexpr 推荐)
├─ 4. 构建 NdDmaParams<T, dim> = {loopInfo, constantValue}
├─ 5. (可选) srcGlobal.SetL2CacheHint(mode)
├─ 6. (多核共享 GM) NdDmaDci()
└─ 7. DataCopy<T, dim, config>(dstLocal, srcGlobal, params)

结构体字段速查

struct NdDmaConfig {
    bool     isNearestValueMode = false;     // true=邻边复制, false=常量填充
    uint16_t loopLpSize = unsetPad;          // 全局左 pad 覆盖(0xffff=不覆盖,用 per-dim 值)
    uint16_t loopRpSize = unsetPad;          // 全局右 pad 覆盖
    bool     ascOptimize = false;            // 保留字段
};

template <uint8_t dim>
struct MultiCopyLoopInfo {                   // 又名 NdDmaLoopInfo<dim>
    uint64_t loopSrcStride[dim] = {0};       // 索引 0 = 最内层
    uint32_t loopDstStride[dim] = {0};
    uint32_t loopSize[dim]      = {0};
    uint8_t  loopLpSize[dim]    = {0};       // [0,255]
    uint8_t  loopRpSize[dim]    = {0};       // [0,255]
};

template <typename T, uint8_t dim>
struct MultiCopyParams {                     // 又名 NdDmaParams<T, dim>
    MultiCopyLoopInfo<dim> loopInfo;
    T constantValue;                         // 常量填充值(isNearestValueMode=false 时生效)
};

NdDmaConfig::loopLpSize/RpSize 设为非 unsetPad 值时,该单一值会覆盖所有维度的 per-dim pad 设置。常用于"全维统一 pad 0"的简洁写法。


8. 实战示例

1 basic_api:2D 广播

// xGm [1,16] -> xLocal [3,16]  (单行复制 3 次)
AscendC::NdDmaLoopInfo<2> loopInfo{
    {1, 0},     // srcStride: dim1=0 → 重读同一源行(广播)
    {1, 16},    // dstStride: dim1=16(目标行间距)
    {16, 3},    // size:      dim0=16, dim1=3(复制3次)
    {0, 0}, {0, 0}
};
AscendC::NdDmaParams<T, 2> params{loopInfo, 0};
AscendC::NdDmaDci();
static constexpr AscendC::NdDmaConfig dmaConfig;
AscendC::DataCopy<T, 2, dmaConfig>(xLocal, xGm, params);

关键srcStride = 0 触发硬件广播,省去多次读取。

2 basic_api:2D 切片

// xGm [32,64], 取 16×10 块中的 5×7 子块 -> xLocal [16,16]
AscendC::NdDmaLoopInfo<2> loopInfo{
    {1, 64},    // srcStride: dim1=64(源行间距)
    {1, 16},    // dstStride: dim1=16(目标行间距)
    {16, 16},   // size:      dim0=16, dim1=16
    {0, 0}, {0, 0}
};
AscendC::NdDmaParams<T, 2> params{loopInfo, 0};
AscendC::NdDmaDci();
static constexpr AscendC::NdDmaConfig dmaConfig;
AscendC::DataCopy<T, 2, dmaConfig>(xLocal, xGm, params);

通过 srcGlobal.SetGlobalBuffer(base + offset) 调整起始地址定位子块。

3 basic_api:3D 无 pad

// 3D float, shape [10,8,2]
AscendC::NdDmaLoopInfo<3> loopInfo{
    {1, 13, 13*15},       // srcStride: dim0=1, dim1=13, dim2=13*15
    {1, 10, 8*10},        // dstStride: dim0=1, dim1=10, dim2=80
    {10, 8, 2},           // size
    {0, 0, 0}, {0, 0, 0}  // 无 pad
};
AscendC::NdDmaParams<float, 3> params{loopInfo, 0.0f};
AscendC::NdDmaDci();
static constexpr AscendC::NdDmaConfig dmaConfig;
AscendC::DataCopy<float, 3, dmaConfig>(dstLocal, srcGm, params);

9. 常见陷阱

陷阱 后果 规避
padding_mode / isNearestValueMode 极性混淆 填充值被忽略或填充模式错误 牢记:c_api true=常量;basic_api true=邻边。取反关系
c_api setter 残留 SPR 状态 下次搬运用错 stride/pad 每次搬运前显式调全 5 个 stride setter + pad_count
dst 起始地址非 32B 对齐 硬件异常 UB 分配时保证 32B 对齐(TPipe 默认满足)
loopN_size 未用维度填 0 硬件视为 0 元素,搬运空 未用维度填 1,不是 0
src_stride=0 但不想要广播 反复重读同一地址 广播场景才用 0,否则填真实跨度
b64 类型用非零 pad 编译失败/运行异常 b64 仅支持 constantValue=0
fp8 类型直接传 asc_set_ndim_pad_value 无匹配重载 reinterpret_cast<int8_t>
多核场景未调 dci 读到旧 cache 数据 共享 GM 时搬运前调 NdDmaDci()
dim0 pad 放进 pad_count_config dim0 pad 不生效 dim0 pad 只在 asc_ndim_copy_gm2ub 调用参数中

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐