Open
【代码侦探Challenge13】北京林业大学 - LeakyReluCustom算子实现 #5645
【代码侦探Challenge13】北京林业大学 - LeakyReluCustom算子实现 #5645
Open
FineL1Add on 2 days ago
FineL1
FineL1
2 days ago

北京林业大学

团队信息

成员

实现基于 Ascend C 的 LeakyReLU 激活算子,支持 float16 / float32 数据类型。按照开源仓规范完成算子属性注册、输出形状推导及 Tiling 数据配置。在 op_def 中注册属性参数 negative_slope,由 Tiling 函数读取该属性并写入 TilingData,传递至 Kernel 侧使用,实现负半轴斜率的动态配置。Kernel 采用 CopyIn → Compute → CopyOut 的流水处理方式,通过 TQue 和 LocalTensor 管理 UB 数据,实现输入数据从 GM 到 UB 的搬运、逐元素计算及结果回写,并利用双缓冲机制提高数据搬运与计算效率。计算阶段基于 Ascend C LeakyRelu 接口实现:
y = max(x, 0) + negative_slope × min(x, 0)
其中 negative_slope 默认值为 0.01。算子面向昇腾 910B4 平台,支持题目指定的 (8, 2048) 输入张量,实现 LeakyReLU 激活功能的开源仓规范化开发。

CANNJudge 测试已通过,验证结果为 PASS。

likedislike
Merge blocked
FineL1FineL1
2 days ago Created pull request, commit 27359238
atomgit-bot
atomgit-bot
2 days ago Comments:

变更摘要

本 PR 为北京林业大学在“代码侦探(第十三期)”中提交的 LeakyReLUCustom 算子实现,基于 Ascend C 构建了一套完整的开源仓规范化工程:在 op_def 中注册单输入 x、单输出 y 的 Leakyrelu 算子(支持 float16 / float32、FORMAT_ND)与属性 negative_slope,通过 Tiling 读取该属性并写入 LeakyreluTilingData 传递到 Kernel 侧;Kernel 采用 TQue 双缓冲的 CopyIn → Compute → CopyOut 流水,调用 Ascend C LeakyRelu 接口完成 y = max(x, 0) + negative_slope × min(x, 0) 的逐元素计算,并配套形状推导、aclnn 样例、数据生成/校验脚本与 UT 测试框架。

主要改动

  • 算子定义与属性注册:新增 op_host/leakyrelu_def.cpp,注册 Leakyrelu 的输入 x、输出 y(DT_FLOAT / DT_FLOAT16、FORMAT_ND、AutoContiguous)以及 Float 类型属性 negative_slope,并通过 AICore().AddConfig("ascend910b") 指定昇腾 910B 平台
  • 形状推导实现:新增 op_host/leakyrelu_infershape.cpp,InferShapeLeakyrelu 校验输入/输出 Shape 指针后将输入形状直接赋给输出,体现逐元素算子的形状一致性
  • Tiling 切分与 TilingData 传递:新增 op_host/leakyrelu_tiling.cpp,按 MIN_SPLIT_THRESHOLD 与 AIV 核数确定 blockDim,结合 UB 块对齐与双队列双缓冲容量计算 blockFactor / ubFactor,将 totalNum、blockFactor、ubFactor、negativeSlope 写入 LeakyreluTilingData,并按 dtype 设置 schMode 模板 TilingKey(0 对应 float16、1 对应 float32)
  • Kernel 流水实现:新增 op_kernel/leakyrelu.cpp 与 leakyrelu.h,NsLeakyrelu::Leakyrelu<T> 通过 GetBlockIdx() 划分核内数据段,以 BUFFER_NUM = 2 的 TQue 完成 DataCopy 搬运、LeakyRelu 计算与结果回写,并由 leakyrelu<schMode> 按 TilingKey 实例化 half / float 分支;同时新增 leakyrelu_tiling_data.h 与 leakyrelu_tiling_key.h 定义数据结构与 ASCENDC_TPL_ARGS_DECL 模板参数
  • 样例与精度校验链路:新增 examples/test_aclnn_leakyrelu.cpp、sample_support.h 与 samples/sample.py、samples/reference.py、samples/case.json,以 (8, 2048) float32 用例调用 aclnnLeakyreluGetWorkspaceSize / aclnnLeakyrelu,并按字节长度、形状与 rtol/atol 比对输出与参考结果
  • 构建与 UT 支撑:新增 build.sh、tests/ut/run.sh 及 UT 公共设施(tiling_context_faker、infershape_context_faker、tiling_case_executor、any_value.h、BuildGoogleTest.cmake),并给出 test_leakyrelu_tiling.cpp、test_leakyrelu.cpp、test_op_host_main.cpp 覆盖 Tiling 期望值校验与 kernel 直接执行、结果落盘比对
