[技術乾貨-運算元使用] 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 修改:

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

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