图像 pipeline_多面体优化,Pipeline与深度学习编译器
有幸參與了MICRO2020,見識到了很多優(yōu)秀的論文,其中最讓我驚艷的是華為的在多面體優(yōu)化上做優(yōu)化的文章 <Optimizing the Memory Hierarchy by Compositing Automatic Transformations on Computations and Data>(https://www.di.ens.fr/~zhaojie/micro2020-paper),再看到video之后立刻讀了原文,覺得其中很多思想和Halide提出的Pipeline很像。因此我想在這里發(fā)表一下自己對深度學(xué)習(xí)與編譯器的結(jié)合的看法,拋磚引玉,和各位大佬討論一下。
傳統(tǒng)的多面體優(yōu)化(Polyhedral model)會將給定的程序直接當(dāng)成一大坨進(jìn)行分析,在一大段復(fù)雜的數(shù)學(xué)和工程實(shí)現(xiàn)后,將模型完成轉(zhuǎn)換,得到一個相對不錯的(自動挖掘并行度)程序。
我們都知道,在HPC領(lǐng)域我們關(guān)注的主要是循環(huán)結(jié)構(gòu),對循環(huán)結(jié)構(gòu)的調(diào)整可以影響locality, reuse distance等等。多面體優(yōu)化就是可以在保證正確性的情況下自動對循環(huán)進(jìn)行調(diào)整,并行,如下所示:
當(dāng)然,多面體優(yōu)化本身是一個極復(fù)雜的算法,我會在之后的文章單獨(dú)來寫,這次只是給大家一個直觀的認(rèn)識。
華為的這篇文章做了什么事情?首先考慮下面的代碼(例子來自論文原文)
對這個代碼做變化的話,可能會得到兩種結(jié)果:
這種方法得到的循環(huán)結(jié)構(gòu)比較整齊,都是嵌套循環(huán),利于并行
這種方法雖然盡可能的消掉了循環(huán),但是計算邏輯復(fù)雜,有很多的if判斷。
對于GPU,我們自然希望模型并行度高,而且diverge少,也就是分支判斷少,那么顯然第一種變換優(yōu)于第二種。
因此我們的目標(biāo)也比較明確:就是將程序做變換,使變換后得到的模型并行度好。
一般來說,GPU程序都是對output做并行,因此我們也希望可以整齊的切分output。這里可以介紹一下tiling的概念,對于一個普通的二重循環(huán):
for(int i = 0; i < 16; i++)for(int j = 0; j < 16; j++)fn(i,j);如果我們將兩重循環(huán)分別做切割,得到四重循環(huán):
for(int i_outer = 0; i_outer<4;i_outer++)for(int i_inner = 0; i_inner<4; i_inner++)for(int j_outer = 0; j_outer < 4; j_outer++)for(int j_inner = 0; j_inner < 4; j_inner++)int i = i_outer * 4 + i_inner;int j = j_outer * 4 + j_inner;fn(i, j);再對四重循環(huán)的二三層做交換,即順序變成i outer, j outer, i inner, j inner,那么這個變換就叫做tiling
tiling往往和棋盤格格式緊密相連,下面圖中矩陣乘法的例子就用了tiling,把C切成了9*16塊
tiling的好處在此不做展開,網(wǎng)上的資料比較多。有興趣的同學(xué)直接搜CUDA的矩陣乘法優(yōu)化即可。
介紹完了我們的目標(biāo),還要提一下另一個重要的概念:Pipeline。這個概念最開始在Halide的論文中(http://people.csail.mit.edu/jrk/halide-pldi13.pdf)被提出。Halide是一個圖像處理編譯器,做過CV的同學(xué)都知道,我們處理圖像一般都是走一個Pipeline。比如正則化->高斯模糊->調(diào)整對比度。在這里面有一個偏序關(guān)系,那就是Consumer和Producer(其實(shí)就是上下游兩個op)
生產(chǎn)者生產(chǎn)的值只會被消費(fèi)者使用,也就是說消費(fèi)者的使用決定了生產(chǎn)者的生產(chǎn)。
舉一個最經(jīng)典的例子,假設(shè)我有一個2D矩陣,我希望計算每個點(diǎn)和相鄰8個點(diǎn)的和。
那我可以把這個算法拆成一個Pipeline:
輸入矩陣->blurx矩陣(每個矩陣的元素存儲輸入矩陣每個點(diǎn)和左右兩個點(diǎn)的和)->output(每個矩陣的元素存儲blurx矩陣每個點(diǎn)和上下兩個點(diǎn)的和)
實(shí)際上就是干了下面的事情:
然而根據(jù)消費(fèi)者(out)不同的使用情況,生產(chǎn)者(blurx)的生產(chǎn)也不同,比如下面兩種例子:
第一個例子很正常,一般人都會這么寫,體現(xiàn)不出來啥。。。
這種寫法就很奇葩:幾乎不把blurx存到內(nèi)存里面,用到的時候當(dāng)場算一遍。
| 內(nèi)存占用 | 2048*3072 | 3 |
| 計算量 | 2048*3072 | 2046*3072*3 |
可以發(fā)現(xiàn)前者有最大的內(nèi)存,最小的計算量,而后者正好相反。這兩種不同的producer-consumer模式可以看作是computation和memory的tradeoff
華為的文章利用了這種思想:你不是想并行度好么?行,那我直接就把最后的輸出切一下,每個輸出需要哪些producer用多面體優(yōu)化技術(shù)算一遍,這樣我又可以做tiling(因為output被切了),又可以利用多面體優(yōu)化,對每個output小塊做優(yōu)化
論文中給的例子也很直觀:先把右下角的output均勻切成4塊,然后算每塊output需要的producer(左下角)。
我在深度學(xué)習(xí)編譯器方面也有一些了解,覺得目前看來,這種producer-consumer的優(yōu)化很popular,TVM也在用(compute_at原語)。這種通過輸出推導(dǎo)輸入可以盡可能避免冗余計算,同時生成對于output并行的結(jié)構(gòu)有良好性質(zhì)的代碼。
總結(jié)一下,之前的多面體優(yōu)化是把模型當(dāng)成一坨來優(yōu)化,而在華為的工作中,把模型看作了層級很清楚的Pipeline,對每層分別做優(yōu)化,這樣生成的程序就有更好的并行性。當(dāng)然,這篇論文更可貴的地方是提供了詳細(xì)的完整的代碼實(shí)現(xiàn),真的可以落地到實(shí)際應(yīng)用場景。不過鑒于篇幅有限(主要是我懶。。。),就簡單的把思想說一下。還是強(qiáng)烈推薦有興趣的讀者看看原文!
總結(jié)
以上是生活随笔為你收集整理的图像 pipeline_多面体优化,Pipeline与深度学习编译器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mi pay如何绑定信用卡 小米pay绑
- 下一篇: 梳理百年深度学习发展史-七月在线机器学习