likedislike
Incorrect?
atomgit-bot
atomgit-bot
2 days ago Comments:

代码审查

审查结论

  • [建议] 2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/bfu_FineL1_13/code/exercises/op_host/leakyrelu_tiling.cpp:104 fix: tiling 未保证每核尾块按 32B 对齐,kernel DataCopy 可能拷错尾部
  • [建议] 2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/bfu_FineL1_13/code/exercises/op_kernel/leakyrelu.h:83 fix: 尾块搬运未用 DataCopyPad,非 32B 对齐时越界/漏写
  • [建议] 2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/bfu_FineL1_13/code/exercises/tests/ut/op_kernel/test_leakyrelu.cpp:71 fix: 断言失败路径未释放 GM 内存导致泄漏
  • [建议] 2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/bfu_FineL1_13/code/exercises/tests/ut/op_kernel/test_leakyrelu.cpp:77 fix: tilingData 缓冲区未清零且只赋值 3 个字段
类型 数量
🔴 阻塞 0
🟡 建议 4

💬 仅评论

likedislike
Incorrect?
FineL1
FineL1
2 days ago Comments:

/compile

likedislike
CANN-robotCANN-robotCollaborators
2 days ago Added label: cann-cla/yes
CANN-robot
CANN-robotCollaborators
2 days ago Comments:

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.
You can self-configure the PR merge rules for this repository. For more details, please refer to Here.
For more, you also can visit HICANN.


PR Approval Progress

⚠️ This PR does not yet meet the following requirements:lgtm (requires ≥ 2 person(s) per module)、approve (requires ≥ 1 person(s) per module)

Module Approval Details

module lgtm status approve status
repo-cann/cann-outreach ❌ (0/2)(You can also ask: 田晓亮, li-shengxian3, jxlang, gcw_tgPdOLZT, yanhf) ❌ (0/1)(You can also ask: shaoyf, yanhf, luzx66, 傅涛, 田晓亮)

💡 Tip:

  • Committer can comment /approve or /lgtm
  • Commenting /approve implies both code review (lgtm) and intent to merge (approve)
likedislike
CANN-robot
CANN-robotCollaborators
2 days ago Comments:

CLA Signature Pass

FineL1, thanks for your pull request. All authors of the commits have signed the CLA. 👍

likedislike
CANN-robotCANN-robotCollaborators
2 days ago Set yanhf,jxlang,shaoyf,TianXL,yanyawen,fullt,gcw_tgPdOLZT,Carolina_yuan,luzx66,cherry_03,li-shengxian3 as approver
CANN-robotCANN-robotCollaborators
2 days ago Set yanhf,jxlang,shaoyf,TianXL,yanyawen,fullt,gcw_tgPdOLZT,Carolina_yuan,luzx66,cherry_03,li-shengxian3 as approver
FineL1FineL1
2 days ago Modify description of pull request
FineL1FineL1
2 days ago Modify description of pull request
FineL1FineL1
2 days ago Change title to "[代码侦探(第十三期)] 北京林业大学 - LeakyReluCustom", originally titled "[代码侦探(第十三期)] 北京林业大学 - exercises".
FineL1FineL1
2 days ago Change title to "【代码侦探Challenge13】北京林业大学 - LeakyReluCustom算子实现", originally titled "[代码侦探(第十三期)] 北京林业大学 - LeakyReluCustom".
FineL1FineL1
2 days ago Associated issue: 【代码侦探Challenge13】北京林业大学 - LeakyReluCustom算子实现
atomgit-bot
atomgit-bot2 days agoStart code review1
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/bfu_FineL1_13/code/exercises/tests/ut/op_kernel/test_leakyrelu.cpp
@@ -0,0 +68,4 @@
68+ uint8_t* workspace = (uint8_t*)AscendC::GmAlloc(32);
69+ uint8_t* tiling = (uint8_t*)AscendC::GmAlloc(tilingDataSize);
70+
71+ { std::ifstream f(root + "/input/x.bin", std::ios::binary); ASSERT_TRUE(f.good()); f.read(reinterpret_cast<char*>(x), 65536); ASSERT_EQ(f.gcount(), 65536); }
atomgit-bot
atomgit-bot2 days agoComments:

