已合并
engram support training #8914
luozhonglin创建于 7月20日
engram support training #8914
已合并
Pull Request已成功合入, 合并人@CANN-robot
(感谢 luozhonglin 的贡献)7月20日 创建了 pull request,commit 8a2948b5
atomgit-bot
7月20日 评论:
7月20日 评论:
变更摘要
该 PR 为 Engram 分布式嵌入系统新增了训练态支持,主要引入了两个 Ascend 自定义算子 EngramFetchTrain(前向)和 EngramFetchGrad(反向),并扩展了 ElasticBuffer 类以支持训练模式下的 all-to-all 通信、梯度聚合及 save-for-backward 上下文传递。核心思路是用融合 kernel 替代推理态的 RDMA read,前向通过桶排序→a2a→gather→a2a→还原完成数据获取,反向通过 perm 重排→a2a→scatter-add 实现 FP32 稀疏梯度聚合。
主要改动
- 新增
EngramFetchTrain算子:包含完整的 op_api(aclnnEngramFetchTrain)、算子定义(7 个输出含 perm/sendCounts/recvCounts/recvLocalEntry/numRecv 等 save-for-backward 张量)、infershape、tiling 校验与 workspace 计算,以及 AIV-only kernel 入口(单融合模式,kernel 体待实现) - 新增
EngramFetchGrad算子:包含 op_api(aclnnEngramFetchGrad)、算子定义(输出 gradUniqueOut fp32 + uniqueLocalEntryOut int32 + numUniqueOut int32)、infershape、tiling 实现(hiddenBytes 按 fp32 计算、多维度校验、overflow 保护),以及对应的 UT(op_api 侧参数校验 + tiling 侧正常/异常场景覆盖) - 扩展
ElasticBufferC++ 类:构造函数新增numMaxTokensPerRank和withGrad参数;新增EngramFetchCtx结构体传递前向 save-for-backward;新增QueryHcclBufferResource通过 HCCL 默认 buffer 获取 a2a 收发地址;EngramFetch根据withGrad分支为训练态(调用aclnnEngramFetchTrain并输出 ctx)或推理态(保持原有 RDMA read 异步调用);新增EngramFetchGrad方法调用aclnnEngramFetchGrad并按numUnique裁剪输出 - 扩展 Python 侧
ElasticBuffer:新增EngramFetchCtxdataclass 封装 save-for-backward 张量;__init__支持with_grad参数并调整 MoE 参数校验逻辑;engram_fetch在训练态返回(fetched, ctx)元组;新增engram_fetch_grad方法封装反向调用 - 新增训练样例脚本:
example_engram_train.py提供单机多卡端到端测试,覆盖前向正确性验证(fetched == storage[indices])和反向验证(gradUnique 等于每个 local entry 被请求次数乘以对应 embedding 的 FP32 累加结果)


