MACE源碼解析【GPU內存排布技巧】

前言

在移動端AI推理引擎中,除了一些計算上技巧外,內存排布對效率也是有一定影響的。本篇來淺析一下MACE的opencl模塊在內存上有什麼講究,應用了哪些技巧。歡迎留言討論。

權重的排布

MACE中權重是是由opencl中的image2D對象爲存儲介質(亦有buffer版的kernel),所以需要把4維的filter參數轉換到2維上。一般情況下filter默認排布爲OIHW,其中O爲該層輸出通道個數,I爲該層輸入通道個數,W和H則爲filter kernel寬高。下表是從MACE官方文檔中摘抄的關於filter參數排布的內容。

Tensor Buffer Image size [width, height] Explanation
Convolution Filter OIHW [I, (O+3)/4 * W * H] Convolution filter format,There is no difference compared to [H *W *I, (O+3)/4]
Depthwise Convlution Filter MIHW [H * W * M, (I+3)/4] Depthwise-Convolution filter format
Tensor type Pixel coordinate relationship Explanation
Convolution Filter P[m, n] = {E[o, i, h, w] | (o=[n/HW*4+k], i=m, h=T/W, w=T%W)} HW= H * W, T=n%HW, k=[0, 4)
Depthwise Convolution Filter P[m, n] = {E[0, i, h, w] | (i=[n*4+k], h=m/W, w=m%W)} only support multiplier == 1, k=[0, 4)

只看公式還是有些抽象。舉例說明一下,假設現在有一組filter,輸入爲2通道,輸出爲4通道,kernel大小爲2*2。記爲wo,ikw^{k}_{o,i} ,o和i分別爲輸入和輸出通道,k爲kernel內的編號,此例範圍爲0到3。圖1爲im2col方式中,kernel的內存排列方式。每個輸出通道佔一行,一行內則順序存儲多個輸入通道對應的kernel。默認kernel的訪問爲順序訪問。
im2col

圖1

圖2則展示了MACE上表所表示的內存排列。實際上,openCL的kernel中使用read_imagef(h)來讀取元素,因其設置爲RGBA格式,所以每次read的出來的是一個長度爲4的向量。因此這裏也是使用了以4爲單位的存儲形式,每個向量中權重的索引和輸入通道索引都是相同的,一個向量內的輸出通道是順序排列的。
mace_kernel

圖2

最後來看DepthWise情況的權重排布,dw下少了一個維度,上面公式中用M替換O的位置。M的意思應該是 multiplier ,就是在做DW的同時增加通道數,比如輸入通道爲16,輸出通道爲32,此時multiplier就爲2。MACE文檔中說只支持爲1的情況。所以這個M對應的索引就一直是常量0了。圖3列舉的是kernel size爲4,輸入通道爲4的情況。

在這裏插入圖片描述

圖3

Featuremap的內存排布

特徵圖的內存分佈相對好理解一些。假設尺寸爲NxCxHxW。想象有NxC個大小爲HxW的二維矩陣。按照順序一字排開,其中每4張圖做一個通道交織的操作,得到NxC/4張Hx4W大小的圖。圖4畫出了這個過程。第一行爲原始排列,例子中爲8個通道的2x2 feature map。做完交織後變成第二行的樣子,最終內存排布如第三行所示。假如做一個1x1卷積,取圖中1、2、3、4位置的值,原始排布則每個元素的獲取都要跨越一個imagePitch去訪存,cache命中率偏低。做4通道交織後,則可以提高訪存效率。其實就是根據做卷積時的訪存規律去設計了內存排布。
在這裏插入圖片描述
具體數學表達式例子見圖5
在這裏插入圖片描述

圖4

小結

GPU內存排布有幾個特點

  1. 因爲要使用opencl的image2d 對象作爲filter的存儲對象,所以需要映射到一個2維空間。
  2. opencl的GPU編程中,SIMD是一個基本且重要的優化手段,不論是訪存還是計算都有提速效果。所以image2d對象中的一個像素元素設置爲RGBA。爲了符合這一特點,filter的存儲都是以4通道交織爲基礎。
  3. 在MACE的ARM的版本中filter內存排布爲OIHW,即分plane排布。
  4. 在高通adreno系列GPU上使用image2d對象相比於buffer對象可以利用L1 cache。此外深度學習的模型內存可以做到很高的內存複用,比如第N層做完運算後後續沒有再依賴第N層輸入的層,就可以釋放第N層的輸入所佔用的內存,如果用內存池則這塊內存可以給後面的層去使用。在這樣的內存池的情況下,會出現實際使用的內存和內存大小形狀差距較大的情況。例如一個image2d對象大小爲1000x1200,但後續層的特徵圖很小,實際利用爲100x60。此時如果用buffer去做跨行的內存訪問那cache命中率會差很多。image對象的內存實現有內部機制,對鄰域操作較爲友好。還有如MACE這樣的4通道交織的方法也可以加寬圖像寬度,使得內存更多的在寬度上去擴展,如上所述可以提高cache命中率。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章