🟡 Medium Priority

变更行 71:在 AscendC::GmAlloc 分配 x / y / workspace / tiling(第 66-69 行)之后,立刻用 ASSERT_TRUE(f.good()) 与 ASSERT_EQ(f.gcount(), 65536) 做断言。ASSERT_* 系列在失败时会 return,直接从测试函数返回,而 AscendC::GmFree 只在函数末尾第 95-98 行执行。失败模式:只要 $SAMPLE_DIR/input/x.bin 不存在(例如 sample.py generate 未生成、或 SAMPLE_DIR 指向别的目录),本测试会在持有 4 块 GM 内存的情况下提前返回,四块内存全部泄漏;在多次 gtest 迭代/重复运行时泄漏持续累积。修复:改用 EXPECT_* 让流程走到末尾释放,或把 GM 指针包成 RAII(unique_ptr + 自定义 deleter / 局部 struct),确保任何 return 路径都释放。

建议:把 GM 资源改为 RAII 管理(如 unique_ptr 配 GmFree deleter),或将第 71 行的 ASSERT_TRUE/ASSERT_EQ 改成 EXPECT_* 以保证流程必经第 95-98 行的 GmFree;同时在文件缺失时提前 return 前先释放已分配的 4 块内存。

likedislike
Incorrect?
atomgit-bot
atomgit-bot2 days agoStart code review1
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/bfu_FineL1_13/code/exercises/op_host/leakyrelu_tiling.cpp
@@ -0,0 +107,4 @@
107+ // 为队列预留的总UB空间不超过平台UB容量。
108+ const int64_t maxUbElements =
109+ static_cast<int64_t>(ubSize / (QUEUE_BUFFER_NUM * QUEUE_COUNT * typeSize));
110+ const int64_t ubFactor = FloorAlign(std::min(blockFactor, maxUbElements), alignElements);
atomgit-bot
atomgit-bot2 days agoComments:

🟡 Medium Priority

变更行 leakyrelu_tiling.cpp:104-110 只对 blockFactor 与 ubFactor 做了 CeilAlign/FloorAlign(alignElements),但下发给 kernel 的 blockLength_(每核实际元素数 = totalNum - blockIdx*blockFactor,见 op_kernel/leakyrelu.h:51-61)以及每次循环的 currentNum 都只是 min(blockLength_, ubFactor),并没有保证是 alignElements 的整数倍。kernel 侧 CopyIn/CopyOut(leakyrelu.h:83、leakyrelu.h:109)使用的是 DataCopy(dst, src, count) 而非 DataCopyPad,该接口要求拷贝长度字节数为 32B 的整数倍。

触发条件:输入元素总数 × dtype 字节数不是 32 的倍数,例如 fp32 形状 (3,5)=15 元素(60 字节)或 (1,9)(36 字节)。此时 totalNum=15、blockDim=1、blockFactor=CeilAlign(15,8)=16,最后一块 currentNum=15,DataCopy 长度为 60 字节,非 32B 对齐,尾部元素可能丢失或被越界搬运,表现为精度不通过或 AIC 报错;CopyOut 同样存在该问题。当前题目用例 (8,2048) fp32 = 65536 字节恰好对齐,因此该缺陷在示例用例上不可见,属边界缺陷。

建议:在 kernel 的 CopyIn/CopyOut 尾块使用 DataCopyPad(非对齐长度用 DataCopyExtParams + DataCopyPadExtParams),或在 tiling 中显式判断 blockLength_/最后一次 currentNum 是否 32B 对齐并给出明确处理(补零/退化到标量拷贝),避免现有 DataCopy 在非对齐尾块上出错。

