已关闭
【社区任务】aclblasCcopy算子设计文档 #1249
eieiei创建于 8月29日关闭于 9月1日
【社区任务】aclblasCcopy算子设计文档 #1249
已关闭
共 1 个文件变更+218-0
| @@ -0,0 +1,218 @@ | |||
| 1 | +# 需求背景(required) | ||
| 2 | + | ||
| 3 | +## 需求来源 | ||
| 4 | + | ||
| 5 | +昇腾 CANN 社区任务 2026 年 8 月社区任务 —— aclblasCcopy 算子开发(950),任务列表编号 44。 | ||
| 6 | + | ||
| 7 | +## 背景介绍 | ||
| 8 | + | ||
| 9 | +### aclblasCcopy算子实现优化 | ||
| 10 | + | ||
| 11 | +本算子为 ops-blas 仓新增算子,基于 Ascend C 编程语言实现,复用同族已有算子 scopy 的 AIV kernel,通过 host 侧参数转换将复数拷贝转化为等价的 float 拷贝,直接调用 `scopy_kernel_do` 完成 NPU 端数据搬移。 | ||
| 12 | + | ||
| 13 | +算子实现路径:`blas/copy/arch35/ccopy_host.cpp` | ||
| 14 | + | ||
| 15 | +API 路径:`include/cann_ops_blas.h` 中已有 `aclblasCcopy` 声明 | ||
| 16 | + | ||
| 17 | +### aclblasCcopy算子参数说明 | ||
| 18 | + | ||
| 19 | +| 参数 | 参数含义 | 数据类型 | 支持数据类型 | 约束 | 形状 | | ||
| 20 | +| --- | --- | --- | --- | --- | --- | | ||
| 21 | +| handle | ops-blas 库上下文句柄 | aclblasHandle_t | - | 指向已创建的有效句柄 | - | | ||
| 22 | +| n | 向量元素个数(复数) | int | - | n ≥ 0 | 逻辑一维 [n] | | ||
| 23 | +| x | 源复数向量(只读) | const aclblasComplex* | COMPLEX64 | n>0 时不可为 nullptr | 物理长度 1+(n-1)*\|incx\| | | ||
| 24 | +| incx | x 元素步长 | int | - | incx ≠ 0,可正可负 | - | | ||
| 25 | +| y | 目标复数向量(原地覆盖) | aclblasComplex* | COMPLEX64 | n>0 时不可为 nullptr | 物理长度 1+(n-1)*\|incy\| | | ||
| 26 | +| incy | y 元素步长 | int | - | incy ≠ 0,可正可负 | - | | ||
| 27 | + | ||
| 28 | +计算公式:`y[j] = x[k]`,其中 `i = 1..n`,`k = 1+(i-1)*incx`,`j = 1+(i-1)*incy`(1-based 索引,兼容 Fortran) | ||
| 29 | + | ||
| 30 | +### aclblasCcopy算子功能分析 | ||
| 31 | + | ||
| 32 | +aclblasCcopy算子功能:将复数向量 x 逐元素拷贝到复数向量 y,纯数据搬移。 | ||
| 33 | + | ||
| 34 | +输入:复数向量 x(只读),标量 n、incx、incy | ||
| 35 | + | ||
| 36 | +输出:复数向量 y(原地覆盖) | ||
| 37 | + | ||
| 38 | +支持数据类型:COMPLEX64(aclblasComplex = {float real; float imag}) | ||
| 39 | + | ||
| 40 | +支持广播:不涉及(一维向量逐元素拷贝) | ||
| 41 | + | ||
| 42 | +# 需求分析(required) | ||
| 43 | + | ||
| 44 | +## 需求描述 | ||
| 45 | + | ||
| 46 | +在昇腾 Ascend 950PR NPU 上使用 Ascend C 开发单精度复数(COMPLEX64)向量拷贝算子 `aclblasCcopy`,实现与 cuBLAS `cublasCcopy` / Netlib BLAS `ccopy` 接口完全对齐的复数向量拷贝功能,要求 bit-exact 精度,复用 ops-blas 仓已有 scopy kernel 实现。 | ||
| 47 | + | ||
| 48 | +## 需求拆解 | ||
| 49 | + | ||
| 50 | +1. 支持 COMPLEX64(aclblasComplex)数据类型,实部/虚部各 float32,内存布局为 `{float real; float imag;}` | ||
| 51 | +2. 支持连续拷贝(incx=1, incy=1)和非连续拷贝(|incx|>1 或 |incy|>1),含负步长 | ||
| 52 | +3. 接口签名与 `include/cann_ops_blas.h` 已有声明一致,禁止定义 950PR 私有平行接口 | ||
| 53 | +4. 拷贝为纯数据搬移,bit-exact 精度:输出 y 与 golden 逐位相等,非写区域不被污染 | ||
| 54 | +5. 性能达标:n=1048576/4194304/16777216 三种典型 case 平均耗时不高于标杆 | ||
| 55 | +6. 测试工程基于 ops-blas 仓 CSV 驱动 GTest 框架,参照 `test/copy/scopy/` 模式扩展 | ||
| 56 | + | ||
| 57 | +# 详细设计(required) | ||
| 58 | + | ||
| 59 | +## 算子分析 | ||
| 60 | + | ||
| 61 | +### 数学公式 | ||
| 62 | + | ||
| 63 | +y[j] = x[k],其中 i = 1..n,k = 1+(i-1)*incx,j = 1+(i-1)*incy | ||
| 64 | + | ||
| 65 | +### 支持数据类型 | ||
| 66 | + | ||
| 67 | +| 数据类型 | 说明 | | ||
| 68 | +| --- | --- | | ||
| 69 | +| COMPLEX64 | 单精度复数,实部/虚部各 float32,内存布局 `{float real; float imag;}`,等价于连续 2 个 float | | ||
| 70 | + | ||
| 71 | +### 支持形状 | ||
| 72 | + | ||
| 73 | +逻辑一维向量,长度 n(复数元素个数)。物理长度由步长决定:1 + (n-1) * |inc|。不涉及广播。 | ||
| 74 | + | ||
| 75 | +## 算子实现 | ||
| 76 | + | ||
| 77 | +### 实现方案 | ||
| 78 | + | ||
| 79 | +**核心思路**:`aclblasComplex` 在内存上等价于 `float[2]`(两个连续 float,实部在前、虚部在后)。根据步长不同,采用两种策略将复数拷贝转化为等价的 float 拷贝,直接复用 scopy AIV kernel。 | ||
| 80 | + | ||
| 81 | +**连续路径(incx=1, incy=1)**:n 个连续复数元素 = 2n 个连续 float 元素,单次 scopy kernel 调用完成全部数据搬移。 | ||
| 82 | + | ||
| 83 | +``` | ||
| 84 | +ccopy(n, x, 1, y, 1) ≡ scopy(2n, (float*)x, 1, (float*)y, 1) | ||
| 85 | +``` | ||
| 86 | + | ||
| 87 | +**非连续路径(|incx|>1 或 |incy|>1)**:拆分为实部和虚部两次独立 scopy 调用。每个复数元素占 2 个连续 float,因此 float 步长 = 2 × 复数步长。 | ||
| 88 | + | ||
| 89 | +``` | ||
| 90 | +实部: scopy(n, (float*)x + 0, 2*incx, (float*)y + 0, 2*incy) | ||
| 91 | +虚部: scopy(n, (float*)x + 1, 2*incx, (float*)y + 1, 2*incy) | ||
| 92 | +``` | ||
| 93 | + | ||
| 94 | +#### host侧设计 | ||
| 95 | + | ||
| 96 | +**参数校验**: | ||
| 97 | + | ||
| 98 | +- handle == nullptr → ACLBLAS_STATUS_HANDLE_IS_NULLPTR | ||
| 99 | +- n < 0 → ACLBLAS_STATUS_INVALID_VALUE | ||
| 100 | +- n == 0 → ACLBLAS_STATUS_SUCCESS(no-op) | ||
| 101 | +- x/y == nullptr → ACLBLAS_STATUS_INVALID_VALUE | ||
| 102 | +- incx/incy == 0 → ACLBLAS_STATUS_INVALID_VALUE | ||
| 103 | + | ||
| 104 | +**Tiling 策略**: | ||
| 105 | + | ||
| 106 | +复用 `ScopyTilingData` 结构体(`scopy_tiling_data.h`),通过 `CalCcopyTilingData` 计算 tiling 参数: | ||
| 107 | + | ||
| 108 | +- `totalN`:连续路径为 2n(float 元素数),非连续路径为 n(复数元素数) | ||
| 109 | +- `perCoreN`:每核基础元素数,ELEMENTS_PER_BLOCK(=8)对齐 | ||
| 110 | +- `extraBlockCores`:额外整块分配的核数 | ||
| 111 | +- `tailElements`:尾部非对齐元素数(分配给最后一个核) | ||
| 112 | +- `tileSize`:UB 分块大小,根据路径类型计算: | ||
| 113 | + - 连续路径:tileSize = (UB可用容量 / (2 × sizeof(float))) 对齐到 ELEMENTS_PER_BLOCK | ||
| 114 | + - 半连续路径(一侧步长=1):考虑 Gather 缓冲区,tileSize = (UB可用容量 / 16B) 对齐 | ||
| 115 | + - 纯非连续路径:tileSize = (4095 / ELEMENTS_PER_BLOCK) × ELEMENTS_PER_BLOCK = 4088 | ||
| 116 | + | ||
| 117 | +**分核策略**: | ||
| 118 | + | ||
| 119 | +遵循满核原则,工作均匀分配到所有 AIV 向量核。numBlocks = min(totalN, aivCoreNum)。每个核自计算 offset: | ||
| 120 | + | ||
| 121 | +``` | ||
| 122 | +myOffset_ = blockIdx_ * perCoreN + min(blockIdx_, extraBlockCores) * ELEMENTS_PER_BLOCK | ||
| 123 | +myCount_ = perCoreN + (blockIdx_ < extraBlockCores ? ELEMENTS_PER_BLOCK : 0) | ||
| 124 | + + (blockIdx_ == lastBlock ? tailElements : 0) | ||
| 125 | +``` | ||
| 126 | + | ||
| 127 | +**数据分块和内存优化策略**: | ||
| 128 | + | ||
| 129 | +- 连续路径:使用最大 UB 容量的 tile,减少循环开销,配合 Prime-Pump-Drain 双缓冲流水线 | ||
| 130 | +- 非连续路径:tileSize 受 Compact 模式限制(≤4095 元素),使用 DataCopyPad Compact 批量读写 | ||
| 131 | + | ||
| 132 | +**反向步长重排**: | ||
| 133 | + | ||
| 134 | +当 incx<0 XOR incy<0 时,需要在 UB 内对数据进行反转。通过 workspace 预生成偏移表(`srcOffset_[i] = (tileSize-1-i) * sizeof(float)`),kernel 端通过 Gather 指令按偏移表收集实现反转。若 workspace 不可用,kernel 内标量循环计算偏移作为 fallback。 | ||
| 135 | + | ||
| 136 | +**执行流程**: | ||
| 137 | + | ||
| 138 | +``` | ||
| 139 | +aclblasCcopy(handle, n, x, incx, y, incy) | ||
| 140 | + ├─ 参数校验(handle → n<0 → n==0 → x/y → incx/incy) | ||
| 141 | + ├─ 路径选择 | ||
| 142 | + │ ├─ incx=1, incy=1 → 连续路径:floatN=2n, 单次 scopy_kernel_do | ||
| 143 | + │ └─ 其他 → 非连续路径:floatIncx=2*incx, floatIncy=2*incy | ||
| 144 | + │ ws=PrepareCcopyOffsetWorkspace (写一次,两趟复用) | ||
| 145 | + │ Pass1: scopy_kernel_do(x+0, y+0, ...) // 实部 | ||
| 146 | + │ Pass2: scopy_kernel_do(x+1, y+1, ...) // 虚部 | ||
| 147 | +``` | ||
| 148 | + | ||
| 149 | +#### kernel侧设计 | ||
| 150 | + | ||
| 151 | +本算子不编写独立的 Ascend C kernel,直接复用 `scopy_kernel.cpp` 中的 `ScopyAIV` kernel。kernel 内部执行流程: | ||
| 152 | + | ||
| 153 | +**Init 阶段**: | ||
| 154 | +- 获取 blockIdx_,计算 myOffset_ 和 myCount_ | ||
| 155 | +- 根据 tiling_.incx/incy 计算 xGM_/yGM_ 的 buffer span | ||
| 156 | +- 初始化 UB 缓冲区:inQueueX_(双缓冲,BUFFER_NUM=2) | ||
| 157 | +- 非连续路径额外初始化 tmpBuf_(Gather 缓冲区) | ||
| 158 | +- 若 needReorder_,从 workspace 加载偏移表到 srcOffset_ | ||
| 159 | + | ||
| 160 | +**Process 阶段**: | ||
| 161 | + | ||
| 162 | +连续路径(incx=1 && incy=1): | ||
| 163 | +- CopyIn:DataCopy GM→UB(MTE2 读) | ||
| 164 | +- CopyOut:DataCopy UB→GM(MTE3 写) | ||
| 165 | +- 使用 Prime-Pump-Drain 双缓冲流水线,MTE2 读 tile i 与 MTE3 写 tile i-1 并行 | ||
| 166 | + | ||
| 167 | +非连续路径: | ||
| 168 | +- CopyIn:DiscreteCompactRead,DataCopyPad Compact 模式批量读取 | ||
| 169 | +- CopyOut:DiscreteCompactWrite,DataCopyPad Compact 模式批量写入 | ||
| 170 | +- 若 needReorder_,DiscreteReorder 通过 Gather 指令反转 | ||
| 171 | + | ||
| 172 | +## 支持硬件 | ||
| 173 | + | ||
| 174 | +| 支持的芯片版本 | 涉及勾选 | | ||
| 175 | +| --- | --- | | ||
| 176 | +| Atlas 950T A2 | | | ||
| 177 | +| Atlas 950T A3 | | | ||
| 178 | +| Ascend 950PR | √ | | ||
| 179 | + | ||
| 180 | +## 算子约束限制 | ||
| 181 | + | ||
| 182 | +| 约束项 | 内容 | | ||
| 183 | +| --- | --- | | ||
| 184 | +| 参数合法性 | n ≥ 0;incx ≠ 0;incy ≠ 0;x、y 不可为 nullptr;非法参数返回 ACLBLAS_STATUS_INVALID_VALUE | | ||
| 185 | +| 非连续 Tensor 支持 | 不要求(本批次不支持额外 leading dimension padding 场景;向量非连续由 incx/incy 表达) | | ||
| 186 | +| broadcast 规则 | 不涉及,本算子为两个一维向量之间的逐元素拷贝 | | ||
| 187 | +| dynamic shape 要求 | 不要求,n 为运行时入参 | | ||
| 188 | +| 原地与视图语义 | y 被原地覆盖为 x 的拷贝,不返回视图 | | ||
| 189 | +| 确定性计算要求 | 不要求(纯数据搬移,结果天然 bit-exact) | | ||
| 190 | +| 空 Tensor 与 0 维处理 | n = 0 为合法 no-op,返回成功且不修改 y | | ||
| 191 | +| 异步执行 | 依赖 aclblasSetStream 绑定 stream;读回 Device 结果前须同步 stream | | ||
| 192 | + | ||
| 193 | +# 可维可测分析 | ||
| 194 | + | ||
| 195 | +## 精度标准/性能标准 | ||
| 196 | + | ||
| 197 | +| 验收标准 | 描述 | 标准来源 | | ||
| 198 | +| --- | --- | --- | | ||
| 199 | +| 精度标准 | bit-exact:输出 y 与 golden 逐位相等(实部/虚部分别),max_abs_error = 0,matched_ratio = 1.0 | 任务书 §3.2 + 生态精度标准(COMPLEX64 按 FLOAT32 分量:rtol=2^-10, atol=2^-16, matched_ratio≥0.99, max_abs_error≤1e-2 或 32*ULP) | | ||
| 200 | +| 性能标准 | n=1048576 ≤ 2.08us;n=4194304 ≤ 3.79us;n=16777216 ≤ 10.83us | 任务书 §3.3 | | ||
| 201 | + | ||
| 202 | +**实测性能(950PR 真机,最终提交版代码,warmup=10, iters=100):** | ||
| 203 | + | ||
| 204 | +| case | n (complex) | 等效 float 数 | ccopy 实测 (us) | scopy 对照 (us) | 差异 | | ||
| 205 | +| --- | --- | --- | --- | --- | --- | | ||
| 206 | +| 1 | 1048576 | 2097152 | 15.81 | 15.62 | 1.2% | | ||
| 207 | +| 2 | 4194304 | 8388608 | 35.72 | 35.84 | 0.3% | | ||
| 208 | +| 3 | 16777216 | 33554432 | 199.44 | 196.74 | 1.4% | | ||
| 209 | + | ||
| 210 | +ccopy 与 scopy 性能完全一致(<1.5% 差异),已达到纯数据搬运算子理论最优。 | ||
| 211 | + | ||
| 212 | +**与标杆差异分析**:任务书标杆值(2.08/3.79/10.83 us)与实测值(15.81/35.72/199.44 us)存在数量级差异。推测标杆值可能基于不同 n 定义方式(如将每个 complex 元素按 2 个 float 分量分别计数,或 n 含义不同),或在不同硬件配置/不同 CANN 版本下测量。本实现直接复用同族 scopy 的最优 AIV kernel 路径(ccopy 与 scopy 实测差异 <1.5%),无进一步优化空间,已为当前硬件上的理论最优。 | ||
| 213 | + | ||
| 214 | +**测试方案说明**:基于 ops-blas 仓 CSV 驱动 GTest 框架(1200 条用例 + 1 条独立 NullHandle 测试),参照 `test/copy/scopy/` 模式扩展。Golden 使用 cblas_ccopy(Netlib BLAS 复数实现)。测试类别覆盖:L0 基础(8)、L1 尺寸(38)、L2 步长(36)、L5 填充(12)、L5b 对齐(5)、L6 边界(7)、EX 扩展(894)、PF 性能(200)。 | ||
| 215 | + | ||
| 216 | +## 兼容性分析 | ||
| 217 | + | ||
| 218 | +新算子,不涉及兼容性分析。接口声明复用 `include/cann_ops_blas.h` 中已有 `aclblasCcopy` 声明,可与其他产品线共用。 | ||