再次回到夢開始的地方: Cell的call方法:
如果我們當前不是GRAPH_MODE, 那我們就會跳過378的判斷, 繼續(xù)向下, 走到391行, 用_pynative_exec來創(chuàng)建graph, 繼而執(zhí)行與釋放資源等.
同樣經過一輪跟第1篇類似的操作, 我們可以追蹤到c++代碼的這里:
進去看一下new_graph和end_graph都在做什么:
看上圖, 大概就是調用了另一個叫做GradExecutor的類來執(zhí)行new和end操作, 內部實現呢:
看起來init的時候內部有個什么stack用來存儲需要運行的cell的樣子. 這個后面再深究吧.
在end的時候, 還會對這個stack做pop操作, 應該是跟之前的init對應的.
回到Cell的call方法, 它調用的run_construct實現在哪兒呢?
可以看到, 這里會調用我們手寫的layer的construct函數, 而我們的網絡會調用一些現成的layer或者算子, 因為layer最終還是調用到算子上, 所以我們來看一下算子是如何被調用的.
因為算子跟layer相似, 都是在init函數中創(chuàng)建算子對象, 在construct函數把對象當成函數調用, 就像這樣:
因此算子具體執(zhí)行了什么邏輯, 也是需要看算子的call方法, 所有的算子的基類都是primitive這個類, 我們注意到primitive這個類就是有call方法的:
會調用_run_op這個方法, 然后調用real_run_op:
這個real_run_op同樣也是從c++側定義好, 供python側調用的:
這還是祖?zhèn)魇炙嚵? 繼續(xù)去c++側找具體實現:
在mindspore\ccsrc\pipeline\pynative\pynative_execute.cc這個文件下的RealRunOp方法:
又是我們的老朋友PynativeExecutor了, 繼續(xù)看它的forward_executor和RunOpS方法:
繼續(xù)看RunOpInner的實現:
看起來前4步都是在準備一些需要的輸入數據和輸出格式, 最后一步是執(zhí)行計算, 比如說第四步時需要進行shape和dtype的推斷:
繼續(xù)看:
回調到python側:
以Add算子為例的話, 就會調用到它的infer_value, 但是實際上這里的x和y不是常量的Tensor, 都是None,
實際的計算還是在第5步進行. 那我們還是關注一下第5步的實現:
繼續(xù)看, 在backendpolicy里面隨便選一個, 就看RunOpInVM吧
繼續(xù)看到RunPyComputeFunction
那么vm是拿什么函數來執(zhí)行的呢?
可以看到, 這個vm有個注冊器, 只要注冊在里面的算子就可以找到它的vm的實現, 從而調用到, 以Add為例的話, 可以該算子測試時是有python本地實現的:
但我們此時安裝的mindspore并不包含test包, 在我們的包的安裝目錄下是找不到上圖這段代碼的.
也就是說我們走的不是vm的邏輯, 而是走的其他邏輯. 哈哈哈, 我這里繞了個大彎路, 但是我覺得寫出來也是有意義的, 因為我通過設置環(huán)境變量來打印了更多的log, 后面還是發(fā)現在pycharm中實際走的是哪一個了! mindspore有一個叫做GLOG_v的環(huán)境變量, 默認為3, 也就是只打印warning和error, 我們可以把它設置為0, 就可以看到info和debug信息了! 如何設置呢? 很簡單, 就在pycharm, run, edit configurations就可以了:
增加一個環(huán)境變量GLOG_v, 值設為0:
重新跑一次我們的用例, 就可以看到詳細的log了
通過log其實可以看到, 我們走的是這個!
在這個方法中, 會調用session來進行計算, 這里的session根據我們的設置不同, 可以是cpu/gpu或者ascend.
我在自己家里的電腦, 自然是cpu, 繼續(xù)看log也可以證明這一點.
在執(zhí)行算子kernel之前, 還會檢查已經注冊過的算子中是否有當前的這個名字(Add). 這里又有很長的故事了, 以后有機會再寫.
cpu_session會調用runtime的run方法
繼而調用每一個kernel的launch方法
注意這個kernel_mod是一個基類, 每一個kernel都是它的子類:
最終會調用到cpukernel下Add算子的實現:
總結一下就是:
動態(tài)圖的執(zhí)行從python側Cell的call方法開始, 調用到算子的call方法, 層層封裝調到了c++側. 通過不同的設置可以選擇走ms或者ge或者vm, 我們已經看到vm在我們用戶安裝包里是不會走到的, 我的情況是走的ms, 然后又會選擇不同的后端(cpu/gpu/ascend)來選擇不同的算子, 在選擇算子這里需要算子提前注冊好. 然后最終調用到算子的launch方法, 實現真正的計算.
當然在這個過程中我沒有關心資源管理的問題, 后續(xù)有機會再寫文章詳細探討