Closed
[Bug-Report|缺陷反馈]: tril 算子 tiling 状态寄存于进程级全局,同进程连续调用两次即产出错误 tiling(matrixNum 累乘放大,kernel 循环越界) #3709
liuhanwen8Add on  20 days agoClosed at  17 days ago
liuhanwen8
liuhanwen8
20 days ago Create

一、问题描述 (必填)

experimental/conversion/tril/op_host/tril_tiling.cpp 把本次调用的 tiling 中间状态放在 namespace optiling { namespace { ... } } 的 TU 级可变全局里。其中 matrix_Num 的唯一写点是累乘,且任何路径都不复位:

  • :51 声明 int64_t matrix_Num = 1, matrix_Size = 1;
  • :148~:149 唯一写点:matrix_Num *= inputShape.GetDim(i);(只对前 rank-2 维累乘)
  • 全文件没有 matrix_Num = 1 的复位;:119 / :133 的 memset_s 只清 context 自带的 TrilTilingData,清不到该全局
  • :169 用它计算 total_Length_Aligned,:221 写入 tiling data

因此从第 2 次调用起 matrix_Num 携带历史乘积;rank == 2 的调用因为 :148 循环不执行,会直接继承上一次的值。

matrixNum 在 kernel 里是 GM 访存循环上界(op_kernel/tril.h:89 赋值,:161 / :183 为循环条件),且无任何边界校验,放大的取值会让循环次数超过实际矩阵数。这一步是代码推演,未上板观测到越界现象。

同文件 :48 的 ub_Sharing_Num 属同类:走 keyTwo 的 else 分支(:211)时不写该变量,若上一次调用执行了 :203(置 4),本次 :175 的 ub_length 会按 4 计算,tiling 随调用历史变化。

二、环境信息 (可选)

  • 昇腾硬件型号:Atlas 800T A2(910B2),CANN 9.2.0,aarch64,g++ 11.4.0
  • 复现方式:以最小桩头文件直接编译上述 tiling 源文件(源文件未改动),在同一进程内按序调用 tiling 入口
  • 未覆盖:device 侧 aclnn 全链路(该算子仅 --experimental 构建时入包,见备注)

三、重现步骤 (可选)

同进程连续调用,float32、diagonal=0:

调用序列 第 2 次 matrixNum 第 2 次 lastTileLength
每次单起进程(基线) 2 / 1 24 / 32
同进程 {2,3,4} → {2,3,4} 4 48
同进程 {2,3,4} → {5,6} 2(应为 1) 64(应为 32)
同进程 {2,3,4} → {0,5} → {2,3,4} 4 48

第 2 行是两次入参完全相同的情形,第 2 次仍然产出不同的 tiling。第 4 行说明空输入在 :116 提前返回、同样不复位。

ub_Sharing_Num:{1,200,200} diagonal=200(走 keyThree,:203 置 4)后再调 diagonal=-200(走 keyTwo),loopCnt 由基线 2 变 4、fullTileLength 由 24568 变 12280。

四、预期结果 (可选)

同一进程内任意调用顺序下,相同入参应产出完全相同的 tiling。建议把这 15 个状态改为 tiling 类的私有成员(框架每次 tiling 新建实例);最小改动至少要在 :148 循环前把 matrix_Num 置 1、进入分支前把 ub_Sharing_Num 恢复默认 2。仅补复位属治标,载体仍是进程级全局,并发与其余状态变量未处理。

💡 备注(选填)

  • 可达性:CMakeLists.txt:47 为 option(ENABLE_EXPERIMENTAL "Enable experimental module" OFF),:132~:133 才 add_subdirectory(experimental),故默认发布包不含该算子;以 --experimental 构建后即受影响。已发布的 aclnnTril 对应 conversion/tril(arch35 路径),该路径无同类全局。
  • 成对算子 triu 存在同型写法,已另开 #3710 跟踪。
  • 该算子 tests/ut/ 仅含 .gitkeep(无 UT),examples/ 每进程只调用一次 aclnn,故该问题未被用例覆盖。
  • 查重:本仓 state=all 全量翻页 3608 条逐条比对(关键词:tril / triu / 全局变量 / 静态变量 / tiling 状态 / 跨调用 / 可重入 / 二次调用 / 同进程 / matrix_Num / matrixNum / ub_Sharing_Num / 并发),未命中同一点位。同缺陷类别的在办单 #3675(aclnnAddN 同进程二次调用踩残留状态)与已结单 #801(mul 算子 tiling 全局变量作用域限制)、#1004、#1772 均为不同算子或不同机理,非重复;其中 #801 的处置方向是加匿名空间以隔离跨算子符号,与本次的跨调用状态残留不是同一问题。
likedislike
liuhanwen8liuhanwen8
20 days ago Added label: bug-report
liuhanwen8liuhanwen8
20 days ago Modify description of issue
陈思
陈思Collaborators
20 days ago Comments:

您好,感谢反馈!

问题分析

核查 cann/ops-math 的固定版本 b19eaee4f58e5e1e3cb3b2ecbfed2d02c11c820a 后,可以确认您指出的跨调用状态残留:TrilTilingFunc 使用匿名命名空间中的可变变量,matrix_Num 只在声明时初始化为 1,之后按前 rank-2 维累乘;两处 memset_s 清理的是本次的 TrilTilingData,不会清除这些变量。因此,同一进程内连续对 {2,3,4} 做 tiling 时,矩阵数会从 2 累乘为 4;后续二维输入不执行累乘循环,也会继承此前的矩阵数。空输入的提前返回同样不会恢复该状态。

该值随后写入 matrixNum 并参与 totalLengthAligned 计算。kernel 的 NaivePath 和 FastPath 直接以 matrixNum 为矩阵循环上界,因此参数放大可能使访问范围超过本次输入、输出的实际矩阵数。这是当前源码能够确认的风险机制;本次没有进行上板验证,不能将其表述为已经观测到的设备越界。

ub_Sharing_Num 也有相同的生命周期问题:快速路径和 keyThree 分支会将其设为 4,而后续普通路径或 keyTwo 分支不会恢复默认值 2,导致 UB 分块参数受先前调用影响。上述结论限定在 experimental/conversion/tril;顶层构建配置 默认关闭 ENABLE_EXPERIMENTAL,启用该选项(如 --experimental)才进入这套实现,不能据此推断其他 Tril 实现也存在同一问题。

处理方向

一个可行的方案是把这 15 个可变 tiling 中间量收敛为每次 TrilTilingFunc 调用独立持有的局部状态,并在本次选定缓冲策略后,统一计算 UB 长度、循环次数和尾块参数。这样可以消除跨调用残留,也避免并发调用共享写状态。仅复位 matrix_Num 和 ub_Sharing_Num 可以缓解顺序调用现象,但不能解决其余全局变量的并发共享问题。

验证重点可沿用您给出的相同输入重复调用、批量输入切换到二维、空输入插入和不同 tiling 分支切换,并进一步确认并发调用与设备侧访问范围。我们会继续确认该问题。

likedislike
陈思陈思Collaborators
20 days ago Assign chensi79 as responsible person
陈思陈思Collaborators
20 days ago Added label: pending-confirmation
陈思陈思Collaborators
20 days ago Removed label: pending-confirmation
陈思陈思Collaborators
20 days ago Associated pull request
CANN-robotCANN-robotCollaborators
17 days ago Close issue
CANN-robotCANN-robotCollaborators
17 days ago Added label: resolved