常见问题【免费下载链接】pyptoPyPTO发音: pai p-t-oParallel Tensor/Tile Operation编程范式。项目地址: https://gitcode.com/cann/pyptokernel函数出参未写回导致计算不生效问题现象描述当前PyPTO框架用pypto.frontend.jit装饰的kernel函数不支持有返回值输出需要通过参数的形式传入并使用[:]等进行写回操作如果直接使用等号赋值无法将数据写入输出Tensor中。示例代码pypto.frontend.jit def add_kernel(x, y): pypto.set_vec_tile_shapes(4, 4) y x 1 # 此处会创建新的Tensor y torch.npu.set_device(0) x torch.ones(4, 4, dtypetorch.float32) y torch.empty(4, 4, dtypetorch.float32) add_kernel(pypto.from_torch(x), pypto.from_torch(y)) print(y) # 输出torch.empty创建的未经初始化的随机值输出数据tensor([[2.0703e-19, 7.1833e22, 1.8502e28, 6.8608e22], [4.8011e30, 1.2123e25, 4.7418e30, 1.8465e25], [1.2122e25, 4.6114e24, 1.7836e31, 1.7591e22], [1.1306e24, 4.2245e-39, 6.8664e-44, 0.0000e00]])原因分析在add_kernel函数内部执行y x 1时这里的y是函数的局部变量相当于创建了一个新的变量y它会覆盖传入参数y的引用。也就是说这行代码只是让函数内的y指向了x 1的新Tensor并不会修改外部传入的Tensor y的内容。解决措施通过全切片操作符[:]将计算结果写入函数参数y的原有内存空间示例代码pypto.frontend.jit def add_kernel(x, y): pypto.set_vec_tile_shapes(4, 4) y[:] x 1 # 将x1的结果写入函数参数y的原有内存空间 torch.npu.set_device(0) x torch.ones(4, 4, dtypetorch.float32) y torch.empty(4, 4, dtypetorch.float32) add_kernel(pypto.from_torch(x), pypto.from_torch(y)) print(y) # 输出x 1的结果输出数据tensor([[2., 2., 2., 2.], [2., 2., 2., 2.], [2., 2., 2., 2.], [2., 2., 2., 2.]])其中y[:] x 1也可以替换为y.move(x 1)或者y.assemble(x 1, [0, 0])。未设置执行算子的设备id问题现象描述在昇腾AI处理器上执行算子时出现失败报错信息如下。2025-12-17 14:31:32.491 E | fail get device id, check if set device id 2025-12-17 14:31:32.492 E | RuntimeAgent::AllocDevAddr failed for size 20448 2025-12-17 14:31:32.493 E | RuntimeAgent::AllocDevAddr failed for size 20448 2025-12-17 14:31:32.493 E | aclmdlRICaptureGetInfo failed, return[100000]可能原因用户定义的算子未使用jit进行装饰且未使用torch_npu接口显式设置当前算子执行的Device ID。处理步骤在算子执行前设置Device ID例如def test_onboard(): device_id int(os.environ.get(TILE_FWK_DEVICE_ID, 0)) #从环境变量获取期望执行的device id torch.npu.set_device(device_id) #显式设置device id ....CANN包不兼容问题现象描述算子上板执行时出现如下报错字样ErrorTracking callback in, task_id 0, stream_id 3. [ERROR] Exception Type: exception invalid error taskid: 0, streamid: 3, tid: 6495, deviceid: 0, retcode: 507018 kernelName (null) ErrorTracking callback in, task_id 1, stream_id 3. [ERROR] Exception Type: exception invalid error taskid: 1, streamid: 3, tid: 6495, deviceid: 0, retcode: 507018 kernelName (null)且device日志中出现类似如下接口为空报错~/ascend/log/debug/device-0/device-6495_20251222194004973.log [ERROR] CCECPU(5670,aicpu_scheduler):2025-12-22-19:40:01.899.541 [ae_kernel_lib_aicpu_kfc.cpp:105][CallKernelApi][tid:5680][AICPU_PROCESSER] Get KFC DynTileFwkKernelServerInit api success, but func is nullptr: (null) [ERROR] CCECPU(5670,aicpu_scheduler):2025-12-22-19:40:01.902.745 [ae_kernel_lib_aicpu_kfc.cpp:105][CallKernelApi][tid:5681][AICPU_PROCESSER] Get KFC DynTileFwkKernelServer api success, but func is nullptr: (null)原因分析PyPTO驱动包支持25.2.0以上版本CANN包支持8.5.0以上版本。解决措施可以查看驱动包安装目录下的version信息如/usr/local/Ascend/driver/version.info Version25.3.rc1 ascendhal_version7.35.23 aicpu_version1.0 tdt_version1.0 log_version1.0 prof_version2.0 dvppkernels_version1.1 tsfw_version1.0 InnerversionV100R001C23SPC002B212 compatible_version[V100R001C19],[V100R001C20],[V100R001C21],[V100R001C22],[V100R001C23] compatible_version_fw[7.0.0,8.9.9] package_version25.3.rc1同样可以查看CANN包安装目录下opp包内的version信息如/usr/local/Ascend/ascend-toolkit/latest/opp/version.info Version8.5.0.2.220 version_dir8.5.0 timestamp20251117_000024591 required_package_amct_acl_version8.5按上述方法查看驱动包和CANN包是否满足版本要求如不满足请升级对应版本。TileShape与Tensor维度不匹配问题现象描述算子执行时出现如下报错2025-12-18 10:33:06.107 E | [ExpandFunction][Function][ERROR]: FUnction[TENSOR_b_loop_Unroll1_PATH0_hiddenfunc0] ExpandFunction failed: Tile shape size 1 is not matched the output shape size 2. 2025-12-18 10:33:06.107 E | Run pass [ExpandFunction] failed. 2025-12-18 10:33:06.107 E | Run pass ExpandFunction failed可能原因某个操作的TileShape设置的维度过小小于该操作的输出Tensor的Shape维度导致出现错误。处理步骤根据报错提示定位到相应的循环如下所说问题代码出现在b_loop循环中。FUnction[TENSOR_b_loop_Unroll1_PATH0_hiddenfunc0]找到对应的循环后根据日志提示的错误维度以及代码逻辑确定代码中TileShape的维度为1而输出Shape的维度为2将TileShape重新设置为2维即可。view未传入valid_shape导致精度问题问题现象描述部分场景使用view时未传入valid_shape导致精度问题问题原因当view接口的输入tensor没有一个正确的validshape时框架无法正确推导出输出的validshape。处理步骤当怀疑view部分的validshape推导有问题时首先给view传入一个validshape观察输出结果是否符合预期。一个典型的需要传入validshape的场景当输入的validShape依赖别的tensor标识必须传入dynValidShape。如下所示场景q0的validshape curSeq来自于另外一个tensor无法通过推导得到# 输入 input [B, S, H] # 输入 act_seqs [B] # 输出 out [B, S, H] # 计算过程 AddS # 代码如下 for b_idx in pypto.loop(B, nameb_loop, idx_nameb): cur_seq act_seqs[b_idx] a0 pypto.view(input, [1, S, H], [b_idx, 0, 0], valid_shape[1, cur_seq, H]) a1 a0 1.0 out[b_idx:, :, :] a1set_xxx_tile_shapes最后一维没有32字节对齐校验报错问题现象描述with pypto.function(TENSOR_SUM_FP32, [x], [res]): for _ in pypto.loop(1, nameLOOP_L0, idx_namea_idx): pypto.set_vec_tile_shapes(4, 8) res.move(x.sum())通过pypto.set_xxx_tile_shapes设置TileShape大小最后一维需要32字节对齐否则会校验报错。C exception with description ASSERTION FAILED: vecTile[lastDim] % alignNum 0 Sum op: the tileShape of last axis need to 32Byte align!, func Sum, file reduction.cpp, line 374 libtile_fwk_interface.so(npu::tile_fwk::Sum(npu::tile_fwk::Tensor const, int, bool)0x620) [0xffff9ff2e090]问题原因硬件指令限制处理的数据需要32字节对齐。处理步骤通过pypto.set_xxx_tile_shapes设置TileShape大小需要将最后一维大小设置成32字节对齐的数即TileShape[-1] * sizeof(dtype) % 32 0。算子编译报堆栈溢出错误问题现象描述算子编译时出现类似如下报错字样error: stack frame size (*****) exceeds limit (32768) in function *****在打屏日志中呈现样例如下error: stack frame size (47928) exceeds limit (32768) in function TENSOR_nLoop_Unroll1_PATH0_6_0_4503599627370496 error: stack frame size (47928) exceeds limit (32768) in function TENSOR_nLoop_Unroll1_PATH0_6_0_4503599627370496 2 errors generated. terminate called after throwing an instance of npu::tile_fwk::Error what(): ASSERTION FAILED: ret 0 CompileCCE failed. errCode 256, cce file: output/output_20251111_175724_806073/kernel_aicore/TENSOR_nLoop_Unroll1_PATH0_6_17699850674043372772_0_aic.cpp原因分析由于硬件限制昇腾AI处理器核内的标量处理单元栈空间最大为32K。因此在算子编译时底层的毕昇编译器会对算子核内函数的栈使用情况进行分析和校验。如果函数实现较为复杂例如变量较多或变量生命周期较长等因素均可能影响分析结果。如果最终分析结果超出32K限制毕昇编译器将拦截并报告该错误。在PTO编程模型下算子核内函数实现复杂的主要原因主要如下子图规模较大导致变量过多。子图计算逻辑复杂导致变量的生命周期较长。解决措施针对上述可能的原因可以采取以下措施进行处理调整Tensor的Tiling切分策略采用更大的Tile块进行切分。通过增加单个Tile块的计算数据量在总数据量保持不变的情况下可以减少子图所需的计算步骤从而有效减小子图的规模。相关设置接口为pypto.set_vec_tile_shapes和pypto.set_cube_tile_shapes。对于矩阵乘MATMUL计算场景建议用户对K轴进行多核切分。修改控制子图大小的选项cycle_upper_bound将单个子图的最大规模限制在指定范围内。相关设置接口为pypto.set_pass_options循环中使用Python print函数打印问题现象描述pypto.frontend.jit def add_kernel_0(a, b, c): for i in pypto.loop(20): print(i , i) c[:] a b i 0 pypto.frontend.jit def add_kernel_1(a, b, c): for i in pypto.loop(20): print(i , i) if pypto.cond(i 0): c[:] a b else: c[:] a - b i 0 i 1可能原因用户算子描述的是构图过程而非实际的执行逻辑。在构图阶段Loop执行仅用于遍历所有执行路径。示例代码add_kernel_0中仅有一条执行路径因此Loop仅执行一次。add_kernel_1中存在if/else两条路径因此Loop执行两次。处理步骤N/Alocal Tensor 无 producer 时被读取触发device侧断言问题现象描述在算子编写过程中如果 local Tensor 仅创建但未写入就在 loop 内被读取编译/执行到设备路径时可能触发设备侧断言或异常。典型报错如下ASSERTION FAILED: WORKSPACE_ITER_INVALID Root[...] incast ... slotIndex ... read from empty address.该现象通常对应读取到的 slot 没有 producer即 Tensor 对应 slot 未被任何 operation 写入。最小示例语义示意def kernel(x): t pypto.tensor((1,), pypto.DT_FP32, t) for i in range(n): _ t[0]可能原因local Tensor如t仅被创建但在被读取前没有任何写操作如切片赋值、move、assemble或其他能建立 producer 的写入导致读取路径访问到空地址并触发断言。处理步骤可采用以下任一方式规避在读取前先对 local Tensor 执行有效写入例如切片赋值、move、assemble等确保其存在 producer。将该 local Tensor 写在 loop 内作为 loop 内 Tensor 使用使其在当前 loop 作用域内pypto内存管理策略会为其申请内存地址Loop 原理及其描述在编译阶段会将loop编译为控制流将loop body转换为计算流分别对应kernel_aicpu/kernel_aicore. kernel_aicpu 负责控制流的执行创建kernel_aicore任务包括使用到的内存分配以及执行参数准备 同时分析输入输出的依赖将kernel_aicore任务合并成一个更大的调度单元然后提交给调度单元进行调度原型介绍def loop(start, end, step1, nameNone, idx_nameNone, unroll_list [1], submit_before_loopFalse): def loop_roll(start, end, step1, nameNone, idx_nameNone, unroll_list [1], submit_before_loopFalse):描述start, end, step 分别可以表示循环的起始值、结束值和步长类型可以为 SymbolicScalar 或者 int, 和Python中的range语法基本保持一致name 表示循环的名称默认值为loop_{id}, 对实际使用运行效果无影响仅用于调试和生成代码中注释信息idx_name 表示循环索引的名称默认值为loop_idx_{id}, 对于嵌套累的Loop使用相同的idx会产生覆盖行为 目前会在前端进行检查报错unroll_list 主要用于循环展开产生更大的loop body 降低调度开销。对于unroll_list2, loop大概会产生如下代码new_start start for k in unroll_list: left (stop - start) % k for idx in loop(new_start, stop - left, k): for i in range(k): body(idx) # 需要用户一次处理step1的步长 new_start stop - leftloop_unroll 大概会产生如下代码new_start start for k in unroll_list: left (stop - start) % k for idx in loop(new_start, stop - left, k): body(idx, k) # 需要用户一次处理k的步长 new_start stop - left原则上如果可以一次处理多个i 使用loop_unroll会更高效 如果一次只能处理1个i 则需要使用loopsubmit_before_loop 表示是否在循环开始前提交任务默认值为False。如果设置为True则循环前的任务会先提交到调度队列中等待后续任务完成后再开始执行过多的设置submit_before_loop会增加调度开销 建议仅在必要时设置为Trueunroll_list 对pypto.cond影响 通常一个循环中有一个pypto.cond, 会产生两个分支当unroll次数为4次时会产生 2 ** 4 16个路径分支通常上每个分支都需要单独编译 因此会大量增加编译时间和编译出的代码量. 为了支持关键算子FA的编译优化提供了两个特殊的函数pypto.is_loop_begin()和pypto.is_loop_end()用于优化条件分支考虑到对外层loop进行loop_unroll不能提升loop body的大小 当前仅支持最内侧循环进行unroll为了写代码方便可能有时看到前端算子并没有直接表达loop是如何产生loop和loop body的呢. 实际是在构图阶段前端会隐式的在function开始的位置插入一个loop 循环次数为1. 循环直到下一个循环开始前结束举例如下pypto.frontend.jit def foo(a, b, c): c[:] a b # 等价于 pypto.frontend.jit def foo(a, b, c): for i in pypto.loop(1): c[:] a b pypto.frontend.jit def foo(a, b, c): t a 1 for i in pypto.loop(1): c[:] t b # 等价于 pypto.frontend.jit def foo(a, b, c): for i in pypto.loop(1): t a 1 for i in pypto.loop(1): c[:] t b框架当前不会自动进行loop(1)合并因此在实际使用中建议用户手动合并loop(1)以提高效率【免费下载链接】pyptoPyPTO发音: pai p-t-oParallel Tensor/Tile Operation编程范式。项目地址: https://gitcode.com/cann/pypto创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考