2018年4月11日 星期三

Halide - 實務心得 2

與第一篇著重在與 C/C++ 整合與比較特別的小技巧有所不同
在第二篇中談論到的是演算應用實務上會碰到值得注意的地方
由於此系列並不是有所規劃的, 因此日後累積足夠多心得再撰寫後續文章

演算法歸納撰寫方式

Halide 對於 RDom 的處理相當一板一眼
其強調於演算與排程的脫勾
無論如何必須注意的是 Halide 並不會在演算層次上分析多餘的計算並做演算變更
因此演算描述的好壞也決定了一部份效能的結果, 所以演算上的精練也是很重要的
像是影像處理上許多的演算實則為同一 filter 套用在不同方向上 (eg: gaussian blur or box filter)
若符合這個條件時則可以考慮分為 2-stage 的作法, 這樣的好處有二:

1. 大幅減少運算
像是 5-tap filter, 若套用在 2D 上需要計算 5x5 = 25 次
若分為 2-stage 則為 5+5 = 10次, 可以說 tap 數愈高, 差異愈大

2. 可重複某一平方向結果
由於是在垂直方向套用相同 filter , 因此可以將套用一個方向的結果存下, 重複利用在不同點的計算

因此在演算的撰寫上還是需要注意演算的表示上是否有進一步優化的可能

AOT 在局部影像 ROI (Region Of Interest) 的處理

特定情況需要套用演算在特定區域上, 而非全畫面的處理
這時可以嘗試產生對應特定區域的 Buffer  物件
Halide::Runtime::Buffer 中支援 cropped 操作
對於此操作文件描述為 "Make an image that refers to a sub-range of this image along the given dimension.", 明確指出是直接參考 Buffer 的特定區域.
使用範例:
Buffer<uint8_t> buf(width, height);    //2D buffer
Buffer<uint8_t> crop_buf= buf.cropped(0, crop_x, crop_w).cropped(1, crop_y, crop_h);
如此可以將取得的 crop_buf 帶入到 AOT function 中, 即可只對特定區域做處理

Generator / Auto-Scheduler

JIT 方式儘管套用相當方便,
但因第一次呼叫都需要花時間 realize, 且並不是所有平台都支援 JIT
因此使用上對於嘗試與試驗較好;
對於固定流程之後的應用而言, 使用 AOT 較為實際.
強烈建議將實作的 Func 在獨立撰寫之後轉為 Generator 形式,
除了能夠迅速跨越不同硬體架構平台,
也能夠在完成後快速地套用 Auto Scheduler 來做優化的嘗試. (甚至如同這篇一般做不同優化方式比較)

沒有留言:

在 ARM 平台上使用 Function Multi-Versioning (FMV) - 以使用 Android NDK 為例

Function Multi-Versioning (FMV) 過往的 CPU 發展歷程中, x86 平台由於因應各種應用需求的提出, 而陸陸續續加入了不同的指令集, 此外也可能因為針對市場做等級區隔, 支援的數量與種類也不等. 在 Linux 平台上這些 CPU 資訊可以透過...