likedislike
Incorrect?
atomgit-bot
atomgit-bot2 days agoStart code review1
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/bfu_FineL1_13/code/exercises/op_kernel/leakyrelu.h
@@ -0,0 +80,4 @@
80+__aicore__ inline void Leakyrelu<T>::CopyIn(int64_t progress, int64_t currentNum)
81+{
82+ LocalTensor<T> inputLocal = inputQueueX.AllocTensor<T>();
83+ DataCopy(inputLocal, inputGMX[progress * ubLength_], static_cast<uint32_t>(currentNum));
atomgit-bot
atomgit-bot2 days agoComments:

🟡 Medium Priority

改动行:leakyrelu.h 第 83 行 DataCopy(inputLocal, inputGMX[progress * ubLength_], static_cast<uint32_t>(currentNum));(第 109 行 CopyOut 是同一写法的镜像)。

契约:Ascend C 的 DataCopy(LocalTensor, GlobalTensor, calCount) 以 32B DataBlock 为搬运粒度,要求 calCount * sizeof(T) 为 32 的整数倍、且 GM 基址按 32B 对齐;该接口本身不具备非对齐尾块处理能力,非对齐场景必须使用 DataCopyPad。

失败路径:核内 blockOffset = GetBlockIdx() * blockFactor、blockLength_ = totalNum - blockOffset(第 53–61 行),尾 tile currentNum = blockLength_ - progress*ubLength_(第 124 行)。只要 totalNum 不是 32/sizeof(T) 的整数倍(float 为 8、half 为 16),最后一个核的 blockLength_ 与最后一次 currentNum 必然非对齐:例如 totalNum=100、blockFactor=64 时 1 号核 currentNum=36(float 为 144B),指令按 32B 粒度读/写会多出尾部字节 —— 输入侧多读、输出侧越界写入 outputLocal 已分配长度之外的 UB 空间(InitBuffer 只按 ubLength_ * sizeof(T) 分配,见第 73–74 行),踩坏相邻缓冲或产生 AIC 精度错误;同时第 69–70 行 SetGlobalBuffer((__gm__ T*)x + blockOffset, ...) 的基址在 blockFactor 非对齐时也不满足对齐要求。该算子的 infershape 是将输入 shape 透传到输出的通用实现,任意非对齐 shape 都可达。

建议:把 CopyIn/CopyOut 的尾块搬运改为 DataCopyPad(传入 DataCopyExtParams{1, currentNum*sizeof(T), 0, 0, 0} 与 DataCopyPadExtParams),或由 tiling 保证 totalNum/blockFactor/ubFactor 均 32B 对齐并在 kernel 中加断言保护该前提。

likedislike
Incorrect?
atomgit-bot
atomgit-bot2 days agoStart code review1
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/bfu_FineL1_13/code/exercises/tests/ut/op_kernel/test_leakyrelu.cpp
@@ -0,0 +77,4 @@
77+ LeakyreluTilingData* tilingData = reinterpret_cast<LeakyreluTilingData*>(tiling);
78+ tilingData->totalNum = size;
79+ tilingData->blockFactor = size;
80+ tilingData->ubFactor = size;
atomgit-bot
atomgit-bot2 days agoComments:

🟡 Medium Priority

变更行 69/77-80:tiling = GmAlloc(tilingDataSize) 分配的是未初始化内存(GmAlloc 等价 malloc,不做清零),随后只写了 totalNum / blockFactor / ubFactor 三个字段。而按 PR 描述,negative_slope 是由 Tiling 读取 op 属性后写入 TilingData、再传给 kernel 使用的(LeakyreluTilingData 定义在 op_kernel/leakyrelu_tiling_data.h)。失败模式:只要 LeakyreluTilingData 中还存在第 4 个及以后的字段(如 negativeSlope、shape 相关字段),kernel 侧 GET_TILING_DATA 用 memcpy 整结构体拷入后读到的是 GmAlloc 残留的随机字节,负半轴斜率变成随机值,测试结果不确定(时好时坏或恒错),且错误难以定位。修复:先 memset(tiling, 0, tilingDataSize) 清零,再按 leakyrelu_tiling_data.h 的完整字段列表逐项赋值(至少包含 negativeSlope = 0.01f,与 op_host 侧 tiling 保持一致)。

建议:在 reinterpret_cast 之前 memset(tiling, 0, tilingDataSize) 清零,并按 op_kernel/leakyrelu_tiling_data.h 的完整字段清单赋初值(尤其 negativeSlope),不要只赋模板中的 3 个字段。

likedislike
Incorrect?