| Files | Last commit | Last update |
|---|---|---|
pre-commit修复posembedding、mhc、ffn等目录.h/.hpp历史格式问题 Co-authored-by: gitcode_lijd<lijiandong20@huawei.com> # message auto-generated for no-merge-commit merge: !11021 merge fix_3 into master pre-commit修复posembedding、mhc、ffn等目录.h/.hpp历史格式问题 Created-by: gitcode_lijd Commit-by: gitcode_lijd Merged-by: cann-robot Description: ## 关联的Issue 关联Issue https://gitcode.com/cann/ops-transformer/issues/4850 ## 描述 对仓内算子源码提前做 pre-commit 历史格式问题清理,为后续功能改动(如 Regbase API 替换等)扫清钩子障碍,避免后续 PR 因格式问题被大面积格式化污染 diff。 处理方式: find <目录> -type f \( -name '*.h' -o -name '*.hpp' \) | xargs pre-commit run --files,多轮执行直到所有 hook 收敛(无 Failed / 无自动修复)。 修复内容(仅格式,不改任何代码语义): - 尾随空白(trailing whitespace) - 文件末尾换行(end of file) - clang-format 代码重排(换行、缩进、对齐,遵循仓内 .clang-format) ### 排除范围(不做格式化) - *_def.cpp 算子定义文件(排版由开发者自行维护,另见 PR #11041) - */op_api/aclnn*.h、*/op_graph/*_proto.h、*/op_host/op_api/aclnn*.h 接口/原型头文件(排版由开发者自行维护) - mamba/ 目录(该目录 causal_conv1d 算子 UT 上游基线存在失败,见 Issue #4840,本批不涉及) - examples/、experimental/ 目录 ### PR 拆分说明 全仓 .h/.hpp 清理共 2362 个文件,按场景目录拆分为 6 个 PR(分支不同、内容互不重叠,可独立 review 合入): | PR | 分支 | 范围 | |---|---|---| | #4878 | compile | attention 第1批(按算子字母序前段) | | #10422 | graph | attention 第2批(按算子字母序中段) | | #9331 | test | attention 第3批(按算子字母序后段) | | #10991 | fix | mc2 | | #11020 | fix_2 | moe、gmm | | #11021 | fix_3 | posembedding、mhc、ffn、common、tests、torch_extension | ## 测试 - 纯格式化改动:不改变任何代码语义,逐文件 diff 均为空白/换行/clang-format 重排 - pre-commit 全量 hook 收敛验证:多轮执行后 trailing-whitespace / end-of-file-fixer / clang-format 均 Passed - 建议合入前对相关算子做一次编译验证 See merge request: cann/ops-transformer!11021 | 2 months ago | |
feat(posembedding): 迁移 RotaryPositionEmbedding 融合 pass 至新 Pass 框架 Co-authored-by: alfengyuan<yyfgdut@gmail.com> # message auto-generated for no-merge-commit merge: !12271 merge feature/posembedding-rpe-fusion-pass-5397 into master feat(posembedding): 迁移 RotaryPositionEmbedding 融合 pass 至新 Pass 框架 Created-by: alfengyuan Commit-by: alfengyuan Merged-by: cann-robot Description: ## 描述 将 canndev 内建 rope_fusion_pass 中的 RotaryMul2RotaryPositionEmbeddingFusionPass(RotaryMul → RotaryPositionEmbedding,mode=0)迁移到算子仓,基于 ge::fusion::PatternFusionPass + ES 接口重写。 要点: - 类名与 canndev 逐字相同,REG_FUSION_PASS 注册 kCompatibleInherited 阶段,运行时同名顶替内建 pass,canndev 侧保留不动; - RotaryMul 无 ES API,pattern 侧用 CompliantNodeBuilder 构图;替换侧使用 es::RotaryPositionEmbedding(x, cos, sin, nullptr, 0); - 平台门控复刻旧 IsRegBase 语义:short_soc_version ∈ {Ascend950, Ascend350, MC62CM12A}(NpuArch 3510/5102); - 版本守卫 CANN_VERSION_NUM >= 90000000(kCompatibleInherited 自 9.0 引入),头文件 guard 采用 TRANSFORMER_ 前缀避免与 canndev 宏撞名; - tests/ut/framework_normal/op_graph/CMakeLists.txt 补链 ge_compiler、opp_registry(PatternFusionPass 类 UT 必需;该基建改动在 #5398/#5399 的 PR 中同样携带,内容一致,合入顺序无关)。 ## 关联的Issue 关联Issue #5397 ## 测试 - 新增 op_graph UT 5 例(pattern 创建、Ascend910B 不融合、Ascend950/Ascend350/MC62CM12A 融合成功、mode attr 与输入边校验),全部通过; - 真机 Ascend950 图模式验证:同名顶替生效(我方 pass 执行、内建零命中),数值与 golden 一致; - 回归:ttk e2e npu_rotary_mul 93/93 全绿。 ## 文档更新 无 ## 类型标签 - [x] ✨ 新特性 See merge request: cann/ops-transformer!12271 | 1 month ago | |
ci(compile): 新增 build.sh --PR_COMPILE 编译范围推导机制(白名单优先 + depends反向闭包 + def SoC过滤) Co-authored-by: gitcode_lijd<lijiandong20@huawei.com> # message auto-generated for no-merge-commit merge: !13100 merge test into master ci(compile): 新增 build.sh --PR_COMPILE 编译范围推导机制(白名单优先 + depends反向闭包 + def SoC过滤) Created-by: gitcode_lijd Commit-by: gitcode_lijd Merged-by: cann-robot Description: ## 描述 新增 PR 编译范围推导机制,替代原先由 tests/test_config.yaml(example 看护规则)间接决定编译范围的方式。**本 PR 为纯新增:不改任何现有逻辑/任务,UT 维持原状,master 上无 --PR_COMPILE 调用方,对现存 CI 零影响。** ### 1. 推导流程(cmake/scripts/parse_compile_scope.py) 变更文件 → 映射为 <domain>/<op> 键(仅认 attention/ffn/gmm/mamba/mc2/mhc/moe/posembedding 八个大目录;docs/、.md/.json/.ini 跳过)→ 按以下顺序决定编译清单: 1. **白名单优先**:直接变更的算子命中 tests/compile_scope_whitelist.yaml 时,按其 impact(完整编译清单)+ soc(必填,加载即校验,缺失直接报错)选定。当前收录 18 条目:attention/*_metadata(impact=自身+对应无后缀算子,soc 按无后缀 def 推导)、AICPU 算子 attention_worker_scheduler/ffn_worker_scheduler(仅 ascend910b)、mhc/mhc_sinkhorn_common(impact 为 mhc_sinkhorn 正反向,ascend950)。 2. **未命中走正规流程**:op_host/CMakeLists.txt 的 *_depends(兼容 PARENT_SCOPE 与 CACHE INTERNAL 标记)反向闭包,拉出所有声明依赖变更算子的下游;再按 *def.cpp 的 AICore AddConfig 做目标 SoC 过滤。 3. **域内 common/3rd**:变更映射为共享键(如 mc2/common),经 depends 反向闭包触发所有依赖它的算子(如改 mc2/common @910b → 17 个 mc2 算子)。 ### 2. SoC 依据 唯一依据为 def.cpp AddConfig(支持 ascend 短名/长名互认);op_host/config/<soc> 目录已废弃不作依据。def 中宏/变量等非常规写法的识别待 def 规范化后加强。 ### 3. build.sh 集成 新增 --PR_COMPILE <pr_filelist> 选项(注意 --soc 需在其前传):推导为空时打印提示并 exit 200(CI 侧约定 200=跳过成功);非空时设为 ascend_op_name 并进入 custom pkg 编译(出 kernel bin)。 ### 4. 白名单自检 python3 cmake/scripts/parse_compile_scope.py --check:校验白名单条目(算子存在、impact 目标存在、soc 合法、impact 为列表、soc 必填),有错非零退出逐条打印。已挂 pre-commit compile-scope-whitelist-check(仅白名单文件变更时触发)。 ### 5. 已确认的边界(不在本 PR 处理) - ascend310p/ascend350 暂不编译(后续单独出方案);多 SoC 单构建不支持(CI 走单 SoC 命令); - 框架级变更(根目录脚本/cmake 公共件)不触发编译范围,由改动方自行验证; - UT 逻辑完全不动(test_soc_config.yaml/get_soc_version.py 保留原样)。 ## 关联的Issue 关联Issue #5732;配套 CI 任务拆分(arch22/arch35)见 #13016(依赖本 PR,需后合入)。 ## 测试 - 语法:bash -n / py_compile / yaml / ruff 全过,pre-commit 全绿; - 推导用例:metadata 白名单(@950→flash_attn+metadata)、AICPU 白名单(@910b 命中、@950 空)、mhc_sinkhorn_common(@950→正反向)、mc2/common 闭包(@910b→17 算子)、dispatch_v2 闭包(@arch22→7 算子)、框架变更空范围; - 端到端:build.sh --soc=ascend950 --PR_COMPILE 实编成功(kernel bin 产出),空范围 exit 200。 ## 后续(另起 PR) depends drift 持续看护、def.cpp AddConfig 写法规范化、#13016 的 CI 任务拆分接入。 See merge request: cann/ops-transformer!13100 | 1 month ago | |
添加pybind11依赖声明 Co-authored-by: chenyifan<chenyifan66@h-partners.com> # message auto-generated for no-merge-commit merge: !9952 merge add_module into master 添加pybind11依赖声明 Created-by: mutex_lock Commit-by: chenyifan Merged-by: cann-robot Description: ## 描述 - requirements.txt中增加pybind11依赖声明 ## 关联的Issue https://gitcode.com/cann/ops-transformer/issues/4283 ## 测试 <!--描述进行了哪些测试来验证你的改动。包括但不限于二级冒烟、算子泛化等。--> ## 文档更新 <!--如果这个PR包含文档的更新,请在这里指出。例如:更新了README.md文件。--> ## 类型标签 <!-- [x] 表示选中 --> - [ ] 🐛 Bug修复 - [ ] ✨ 新特性 - [ ] ⚡ 性能优化 - [ ] ♻️ 重构 - [ ] 🧪 测试 - [ ] 📦 构建/CI - [ ] 🔧 配置变更 - [ ] 📝 文档更新 - [x] ⬆️ 依赖升级 - [ ] 🔒 安全修复 - [ ] 🧹 代码清理 - [ ] ❓ 其他,请描述: See merge request: cann/ops-transformer!9952 | 2 months ago | |
AF分离算子性能优化(batch接口多channel) Co-authored-by: jiangxiuhan1<jiangxiuhan@huawei.com> # message auto-generated for no-merge-commit merge: !12452 merge channel_ffn into master AF分离算子性能优化(batch接口多channel) Created-by: jiangxiuhan1 Commit-by: jiangxiuhan1 Merged-by: cann-robot Description: ## 描述 <!--在这里详细描述你的改动,包括改动的原因和所采取的方法。--> ## 关联的Issue <!-- 如果这个PR是为了解决特定的Issue,请在这里提供Issue链接。例如:关联Issue #000--> <!-- 如果这个PR是为了解决特定的问题单,请在这里描述问题单单号。--> ## 测试 <!--描述进行了哪些测试来验证你的改动。包括但不限于二级冒烟、算子泛化等。--> ## 文档更新 <!--如果这个PR包含文档的更新,请在这里指出。例如:更新了README.md文件。--> ## 类型标签 <!-- [x] 表示选中 --> - [ ] 🐛 Bug修复 - [ ] ✨ 新特性 - [ ] ⚡ 性能优化 - [ ] ♻️ 重构 - [ ] 🧪 测试 - [ ] 📦 构建/CI - [ ] 🔧 配置变更 - [ ] 📝 文档更新 - [ ] ⬆️ 依赖升级 - [ ] 🔒 安全修复 - [ ] 🧹 代码清理 - [ ] ❓ 其他,请描述: # PR #12452 代码检视报告 ## 检视概览 - **PR**:https://gitcode.com/cann/ops-transformer/pull/12452(AF 分离算子性能优化——URMA staging 区、多 channel、多层 Layer 支持、batch 接口) - **仓库**:ops-transformer(PR head: 950b5390e) - **变更规模**:27 个文件(26 修改 + 1 删除),diff 3680 行 - **检视范围**:mc2/attention_to_ffn(_v2)、mc2/ffn_to_attention(_v2)、mc2/common、tests/(Kernel + Tiling + op_api + torch_extension + UT + 文档 + 配置,全侧别) - **检视方法**:假设检验驱动(H0/H1、证据收集、自信值 ≥70% 判违规);规则库 cpp-secure / ascendc-topk / ascendc-api / ascendc-perf / mc2-specific / cpp-general / cpp-style / python-secure / regbase-review-checks - **检视流程**:文件分组(5 组)→ 全局预扫描(条例级匹配)→ 负载感知波次逐条检视(6 波 × 51 组,约 161 条规则实例)→ 综合研判(跨组模式识别 / 冲突解决 / 置信度过滤)→ 行号校验(红线 diff 范围校验 22/22 通过) - **检视时间**:2026-09-23 ## 检视统计 | 状态 | 条例数 | 占比 | |------|------|------| | PASS | 112 | 69.6% | | FAIL(发现问题) | 23(去重后 17 个独立问题) | 14.3% | | SUSPICIOUS(待确认附录) | 3 | 1.9% | | N/A(侧别/模式不匹配) | 22 | 13.7% | **综合研判**:系统性风险 2 项;冲突解决 2 项(MC2-04 vs MC2-18 范围互补、SEC-5.1 维持 PASS);去重 4 类根因(F2A 校验删除 7 条例、layerId 2 处、TIL-2 跨组 2 处、microBatchNum 2 条例)。 ### 全局摘要与系统性风险 本 PR 将两个 URMA 通信算子从"逐轮 SyncAll + core0 集中 WriteNbi"重构为"staging → counting-sort 建表 → 多 channel 并行 Batch WQE relay"三阶段,并引入多层 Layer(L≥1)与用户自管通信窗口。重构整体方向合理(同步完备性、DataCopy 对齐、批量搬运、资源生命周期等大量条例 PASS),但存在两类系统性回归: - **系统性风险 1(校验删除回归)**:kernel 侧与 host 侧共 4 处"删除既有防御且无替代"——F2A kernel 全部运行时元数据/窗口边界断言(7 条例独立确认)、ffn_to_attention 的 microBatchNum==1 校验、A2F 的 aivNum==0 校验、A2F 的 UB 容量校验缺失。**建议对本 PR 全部"删除 ascendc_assert / OP_TILING_CHECK"的 diff 段做一次专项全局排查**。 - **系统性风险 2(多层支持遗漏)**:allowMultiLayer 启用后,layerId 运行时校验、scales 层偏移、expertRankTable 层偏移三处仅 1 处(expertRankTable)正确实现,另 2 处遗漏。 --- ## 发现问题(HIGH 置信度) ### H1.【系统性·安全】F2A kernel 重构删除全部运行时元数据与窗口边界校验,负值/越界输入可致 GM 越界读 + 本地/远端窗口越界写 **确认条例**:SEC-2.2(105%)、SEC-1.1(120%)、SEC-1.3(95%)、SEC-3.3(85%)、SEC-3.5(105%)、MC2-05(100%)、GEN-1.1(90%)——7 个条例组独立确认同一根因 **问题描述**:本 PR 将 ReadTokenMetaData 重构为 PrefetchTokenMetaData + ReadTokenMetaDataFromLocal 时,删除了旧代码全部值域/边界断言且无任何替代: 1. actualTokenNum(int64 设备张量值)非负与 ≤maxTokenNum 两条断言被删——负值经 static_cast<uint64_t> 回绕为 ~1.8e19,scanStart/scanEnd 巨大 → PrefetchTokenMetaData 对 sessionIds/microBatchIds/tokenIds/expertOffsets 四个 GM 张量越界读;per-rank 计数越过按 maxTokenNum 分配的地址表容量。 2. curAttenWorkIds / curMicroBatchIds / curTokenBatchOffset / curTokenTopkOffset 四字段范围断言(<axisA_ / <microBatchNum_ / <axisBS_ / <expertNumPerToken_)全部被删——任一负值 int32→uint32 回绕 ~4.29e9,tokenSlot 经乘法放大为天量偏移。 3. 窗口写前断言 remoteDataOffset ≤ requiredWindowSize_、remoteFlagOffset ≤ winTokenInfoTableSize_ 与 curRankWinAddr != nullptr 判空被删——SendToLocal(本地 DataCopyPad)与 AppendRemoteDataWrite/AppendRemoteFlagWrite(URMA WriteNbi 远端 DMA 写)目标地址无界,**构成跨卡远端内存踩踏**。 4. axisH_ ≤ axisHS_ 断言被删且 FT2A tiling 无等价关系校验(对照:AT2FFN 侧本 PR 新增了 CheckUrmaWinSlotCapacity)——H>HS 时每 token 写入溢出 HS 槽位。 5. 上游无补偿:aclnn 层仅判空指针,torch 层仅校验 dim/dtype,tiling 只能校验 shape(无法校验设备张量**值**)。 **代码片段**(mc2/ffn_to_attention_v2/op_kernel/ffn_to_attention_urma.h 行 202-237): cpp inputDataAddr_ = x; xGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ xType *>(x)); sessionIdsGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(sessionIds)); microBatchIdsGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(microBatchIds)); tokenIdsGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(tokenIds)); expertOffsetsGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(expertOffsets)); actualTokenNumGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int64_t *>(actualTokenNum)); if constexpr (isInputRankTable) { attnRankTableGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(attnRankTable)); } actualTokenNum_ = static_cast<uint64_t>(actualTokenNumGMTensor_.GetValue(0UL)); // 旧: >=0 与 <=maxTokenNum_ 断言已删 hSize_ = static_cast<uint64_t>(axisH_) * sizeof(xType); // 旧: axisH_<=axisHS_ 断言已删 batchSizeSendCnt_ = static_cast<uint64_t>(axisBS_) * expertNumPerToken_; const uint64_t tokenSlotNum = static_cast<uint64_t>(microBatchNum_) * batchSizeSendCnt_; winTokenInfoTableSize_ = Ceil(tokenSlotNum * sizeof(int32_t), URMA_WIN_ADDR_ALIGN) * URMA_WIN_ADDR_ALIGN; const uint64_t tokenWindowEnd = winTokenInfoTableSize_ + tokenSlotNum * axisHS_ * sizeof(xType); const uint64_t flagWindowOffset = Ceil(tokenWindowEnd, static_cast<uint64_t>(URMA_FLAG_SLOT_SIZE)) * URMA_FLAG_SLOT_SIZE; GM_ADDR localScratchAddr = GetWindowAddr(rankId_) + flagWindowOffset; localFlagAddr_ = localScratchAddr + static_cast<uint64_t>(aivId_) * URMA_FLAG_SLOT_SIZE; **代码片段**(同文件 行 314-337,六处断言全部删除): cpp __aicore__ inline void FFNToAttentionUrma<TemplateFFNToAttentionUrmaTypeFunc>::ReadTokenMetaDataFromLocal( ReadTokenMetaDataStruct &metaDataStruct, uint32_t localOffset) { int32_t curAttenWorkId = metadataTensor_.GetValue(localOffset); // 旧: assert(>=0) 已删 metaDataStruct.curAttenWorkIds = static_cast<uint32_t>(curAttenWorkId); metaDataStruct.curAttenWorkRank = metaDataStruct.curAttenWorkIds; if constexpr (isInputRankTable) { int32_t curAttenWorkRank = attnRankTableGMTensor_.GetValue(metaDataStruct.curAttenWorkIds); // OOB 读 metaDataStruct.curAttenWorkRank = static_cast<uint32_t>(curAttenWorkRank); } int32_t curMicroBatchId = metadataTensor_.GetValue(METADATA_CHUNK_TOKEN_NUM + localOffset); metaDataStruct.curMicroBatchIds = static_cast<uint32_t>(curMicroBatchId); // 旧: assert(<microBatchNum_) 已删 int32_t curTokenBatchOffset = metadataTensor_.GetValue(METADATA_TOKEN_BATCH_OFFSET_FIELD_INDEX * METADATA_CHUNK_TOKEN_NUM + localOffset); metaDataStruct.curTokenBatchOffset = static_cast<uint32_t>(curTokenBatchOffset); // 旧: assert(<axisBS_) 已删 int32_t curTokenTopkOffset = metadataTensor_.GetValue(METADATA_TOKEN_TOPK_OFFSET_FIELD_INDEX * METADATA_CHUNK_TOKEN_NUM + localOffset); metaDataStruct.curTokenTopkOffset = static_cast<uint32_t>(curTokenTopkOffset); // 旧: assert(<expertNumPerToken_) 已删 } **代码片段**(同文件 行 499-516 + 364-377,未校验值直接推导远端写地址): cpp for (uint32_t i = 0U; i < groupTokens; ++i) { uint64_t sourceTokenIndex = groupStart + i; ReadTokenMetaDataStruct metaDataStruct; ReadTokenMetaDataFromLocal(metaDataStruct, i); // Rank range was already validated in Pass 1 over the same [scanStart, scanEnd) window. uint64_t tokenSlot = (static_cast<uint64_t>(metaDataStruct.curMicroBatchIds) * axisBS_ + metaDataStruct.curTokenBatchOffset) * expertNumPerToken_ + metaDataStruct.curTokenTopkOffset; // 三分量均未校验 uint64_t remoteDataOffset = winTokenInfoTableSize_ + tokenSlot * static_cast<uint64_t>(axisHS_) * sizeof(xType); uint64_t remoteFlagOffset = tokenSlot * sizeof(int32_t); uint32_t dstRank = metaDataStruct.curAttenWorkRank; if (dstRank == rankId_) { SendToLocal(localWindowAddr + remoteDataOffset, localWindowAddr + remoteFlagOffset, sourceTokenIndex, xCopyParams); // 本地窗口越界写 continue; } ... // AppendRemoteDataWrite(行 364-377): uint64_t remoteDataOffset = winTokenInfoTableSize_ + addressEntry.tokenSlot * static_cast<uint64_t>(axisHS_) * sizeof(xType); GM_ADDR sourceDataAddr = inputDataAddr_ + addressEntry.sourceTokenIndex * hSize_; (void)hcomm_.WriteNbi<REMOTE_WQE_CONFIG>(batchHandle, remoteWindowAddr + remoteDataOffset, sourceDataAddr, static_cast<uint32_t>(hSize_)); // URMA 远端 DMA 写,目标无界 **假设检验证据**:正向——规范违反 +40%(外部数据作索引/偏移未校验、有符号→无符号裸转换、窗口写入无边界);上下文防御缺失 +30%(唯一剩余断言 curAttenWorkRank < worldSize_(:429)只覆盖 rank 字段,注释自证仅校验 rank);PR 归属 +20%(断言删除全部在 diff 删除行内);数据流/调用链风险 +15%~+15%(输入为设备张量运行时值,偏移直接进入 WriteNbi/DataCopyPad)。负向——上游校验均经验证不覆盖张量**值**(0 分)。自信值 85%~120%(各条例)≥ 70% → 判定违规。 **修复建议**: 1. Init 恢复:int64_t v = actualTokenNumGMTensor_.GetValue(0UL); ascendc_assert(v >= 0 && (uint64_t)v <= maxTokenNum_, ...);恢复 axisH_ <= axisHS_ 断言。 2. ReadTokenMetaDataFromLocal(或 Pass 1 消费点)恢复四字段范围校验;对直接决定写地址的 tokenSlot 增加硬校验 ascendc_assert(tokenSlot < tokenSlotNum)(ascendc_assert 可能被编译掉,建议至少此项使用不可编译剔除的检查)。 3. SendToLocal/AppendRemoteDataWrite/AppendRemoteFlagWrite 写前恢复以 cclBufferSize/winTokenInfoTableSize_ 为界的偏移断言。 4. FT2A tiling 增加 Host 侧 H > HS 拒绝(对齐 AT2FFN 的 CheckUrmaWinSlotCapacity)。 --- ### H2.【安全】A2F 多层支持:运行时 layerId 无 < L 校验 + layerId_ * expertRankTableCnt uint32 乘法回绕 **确认条例**:SEC-1.1(105%)、SEC-2.2 违规点3(90%) **问题描述**:本 PR 将 expert_rank_table Dim0 校验由 ==1 放宽为 ≥1 并启用 allowMultiLayer=true,文档声明"运行时 LayerId 取值需小于 L",但**该约束无任何代码 enforcement**:kernel 侧 layerId_ 取自 GM 张量后未与 axisL_(tilingData 已下发、Init 行 287 已加载但从未用于校验)比较;且 layIdsExpRankTableOffset_ = layerId_ * expertRankTableCnt 为 uint32×uint32 在 32 位空间回绕后才赋给 uint64。layerId_ ≥ L 或负值时:越界读 expertRankTable → 垃圾 rankCnt/toRankId → GetWindowAddr(toRankId_) → epHcclBuffer_[toRankId] 二次越界(该读发生在 Process 行 1131 的 toRankId 断言**之前**)。此前 L 恒为 1(layerId_ 恒 0)使该风险不可达,本 PR 首次激活。 **代码片段**(mc2/attention_to_ffn_v2/op_kernel/attention_to_ffn_urma.h 行 318-327、607-618): ```cpp DataCacheCleanAndInvalid<int32_t, CacheLine::SINGLE_CACHE_LINE, DcciDst::CACHELINE_OUT>(sessionIdGMTensor_); sessionId_ = sessionIdGMTensor_.GetValue(0); DataCacheCleanAndInvalid See merge request: cann/ops-transformer!12452 | 1 month ago | |
AF分离算子性能优化(batch接口多channel) Co-authored-by: jiangxiuhan1<jiangxiuhan@huawei.com> # message auto-generated for no-merge-commit merge: !12452 merge channel_ffn into master AF分离算子性能优化(batch接口多channel) Created-by: jiangxiuhan1 Commit-by: jiangxiuhan1 Merged-by: cann-robot Description: ## 描述 <!--在这里详细描述你的改动,包括改动的原因和所采取的方法。--> ## 关联的Issue <!-- 如果这个PR是为了解决特定的Issue,请在这里提供Issue链接。例如:关联Issue #000--> <!-- 如果这个PR是为了解决特定的问题单,请在这里描述问题单单号。--> ## 测试 <!--描述进行了哪些测试来验证你的改动。包括但不限于二级冒烟、算子泛化等。--> ## 文档更新 <!--如果这个PR包含文档的更新,请在这里指出。例如:更新了README.md文件。--> ## 类型标签 <!-- [x] 表示选中 --> - [ ] 🐛 Bug修复 - [ ] ✨ 新特性 - [ ] ⚡ 性能优化 - [ ] ♻️ 重构 - [ ] 🧪 测试 - [ ] 📦 构建/CI - [ ] 🔧 配置变更 - [ ] 📝 文档更新 - [ ] ⬆️ 依赖升级 - [ ] 🔒 安全修复 - [ ] 🧹 代码清理 - [ ] ❓ 其他,请描述: # PR #12452 代码检视报告 ## 检视概览 - **PR**:https://gitcode.com/cann/ops-transformer/pull/12452(AF 分离算子性能优化——URMA staging 区、多 channel、多层 Layer 支持、batch 接口) - **仓库**:ops-transformer(PR head: 950b5390e) - **变更规模**:27 个文件(26 修改 + 1 删除),diff 3680 行 - **检视范围**:mc2/attention_to_ffn(_v2)、mc2/ffn_to_attention(_v2)、mc2/common、tests/(Kernel + Tiling + op_api + torch_extension + UT + 文档 + 配置,全侧别) - **检视方法**:假设检验驱动(H0/H1、证据收集、自信值 ≥70% 判违规);规则库 cpp-secure / ascendc-topk / ascendc-api / ascendc-perf / mc2-specific / cpp-general / cpp-style / python-secure / regbase-review-checks - **检视流程**:文件分组(5 组)→ 全局预扫描(条例级匹配)→ 负载感知波次逐条检视(6 波 × 51 组,约 161 条规则实例)→ 综合研判(跨组模式识别 / 冲突解决 / 置信度过滤)→ 行号校验(红线 diff 范围校验 22/22 通过) - **检视时间**:2026-09-23 ## 检视统计 | 状态 | 条例数 | 占比 | |------|------|------| | PASS | 112 | 69.6% | | FAIL(发现问题) | 23(去重后 17 个独立问题) | 14.3% | | SUSPICIOUS(待确认附录) | 3 | 1.9% | | N/A(侧别/模式不匹配) | 22 | 13.7% | **综合研判**:系统性风险 2 项;冲突解决 2 项(MC2-04 vs MC2-18 范围互补、SEC-5.1 维持 PASS);去重 4 类根因(F2A 校验删除 7 条例、layerId 2 处、TIL-2 跨组 2 处、microBatchNum 2 条例)。 ### 全局摘要与系统性风险 本 PR 将两个 URMA 通信算子从"逐轮 SyncAll + core0 集中 WriteNbi"重构为"staging → counting-sort 建表 → 多 channel 并行 Batch WQE relay"三阶段,并引入多层 Layer(L≥1)与用户自管通信窗口。重构整体方向合理(同步完备性、DataCopy 对齐、批量搬运、资源生命周期等大量条例 PASS),但存在两类系统性回归: - **系统性风险 1(校验删除回归)**:kernel 侧与 host 侧共 4 处"删除既有防御且无替代"——F2A kernel 全部运行时元数据/窗口边界断言(7 条例独立确认)、ffn_to_attention 的 microBatchNum==1 校验、A2F 的 aivNum==0 校验、A2F 的 UB 容量校验缺失。**建议对本 PR 全部"删除 ascendc_assert / OP_TILING_CHECK"的 diff 段做一次专项全局排查**。 - **系统性风险 2(多层支持遗漏)**:allowMultiLayer 启用后,layerId 运行时校验、scales 层偏移、expertRankTable 层偏移三处仅 1 处(expertRankTable)正确实现,另 2 处遗漏。 --- ## 发现问题(HIGH 置信度) ### H1.【系统性·安全】F2A kernel 重构删除全部运行时元数据与窗口边界校验,负值/越界输入可致 GM 越界读 + 本地/远端窗口越界写 **确认条例**:SEC-2.2(105%)、SEC-1.1(120%)、SEC-1.3(95%)、SEC-3.3(85%)、SEC-3.5(105%)、MC2-05(100%)、GEN-1.1(90%)——7 个条例组独立确认同一根因 **问题描述**:本 PR 将 ReadTokenMetaData 重构为 PrefetchTokenMetaData + ReadTokenMetaDataFromLocal 时,删除了旧代码全部值域/边界断言且无任何替代: 1. actualTokenNum(int64 设备张量值)非负与 ≤maxTokenNum 两条断言被删——负值经 static_cast<uint64_t> 回绕为 ~1.8e19,scanStart/scanEnd 巨大 → PrefetchTokenMetaData 对 sessionIds/microBatchIds/tokenIds/expertOffsets 四个 GM 张量越界读;per-rank 计数越过按 maxTokenNum 分配的地址表容量。 2. curAttenWorkIds / curMicroBatchIds / curTokenBatchOffset / curTokenTopkOffset 四字段范围断言(<axisA_ / <microBatchNum_ / <axisBS_ / <expertNumPerToken_)全部被删——任一负值 int32→uint32 回绕 ~4.29e9,tokenSlot 经乘法放大为天量偏移。 3. 窗口写前断言 remoteDataOffset ≤ requiredWindowSize_、remoteFlagOffset ≤ winTokenInfoTableSize_ 与 curRankWinAddr != nullptr 判空被删——SendToLocal(本地 DataCopyPad)与 AppendRemoteDataWrite/AppendRemoteFlagWrite(URMA WriteNbi 远端 DMA 写)目标地址无界,**构成跨卡远端内存踩踏**。 4. axisH_ ≤ axisHS_ 断言被删且 FT2A tiling 无等价关系校验(对照:AT2FFN 侧本 PR 新增了 CheckUrmaWinSlotCapacity)——H>HS 时每 token 写入溢出 HS 槽位。 5. 上游无补偿:aclnn 层仅判空指针,torch 层仅校验 dim/dtype,tiling 只能校验 shape(无法校验设备张量**值**)。 **代码片段**(mc2/ffn_to_attention_v2/op_kernel/ffn_to_attention_urma.h 行 202-237): cpp inputDataAddr_ = x; xGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ xType *>(x)); sessionIdsGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(sessionIds)); microBatchIdsGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(microBatchIds)); tokenIdsGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(tokenIds)); expertOffsetsGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(expertOffsets)); actualTokenNumGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int64_t *>(actualTokenNum)); if constexpr (isInputRankTable) { attnRankTableGMTensor_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t *>(attnRankTable)); } actualTokenNum_ = static_cast<uint64_t>(actualTokenNumGMTensor_.GetValue(0UL)); // 旧: >=0 与 <=maxTokenNum_ 断言已删 hSize_ = static_cast<uint64_t>(axisH_) * sizeof(xType); // 旧: axisH_<=axisHS_ 断言已删 batchSizeSendCnt_ = static_cast<uint64_t>(axisBS_) * expertNumPerToken_; const uint64_t tokenSlotNum = static_cast<uint64_t>(microBatchNum_) * batchSizeSendCnt_; winTokenInfoTableSize_ = Ceil(tokenSlotNum * sizeof(int32_t), URMA_WIN_ADDR_ALIGN) * URMA_WIN_ADDR_ALIGN; const uint64_t tokenWindowEnd = winTokenInfoTableSize_ + tokenSlotNum * axisHS_ * sizeof(xType); const uint64_t flagWindowOffset = Ceil(tokenWindowEnd, static_cast<uint64_t>(URMA_FLAG_SLOT_SIZE)) * URMA_FLAG_SLOT_SIZE; GM_ADDR localScratchAddr = GetWindowAddr(rankId_) + flagWindowOffset; localFlagAddr_ = localScratchAddr + static_cast<uint64_t>(aivId_) * URMA_FLAG_SLOT_SIZE; **代码片段**(同文件 行 314-337,六处断言全部删除): cpp __aicore__ inline void FFNToAttentionUrma<TemplateFFNToAttentionUrmaTypeFunc>::ReadTokenMetaDataFromLocal( ReadTokenMetaDataStruct &metaDataStruct, uint32_t localOffset) { int32_t curAttenWorkId = metadataTensor_.GetValue(localOffset); // 旧: assert(>=0) 已删 metaDataStruct.curAttenWorkIds = static_cast<uint32_t>(curAttenWorkId); metaDataStruct.curAttenWorkRank = metaDataStruct.curAttenWorkIds; if constexpr (isInputRankTable) { int32_t curAttenWorkRank = attnRankTableGMTensor_.GetValue(metaDataStruct.curAttenWorkIds); // OOB 读 metaDataStruct.curAttenWorkRank = static_cast<uint32_t>(curAttenWorkRank); } int32_t curMicroBatchId = metadataTensor_.GetValue(METADATA_CHUNK_TOKEN_NUM + localOffset); metaDataStruct.curMicroBatchIds = static_cast<uint32_t>(curMicroBatchId); // 旧: assert(<microBatchNum_) 已删 int32_t curTokenBatchOffset = metadataTensor_.GetValue(METADATA_TOKEN_BATCH_OFFSET_FIELD_INDEX * METADATA_CHUNK_TOKEN_NUM + localOffset); metaDataStruct.curTokenBatchOffset = static_cast<uint32_t>(curTokenBatchOffset); // 旧: assert(<axisBS_) 已删 int32_t curTokenTopkOffset = metadataTensor_.GetValue(METADATA_TOKEN_TOPK_OFFSET_FIELD_INDEX * METADATA_CHUNK_TOKEN_NUM + localOffset); metaDataStruct.curTokenTopkOffset = static_cast<uint32_t>(curTokenTopkOffset); // 旧: assert(<expertNumPerToken_) 已删 } **代码片段**(同文件 行 499-516 + 364-377,未校验值直接推导远端写地址): cpp for (uint32_t i = 0U; i < groupTokens; ++i) { uint64_t sourceTokenIndex = groupStart + i; ReadTokenMetaDataStruct metaDataStruct; ReadTokenMetaDataFromLocal(metaDataStruct, i); // Rank range was already validated in Pass 1 over the same [scanStart, scanEnd) window. uint64_t tokenSlot = (static_cast<uint64_t>(metaDataStruct.curMicroBatchIds) * axisBS_ + metaDataStruct.curTokenBatchOffset) * expertNumPerToken_ + metaDataStruct.curTokenTopkOffset; // 三分量均未校验 uint64_t remoteDataOffset = winTokenInfoTableSize_ + tokenSlot * static_cast<uint64_t>(axisHS_) * sizeof(xType); uint64_t remoteFlagOffset = tokenSlot * sizeof(int32_t); uint32_t dstRank = metaDataStruct.curAttenWorkRank; if (dstRank == rankId_) { SendToLocal(localWindowAddr + remoteDataOffset, localWindowAddr + remoteFlagOffset, sourceTokenIndex, xCopyParams); // 本地窗口越界写 continue; } ... // AppendRemoteDataWrite(行 364-377): uint64_t remoteDataOffset = winTokenInfoTableSize_ + addressEntry.tokenSlot * static_cast<uint64_t>(axisHS_) * sizeof(xType); GM_ADDR sourceDataAddr = inputDataAddr_ + addressEntry.sourceTokenIndex * hSize_; (void)hcomm_.WriteNbi<REMOTE_WQE_CONFIG>(batchHandle, remoteWindowAddr + remoteDataOffset, sourceDataAddr, static_cast<uint32_t>(hSize_)); // URMA 远端 DMA 写,目标无界 **假设检验证据**:正向——规范违反 +40%(外部数据作索引/偏移未校验、有符号→无符号裸转换、窗口写入无边界);上下文防御缺失 +30%(唯一剩余断言 curAttenWorkRank < worldSize_(:429)只覆盖 rank 字段,注释自证仅校验 rank);PR 归属 +20%(断言删除全部在 diff 删除行内);数据流/调用链风险 +15%~+15%(输入为设备张量运行时值,偏移直接进入 WriteNbi/DataCopyPad)。负向——上游校验均经验证不覆盖张量**值**(0 分)。自信值 85%~120%(各条例)≥ 70% → 判定违规。 **修复建议**: 1. Init 恢复:int64_t v = actualTokenNumGMTensor_.GetValue(0UL); ascendc_assert(v >= 0 && (uint64_t)v <= maxTokenNum_, ...);恢复 axisH_ <= axisHS_ 断言。 2. ReadTokenMetaDataFromLocal(或 Pass 1 消费点)恢复四字段范围校验;对直接决定写地址的 tokenSlot 增加硬校验 ascendc_assert(tokenSlot < tokenSlotNum)(ascendc_assert 可能被编译掉,建议至少此项使用不可编译剔除的检查)。 3. SendToLocal/AppendRemoteDataWrite/AppendRemoteFlagWrite 写前恢复以 cclBufferSize/winTokenInfoTableSize_ 为界的偏移断言。 4. FT2A tiling 增加 Host 侧 H > HS 拒绝(对齐 AT2FFN 的 CheckUrmaWinSlotCapacity)。 --- ### H2.【安全】A2F 多层支持:运行时 layerId 无 < L 校验 + layerId_ * expertRankTableCnt uint32 乘法回绕 **确认条例**:SEC-1.1(105%)、SEC-2.2 违规点3(90%) **问题描述**:本 PR 将 expert_rank_table Dim0 校验由 ==1 放宽为 ≥1 并启用 allowMultiLayer=true,文档声明"运行时 LayerId 取值需小于 L",但**该约束无任何代码 enforcement**:kernel 侧 layerId_ 取自 GM 张量后未与 axisL_(tilingData 已下发、Init 行 287 已加载但从未用于校验)比较;且 layIdsExpRankTableOffset_ = layerId_ * expertRankTableCnt 为 uint32×uint32 在 32 位空间回绕后才赋给 uint64。layerId_ ≥ L 或负值时:越界读 expertRankTable → 垃圾 rankCnt/toRankId → GetWindowAddr(toRankId_) → epHcclBuffer_[toRankId] 二次越界(该读发生在 Process 行 1131 的 toRankId 断言**之前**)。此前 L 恒为 1(layerId_ 恒 0)使该风险不可达,本 PR 首次激活。 **代码片段**(mc2/attention_to_ffn_v2/op_kernel/attention_to_ffn_urma.h 行 318-327、607-618): ```cpp DataCacheCleanAndInvalid<int32_t, CacheLine::SINGLE_CACHE_LINE, DcciDst::CACHELINE_OUT>(sessionIdGMTensor_); sessionId_ = sessionIdGMTensor_.GetValue(0); DataCacheCleanAndInvalid See merge request: cann/ops-transformer!12452 | 1 month ago |
| Files | Last commit | Last update |
|---|---|---|
| 2 months ago | ||
| 1 month ago | ||
| 1 month ago | ||
| 2 months ago | ||
| 1 month ago | ||
| 1 month ago |