不准确?
7月20日 添加了label:stat/needs-squash
7月20日 添加了label:cann-cla/yes
CANN-robot
7月20日 评论:
7月20日 评论:
Thanks for your pull-request.
The full list of commands accepted by me can be found at here。
You can get sig-info at here
PR Approval Progress
✅ Congratulations! All modules have met the lgtm and approve requirements.
Module Approval Details
| module | lgtm status | approve status |
|---|---|---|
| */*/op_host/*_def.cpp | ✅ 王永光, 刘丹 (2/2) | ✅ 王永光 (1/1) |
| mc2 | ✅ 刘丹, chenjunjian11 (2/2) | ✅ 刘丹 (1/1) |
💡 Tip:
- Committer can comment
/approveor/lgtm- Commenting
/approveimplies both code review (lgtm) and intent to merge (approve)
CLA Signature Pass
luozhonglin222, thanks for your pull request. All authors of the commits have signed the CLA. 👍


此处折叠了260条消息 查看更多
wangyongguang
7月25日 评论:
7月25日 评论:
/approve


7月25日 添加了label:lgtmapproved
7月25日 关闭了关联的issue
7月25日 合入了pull request
描述
engram算子支持训练HOST代码,前向复用现有接口,增加相应参数,反向增加新接口
关联的Issue
https://gitcode.com/cann/ops-transformer/issues/3838
测试
本地验证
文档更新
不涉及
类型标签
PR #8914 代码检视报告
检视概览
检视统计
逐文件组统计
系统性风险
SEC-4.1 / TOPK-7 在 H1 和 H2 中均失败:两个 Tiling 侧文件组(engram_fetch_grad 和 engram_fetch_train)均存在
sendCounts/recvLocalEntry(或sendCountsOut/recvLocalEntryOut)的 dim(0) 值未做非负/范围校验的问题。两个算子的 tiling 代码结构高度相似,校验缺失模式相同,建议全局排查并统一修复。发现问题(HIGH 置信度)
1. [engram_fetch_grad_host_G1] SEC-4.1 外部输入数据需要做合法性校验
问题描述:
sendCounts的 dim(0) 和recvLocalEntry的 dim(0) 作为外部输入(来自 graph shape)仅校验了维度数(1D),未对 dim(0) 的值做合法性校验,违反 SEC-4.1「外部输入数据需要做合法性校验且确保校验范围正确」。同一函数中commContext(行 206GetDim(0) <= 0)和gradFetched(行 221numTokens < 0、行 226hiddenDim <= 0)均对 dim 值做了显式校验,sendCounts/recvLocalEntry缺失同类校验,形成不一致的安全缺口。风险链路 1(sendCounts):dim(0) 若为负值或超过 UINT32_MAX,行 260
static_cast<uint32_t>会发生窄化截断/回绕,rankSize变为巨大错误值 →SetBlockDim和 workspace 计算均受影响。风险链路 2(recvLocalEntry):dim(0) 赋值给
int64_t totalRecv(行 281),若为负值则保留负数 →wsRecvGrad = totalRecv * hiddenBytes为负 →wsTotal求和可能为负 →static_cast<size_t>(wsTotal)将负数回绕为极大 size_t 值,导致 workspace 申请异常。代码片段(
mc2/engram_fetch_grad/op_host/op_tiling/engram_fetch_grad_tiling.cpp行 251-281):// sendCounts: 1D (W,) const gert::StorageShape *sendCountsShape = context->GetInputShape(IN_SEND_COUNTS); OP_TILING_CHECK(sendCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCounts", (std::to_string(sendCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCounts must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsShape->GetStorageShape().GetDim(0)); // recvCounts: 1D (W,) const gert::StorageShape *recvCountsShape = context->GetInputShape(IN_RECV_COUNTS); OP_TILING_CHECK(recvCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvCounts", (std::to_string(recvCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvCounts must be 1D."), return ge::GRAPH_FAILED); // recvLocalEntry: 1D (R,) const gert::StorageShape *recvLocalEntryShape = context->GetInputShape(IN_RECV_LOCAL_ENTRY); OP_TILING_CHECK(recvLocalEntryShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvLocalEntry"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvLocalEntryShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvLocalEntry", (std::to_string(recvLocalEntryShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvLocalEntry must be 1D."), return ge::GRAPH_FAILED); totalRecv = recvLocalEntryShape->GetStorageShape().GetDim(0);假设检验证据:
修复建议:
sendCountsdim(0) 值校验(> 0)recvLocalEntrydim(0) 值校验(>= 0)commContext/gradFetched的校验模式2. [engram_fetch_grad_host_G1] TOPK-7 融合规则/InferShape/Tiling 外部输入校验
问题描述:TOPK-7 要求「Tiling 外部输入使用时必须进行合法性校验」,校验内容包括「Shape 维度是否合法(不为负数、不超过限制)」。
sendCountsdim(0) 和recvLocalEntrydim(0) 作为外部输入被提取后直接使用,未做非负/范围校验。同函数内commContextdim0(<=0校验)、gradFetcheddim0(<0校验)、hiddenDim(<=0校验)均有非负防御,唯独sendCounts和recvLocalEntry缺失。此问题与 SEC-4.1 同构。代码片段(
mc2/engram_fetch_grad/op_host/op_tiling/engram_fetch_grad_tiling.cpp行 251-281):// sendCounts: 1D (W,) const gert::StorageShape *sendCountsShape = context->GetInputShape(IN_SEND_COUNTS); OP_TILING_CHECK(sendCountsShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCounts"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCounts", (std::to_string(sendCountsShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCounts must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsShape->GetStorageShape().GetDim(0)); // ...(recvLocalEntry 同理) totalRecv = recvLocalEntryShape->GetStorageShape().GetDim(0);假设检验证据:
修复建议:与 SEC-4.1 同一问题,修复方案一致。
3. [engram_fetch_train_host_G1] SEC-4.1 外部输入数据需要做合法性校验
问题描述:
CheckTensorDim中sendCountsOut和recvLocalEntryOut的 dim0 值作为外部输入未做范围校验。rankSize(uint32_t)通过static_cast从 int64_t 缩窄转换,若 dim0 为负或超过 UINT32_MAX 将静默截断/回绕为巨大值;totalRecv(int64_t)未校验非负,若为负值将导致wsLocalData = totalRecv * hiddenBytes为负,进而wsTotal为负,再经static_cast<size_t>(wsTotal)回绕为约 1.8e19 的巨大 workspace 申请。同函数内commContextShape dim0 > 0(行 188)、numTokens >= 0(行 202)、numRecvOutShape dim0 == 1(行 272)均已校验,此处校验明显不一致。代码片段(
mc2/engram_fetch_train/op_host/op_tiling/engram_fetch_train_tiling.cpp行 235-262):const gert::StorageShape *sendCountsOutShape = context->GetOutputShape(OUT_SEND_COUNTS); OP_TILING_CHECK(sendCountsOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "sendCountsOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(sendCountsOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "sendCountsOut", (std::to_string(sendCountsOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of sendCountsOut must be 1D."), return ge::GRAPH_FAILED); rankSize = static_cast<uint32_t>(sendCountsOutShape->GetStorageShape().GetDim(0)); const gert::StorageShape *recvCountsOutShape = context->GetOutputShape(OUT_RECV_COUNTS); OP_TILING_CHECK(recvCountsOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvCountsOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvCountsOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvCountsOut", (std::to_string(recvCountsOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvCountsOut must be 1D."), return ge::GRAPH_FAILED); const gert::StorageShape *recvLocalEntryOutShape = context->GetOutputShape(OUT_RECV_LOCAL_ENTRY); OP_TILING_CHECK(recvLocalEntryOutShape == nullptr, OP_LOGE_WITH_INVALID_INPUT(nodeName, "recvLocalEntryOut"), return ge::GRAPH_FAILED); OP_TILING_CHECK(recvLocalEntryOutShape->GetStorageShape().GetDimNum() != DIM_ONE, OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON(nodeName, "recvLocalEntryOut", (std::to_string(recvLocalEntryOutShape->GetStorageShape().GetDimNum()) + "D").c_str(), "The shape dim of recvLocalEntryOut must be 1D."), return ge::GRAPH_FAILED); totalRecv = recvLocalEntryOutShape->GetStorageShape().GetDim(0);假设检验证据:
修复建议:
sendCountsOutdim(0) 非负及<= UINT32_MAX校验recvLocalEntryOutdim(0)>= 0校验numTokens(行 202-205)的校验模式4. [engram_fetch_train_host_G1] TOPK-7 融合规则/InferShape/Tiling 外部输入校验
问题描述:与 SEC-4.1 同一问题。TOPK-7 要求 Tiling 外部输入的 Shape 维度不为负数、不超过限制。
sendCountsOutdim0(赋值给rankSize,缩窄为 uint32_t)和recvLocalEntryOutdim0(赋值给totalRecv)均未做非负/范围校验。同函数内commContextShape dim0 > 0(行 188)、indicesShape numTokens >= 0(行 202)均已校验,存在校验不一致。代码片段(
mc2/engram_fetch_train/op_host/op_tiling/engram_fetch_train_tiling.cpp行 235-262):(同 SEC-4.1)假设检验证据:
修复建议:与 SEC-4.1 同一问题,修复方案一致。
5. [shared] GEN-2.4 禁止 extern 声明引用外部接口
问题描述:UT 文件通过
extern "C"声明引用 aclnn 接口(非 Kernel 入口函数),违反 GEN-2.4。更严重的是,UT 中的声明与aclnn_engram_fetch_grad.cpp实现签名严重不一致:numRecv(第7输入)和commBufferSize(attr),UT 声明缺这两者;UT 声明多出hiddenSize(实现中没有);attr 位置在实现中位于 outputs 之后,在 UT 声明中位于 inputs 之后OP_API_UT 框架按 UT 声明的函数指针类型压栈调用,但 C 链接符号解析到实现函数,实现按自己的签名读栈。栈布局完全不匹配,导致 CheckNotNull 把整数当指针校验,后续 Inner 实现解引用无效指针必然段错误或数据错乱。
代码片段(
mc2/engram_fetch_grad/tests/ut/op_api/test_aclnn_engram_fetch_grad.cpp行 32-43):extern "C" { aclnnStatus aclnnEngramFetchGradGetWorkspaceSize( const aclTensor *commContext, const aclTensor *gradFetched, const aclTensor *perm, const aclTensor *sendCounts, const aclTensor *recvCounts, const aclTensor *recvLocalEntry, int32_t hiddenSize, int64_t numEntriesPerRank, aclTensor *gradUniqueOut, aclTensor *uniqueLocalEntryOut, aclTensor *numUniqueOut, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnEngramFetchGrad(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream); }对照实现(
mc2/engram_fetch_grad/op_api/aclnn_engram_fetch_grad.cpp行 63-82):aclnnStatus aclnnEngramFetchGradGetWorkspaceSize( const aclTensor *commContext, const aclTensor *gradFetched, const aclTensor *perm, const aclTensor *sendCounts, const aclTensor *recvCounts, const aclTensor *recvLocalEntry, const aclTensor *numRecv, aclTensor *gradUniqueOut, aclTensor *uniqueLocalEntryOut, aclTensor *numUniqueOut, int64_t numEntriesPerRank, int64_t commBufferSize, uint64_t *workspaceSize, aclOpExecutor **executor)假设检验证据:
修复建议:
numRecv,删除hiddenSize,补上commBufferSize,attr 移到 outputs 之后)aclnn_engram_fetch_grad.h对外头文件,UT 通过#include引入