You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化可分离卷积的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 14:13:13