C++性能优化系列——3D高斯核卷积计算(二)FMA向量化计算一维卷积
高斯核的卷積計算是可分離的,即高斯核的每一個維度可以分開處理。因此,一維卷積計算成為了實現3D高斯卷積的基礎。一維卷積計算的性能直接影響了整個程序的性能。本篇將實現一維卷積功能,同時引出ICC編譯器對多層嵌套循環場景的向量化優化傾向的調查結果。
Base版本實現
Base版本思路是依照滑窗算法,即卷積核依次移動并計算乘加和,更新到目標矩陣中。因為原始矩陣長度為432 * 4 Bytes,卷積核 31 * 4 Bytes,因此外層循環遍歷更新目標矩陣,內層循環計算原始矩陣與卷積核的點積,可以充分利用原始矩陣數據和目標矩陣數據的空間局部性,避免內存的重復加載。
代碼實現,這里為了增加結果的穩定性,重復執行100000次。
其中參數說明
pSrc->原始一維矩陣指針 iLength->原始一維矩陣長度 pKernel->一維卷積核指針 kernelSize->一維卷積核長度 pDst->卷積結果指針執行時間
TestConv1D(base) cost total Time 623 TestConv1D cost Time 0.00623Base版本VTune分析性能瓶頸
前面已經分析過,內存訪問具有良好的空間局部性,從VTune整體分析結果可以很好的驗證這一點。
程序整體CPI為0.436.
主要問題是程序沒做向量化,沒有充分利用向量寄存器。
定位代碼中的熱點位置,可以看到for循環執行的指令比實際做乘加運算的指令數多了一倍多。
查看2300行的反匯編,for循環判斷循環結束的匯編指令cmp執行的太多次數,同時這個指令原則上是無法向量化的。
查看2302行的反匯編,使用了乘加運算fmaddss指令。但是ss標記表示是對浮點數標量運算,一次只計算一個浮點數。
關于指令fmaddss詳細信息如下:
向量化優化
分析代碼邏輯,最內層循環是做點積運算,是可以通過編譯指導語句強制編譯器做向量化。因此嘗試在Base的基礎上,添加向量化優化,同時點積運算是歸約類型的計算,通過添加指導關鍵字reduction,合并計算結果。
代碼實現
void Conv1D_opt(float* pSrc, int iLength, float* pKernel, int kernelSize, float* pDst){for (int itDst = (kernelSize / 2); itDst < iLength - (kernelSize / 2); ++itDst){ #pragma omp simd reduction(+:pDst[itDst])for (int itker = 0, itSrc = itDst - (kernelSize / 2); itker < kernelSize; ++itker, ++itSrc){pDst[itDst] += pKernel[itker] * pSrc[itSrc];}}}執行時間
TestConv1D(opt) cost total Time 116 TestConv1D cost Time 0.00116向量化版本VTune分析
和Base版本對比,向量化程度增加,FPU指標升高。
FPU表示浮點數計算向量化的程度,關于FPU的解釋:
This metric represents how the application code vectorization relates to the floating point computations. A value of 100% means that all floating point instructions are vectorized with the full vector capacity.
可以看到,指令數量上Base版本是優化版本的五倍左右,兩個版本CPI相差不大,這可以解釋速度提高的原因。
總指令數量10億數量級。
對比Base版本,優化版本的的反匯編指令中多出了fmadd231ps vaddps等向量化指令,處理xmm寄存器中數據,因此向量化一次處理4個float單元。
fmaddps指令解釋如下:
這里即便指定了向量化優化的指令集AVX2,最終生成的代碼還是無法使用ymm寄存器,原因是AVX2指令集一次能夠處理8float,最內層31個float數量太少,編譯器認為使用AVX2指令集效率并非最佳。
編譯器配置如下:
循環轉換
卷積的計算方式不止一種,可以使用滑窗法以外的計算方式。將滑窗法的計算過程分解與重組,轉換循環順序。卷積核每個元素依次與原始矩陣有效范圍內元素做乘法運算,并將計算結果加到目標矩陣對應的位置。
代碼實現:
執行時間
TestConv1D(Conv1D_Opt_Cmb) cost total Time 60 TestConv1D cost Time 0.0006VTune分析循環轉換性能
這里FPU已經不再是性能瓶頸。
整體指令數量與CPI都得到了優化。
這里執行指令數量(循環+計算)為7億,對比Conv1D_opt減少了百分之40多,同時CPI降低到0.316
這里fma指令使用了ymm寄存器。最內層循環長度400個float,因此編譯器認為使用了256位的寄存器是更高校的向量化方法。
總結
本文依次通過向量化與循環轉換等優化手段,將一維卷積功能速度提高了10倍,對長度432 float數據,卷積核31 float,最終執行速度0.0006 ms。
同時可以看到,對最內層循環,循環長度越大,向量化程度越高。
總結
以上是生活随笔為你收集整理的C++性能优化系列——3D高斯核卷积计算(二)FMA向量化计算一维卷积的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 查看java进程内存占用情况
- 下一篇: QT入门第一天平台使用规则和代码逻辑学习