[技术干货-算子使用] Mindspore 控制流中存在原地更新操作类副作用算子时循环值不更新问题记录

关于mindspore 原地更新类算子的一点思考记录如下:

现象记录:

         原始测试代码

错误结果复现:

分析:

         如果在场景中加入42行的copy()操作此时cpu的结果就会正确,但是gpu的结果则不受copy做操作的影响

修改后测试结果:

Mindspore 控制流场景中,我们的H是由mnp.one 新建出来的,但是其底层是调用的numpy的封装申请出来的是一个const的数据,这样的数据通过赋值给H,如果H在传递到其他地方,其内部被修改,下次在赋值,这块const的地址不会新创建,那么下次循环H的

值就会不对了。

解决办法:

  1. 打開42行copy的注釋
  2. 將k 以及 k_iter的值從tensor改成数值0,这样while控制流会将这些body 展开,相当于 H的值存储了多份也不会有任何更改
  3. 查看tensorscatterNdUpdate 后端实现,确定其实现是否是直接修改输入并且返回输入的操作

     结果发现:其实现是在input的基础上inplace操作然后拷贝到输出地址,这样修改了原先的干净的输入地址,倘若存在内存复用其生命周期还未结束,那么其他地方拿到的值就会被污染,因此修改实现为拷贝输入到输出然后在做inplace 操作,修改后代码,这样不论输入的生命周期是否结束,均不会影响到原来的值

总结:  

         1.为什么静态图下会出错?

             动态图中不会有静态的推断,H的值每次都会新赋值不同地址,因此上次的H的地址内容被修改,不会有影响

  1. 为什么cpu会出错 gpu 不出错?

            因为cpu不存在host和device的概念,在图运行时cpu inplace类算子做相关操作时直接污染了cpu的输入原始地址,但是gpu操作时会先将host侧内容拷贝到device侧操作那么device侧输入原始地址被污染没关系,下次循环子图时会有新的地址过来,无影响。

  1. 解决办法加copy操作好还是直接修改后端算子好?

             3.1. 直接加copy会导致某一后端正确(cpu)但某一后端无影响是冗余操作,不妥且只是规避并没有解决这个问题。

             3.2. 直接修改后端算子的话除了能解决当前场景的问题,也能避免内存复用引用计数时因为输入的生命周期过长,而输入的原始地址被污染产生更难定位的问题。

             3.3.  同时也能知道mindspore的机制在后端是不支持直接的inplace操作的,一切对输入的inplace操作,都存在一定的隐患,因此编写算子过程中不光cpu 其他gpu ascend,均应该先将输入拷贝到输出,再在输出中做inplace 操作,不管要保证输出地址的合法性也要保证输入地址没有被污染。因此编写原地更新操作类算子应该遵循上述准则才可以。

对应pr 修改:

         https://gitee.com/mindspore/mindspore/pulls/27203/files

https://bbs.huaweicloud.com/forum/forum.php?mod=viewthread&tid=171550&fromuid=642331

posted @ 2021-12-30 11:12  Zoloft先生  阅读(83)  评论(0编辑  收藏  举报