优化可分离卷积的SIMD友好性与效率:GCC代码优化问询
2D可分离卷积的SIMD向量化优化问题
原实现代码
void SeparableConvolution2D(const float * restrict mI, float * restrict mO, float * restrict mB, int numRows, int numCols, const float * restrict vKRow, int rowKernelLen, const float * restrict vKCol, int colKernelLen) { // mI (numRows x numCols) - 输入图像 // mO (numRowsO x numColsO) - 输出图像 // mB (numColsO x numRows) - 中间缓冲区(行滤波结果的转储) // 备注:并行化开销过大,GCC原生SIMD比OpenMP效果更好 int rowKernelRad = rowKernelLen / 2; // 行核半径 int colKernelRad = colKernelLen / 2; // 列核半径 // "Valid"模式下的输出尺寸 int numRowsO = numRows - (colKernelLen - 1); int numColsO = numCols - (rowKernelLen - 1); // 沿行应用1D滤波 for (int r = 0; r < numRows; r++) { #pragma omp simd for (int c = 0; c < numColsO; c++) { float sum = 0.0f; for (int k = 0; k < rowKernelLen; k++) { sum += mI[(r * numCols) + (c - k + rowKernelLen - 1)] * vKRow[k]; } mB[c * numRows + r] = sum; // 存储行滤波结果 } } // 沿列应用1D滤波 // mB中的数据是转置后的,因此依然沿行处理 for (int r = 0; r < numColsO; r++) { #pragma omp simd for (int c = 0; c < numRowsO; c++) { float sum = 0.0f; for (int k = 0; k < colKernelLen; k++) { sum += mB[(r * numRows) + (c - k + colKernelLen - 1)] * vKCol[k]; } mO[c * numColsO + r] = sum; // 存储最终结果 } } }
优化需求
我希望代码具备SIMD高效性和可移植性,因此不想使用任何SIMD内置函数,也不添加额外依赖。尝试过OpenMP SIMD但效果不佳,目前编译器仅对sum变量的计算做了向量化,目标是实现外层c循环的向量化。请问针对GCC编译器,有没有可行的方法?比如编译提示、OpenMP SIMD技巧。
可选变换思路
我考虑过显式展开循环,将行滤波部分修改为以下代码,目的是提示编译器将核值vKRow[k]的乘法广播到多个图像像素,这种方法是否可行?
for (int r = 0; r < numRows; r++) { float *vI = mI + (r * numCols); // 指向当前行的指针 // #pragma omp simd #pragma GCC ivdep #pragma vector always for (int c = 0; c + 4 < numColsO; c += 4) { // 从输入图像加载4个像素 float *vI0 = &vI[c + rowKernelLen - 1]; float *vI1 = &vI[c + rowKernelLen]; float *vI2 = &vI[c + rowKernelLen + 1]; float *vI3 = &vI[c + rowKernelLen + 2]; // 对每个像素应用核 float sum0 = 0.0f; float sum1 = 0.0f; float sum2 = 0.0f; float sum3 = 0.0f; for (int k = 0; k < rowKernelLen; k++) { sum0 += vI0[-k] * vKRow[k]; sum1 += vI1[-k] * vKRow[k]; sum2 += vI2[-k] * vKRow[k]; sum3 += vI3[-k] * vKRow[k]; } mB[c * numRows + r] = sum0; // 存储行结果 mB[(c + 1) * numRows + r] = sum1; // 存储行结果 mB[(c + 2) * numRows + r] = sum2; // 存储行结果 mB[(c + 3) * numRows + r] = sum3; // 存储行结果 } }
更新说明
我标记了@JérômeRichard的答案,实际使用的是自己编写的上述展开循环代码,不过该答案让我了解到可以借助ISPC工具实现正确的SIMD强制优化。
内容的提问来源于stack exchange,提问作者Royi
相关产品推荐
相关产品推荐

