![](/img/trans.png)
[英]Fast way using healpy to get convolution of a healpy map and a 2D Gaussian
[英]Fast 2D Convolution in C
我正在嘗試用Python實現卷積神經網絡。 最初,我使用scipy.signal的convolve2d函數來進行卷積,但它有很多開銷,在C中實現我自己的算法並從python中調用它會更快,因為我知道我的輸入是什么樣的。
我實現了2個功能:
由於我需要降低維數,因此這兩個函數都沒有填充。
// a - 2D matrix (as a 1D array), w - kernel
double* conv2(double* a, double* w, double* result)
{
register double acc;
register int i;
register int j;
register int k1, k2;
register int l1, l2;
register int t1, t2;
for(i = 0; i < RESULT_DIM; i++)
{
t1 = i * RESULT_DIM; // loop invariants
for(j = 0; j < RESULT_DIM; j++)
{
acc = 0.0;
for(k1 = FILTER_DIM - 1, k2 = 0; k1 >= 0; k1--, k2++)
{
t2 = k1 * FILTER_DIM; // loop invariants
for(l1 = FILTER_DIM - 1, l2 = 0; l1 >= 0; l1--, l2++)
{
acc += w[t2 + l1] * a[(i + k2) * IMG_DIM + (j + l2)];
}
}
result[t1 + j] = acc;
}
}
return result;
}
// a - 2D matrix, w1, w2 - the separated 1D kernels
double* conv2sep(double* a, double* w1, double* w2, double* result)
{
register double acc;
register int i;
register int j;
register int k1, k2;
register int t;
double* tmp = (double*)malloc(IMG_DIM * RESULT_DIM * sizeof(double));
for(i = 0; i < RESULT_DIM; i++) // convolve with w1
{
t = i * RESULT_DIM;
for(j = 0; j < IMG_DIM; j++)
{
acc = 0.0;
for(k1 = FILTER_DIM - 1, k2 = 0; k1 >= 0; k1--, k2++)
{
acc += w1[k1] * a[k2 * IMG_DIM + t + j];
}
tmp[t + j] = acc;
}
}
for(i = 0; i < RESULT_DIM; i++) // convolve with w2
{
t = i * RESULT_DIM;
for(j = 0; j < RESULT_DIM; j++)
{
acc = 0.0;
for(k1 = FILTER_DIM - 1, k2 = 0; k1 >= 0; k1--, k2++)
{
acc += w2[k1] * tmp[t + (j + k2)];
}
result[t + j] = acc;
}
}
free(tmp);
return result;
}
使用gcc的-O3標志進行編譯並在2.7GHz Intel i7上進行測試,使用4000x4000矩陣和5x5內核,我分別得到(平均5):
271.21900 ms
127.32000 ms
與scipy.signal的convolve2d相比,這仍然是一個相當大的改進,對於相同的操作需要大約2秒鍾,但我需要更多速度,因為我將調用此函數數千次。 將數據類型更改為浮動目前不是一種選擇,即使它會導致相當大的加速。
有沒有辦法可以進一步優化這些算法? 我可以應用任何緩存技巧或例程來加快速度嗎?
任何建議,將不勝感激。
如果您只在x86上運行,請考慮使用SSE或AVX SIMD優化。 對於double
數據,吞吐量的改善將是適度的,但如果你可以切換到float
那么你可以使用SSE或使用AVX獲得8倍的改進。 關於StackOverflow上的這個主題,有很多問題和答案,您可以從中獲得有關實現的一些想法。 另外,還有許多可用的庫,包括高性能2D卷積(過濾)例程,這些通常利用SIMD來提高性能,例如Intel的IPP(商業)或OpenCV(免費)。
另一種可能性是利用多個核心 - 將圖像分割成塊並在其自己的線程中運行每個塊。 例如,如果您有一個4核CPU,則將圖像分成4個塊。 (見pthreads )。
如果您真的想要完全優化此操作,您當然可以結合上述兩個想法。
您可以應用於當前代碼以及任何未來實現(例如SIMD)的一些小優化:
如果您的內核是對稱的(或奇數對稱的),那么您可以通過添加(減去)對稱輸入值並執行一次乘法而不是兩次來減少操作次數
對於可分離的情況,不要分配一個完整的幀臨時緩沖區,而是考慮使用“條帶挖掘”方法 - 分配一個較小的緩沖區,這是一個全寬,但行數相對較少,然后以“條帶”處理你的圖像,交替應用水平內核和垂直內核。 這樣做的好處是,您具有更多緩存友好的訪問模式和更小的內存占用。
關於編碼風格的一些評論:
register
關鍵字多年來一直是多余的,如果你試圖使用它,現代編譯器會發出警告 - 通過拋棄它來節省一些噪音(和一些打字)
在C中轉換malloc
的結果是不受歡迎的 - 這是多余的並且有潛在危險 。
使任何輸入參數const
(即只讀)並對任何永遠不會混淆的參數使用restrict
(例如a
和result
) - 這不僅有助於避免編程錯誤(至少在const
的情況下),而是在某些情況下,它可以幫助編譯器生成更好的優化代碼(特別是在潛在的別名指針的情況下)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.