OpenCV 中的 Mat 矩阵和 SSE 的 16 字节对齐

Posted

技术标签:

【中文标题】OpenCV 中的 Mat 矩阵和 SSE 的 16 字节对齐【英文标题】:Mat matrix in OpenCV and 16-byte alignment for SSE 【发布时间】:2014-06-06 09:41:40 【问题描述】:

我喜欢测试SSE/SSE2 处理OpenCV's Mat 的增强。由于SSE's 性能增强仅对 16 字节对齐数据很明显,(1) 我需要修改 Mat 矩阵以与SSE 寄存器一起使用吗?我所做的如下,(2)这是正确的方法吗?

 void test(Mat flowxy, Mat flowresult)
    
         __m128 x, y, xsquare, ysquare, ybyx, xRecip , sum, r, theta ;//gen is for general purpose
        float *input = (float*)(flowxy.data);
        for(int i = 0; i  < flowxy.rows; i++)
            
                for(int j = 0; j + SSE_INCREMENT < flowxy.cols; j = j + SSE_INCREMENT)
                

                    x = _mm_set_ps(input[flowxy.step * (j+6) + i ], input[flowxy.step * (j+4) + i ], input[flowxy.step * (j+2) + i ], input[flowxy.step * (j) + i ]);
                    y = _mm_set_ps(input[flowxy.step * (j+7) + i ], input[flowxy.step * (j+5) + i ], input[flowxy.step * (j+3) + i ], input[flowxy.step * (j+1) + i ]);
                    xRecip  = _mm_rcp_ps(x);
                    xsquare = _mm_mul_ps(x, x);
                    ysquare = _mm_mul_ps(y, y);             
                    ybyx = _mm_mul_ps(xRecip , y);
                    sum = _mm_add_ps(xsquare, ysquare);
                    r = _mm_sqrt_ps(sum);
                    theta = taninverse(ybyx);
                


            

    

我按照here的讨论把设置_mm_set_ps的顺序颠倒过来了。

编辑 1:

void CObjectDetection_TrackingDlg::flow_XY_RTHETA(Mat flowxy, vector<Mat> &flowrtheta)

    clock_t start;   
    clock_t finish;  
    start = clock();
    flowrtheta.resize(2);
    if(flowrtheta[0].empty() && flowrtheta[1].empty())
        flowrtheta[0].create(cvSize(flowxy.rows, flowxy.cols), CV_32FC1);
        flowrtheta[1].create(cvSize(flowxy.rows, flowxy.cols), CV_32FC1);
    
    vector<Mat> flowxy_S;
    split(flowxy, flowxy_S);
    printMatGrayDatainfloat(flowxy_S[0]);
    printMatGrayDatainfloat(flowxy_S[1]);
    //check SSE2 available
    bool useSIMD = checkHardwareSupport(CV_CPU_SSE);
    if( useSIMD )
    
        __m128 x, y, xsquare, ysquare, ybyx, xRecip , sum, r, theta ;//gen is for general purpose       
        __declspec(align(16)) struct  int i, j;  sub;
        for(sub.i = 0; sub.i  < flowxy.rows; sub.i++)
        
            const float *input_x = flowxy_S[0].ptr<float>(sub.i);
            const float *input_y = flowxy_S[1].ptr<float>(sub.i);
            float *output_r = flowrtheta[0].ptr<float>(sub.i);
            float *output_t = flowrtheta[1].ptr<float>(sub.i);
            for(sub.j = 0; sub.j + 4 < flowxy.cols; sub.j = sub.j + 4)
            

                x = _mm_loadu_ps(&input_x[sub.j]);
                y = _mm_loadu_ps(&input_y[sub.j]);
                xRecip  = _mm_rcp_ps(x);
                xsquare = _mm_mul_ps(x, x);
                ysquare = _mm_mul_ps(y, y);             
                ybyx = _mm_mul_ps(xRecip , y);
                sum = _mm_add_ps(xsquare, ysquare);
                r = _mm_sqrt_ps(sum);
                theta = taninverse(ybyx);
                _mm_storeu_ps(&output_r[sub.j], r);
                _mm_storeu_ps(&output_t[sub.j], theta);

            


        

    
    else
    
        for(int i = 0; i  < flowxy.rows; i++)
        
            const float *input_x = flowxy_S[0].ptr<float>(i);
            const float *input_y = flowxy_S[1].ptr<float>(i);
            float *output_r = flowrtheta[0].ptr<float>(i);
            float *output_t = flowrtheta[1].ptr<float>(i);
            for(int j = 0; j  < flowxy.cols; j++)
            
                double x_sq = input_x[j] * input_x[j];
                double y_sq = input_y[j] * input_y[j];
                double y_by_x =  input_y[j] / input_x[j];
                output_r[j] = sqrt(x_sq + y_sq);
                output_t[j] = atan(y_by_x);
            


        


    
    flowxy_S[0].release();
    flowxy_S[1].release();
    finish = clock() - start;
    double interval = finish / (double)CLOCKS_PER_SEC;
    //printMatGrayDatainfloat(flowrtheta[0]);
    //printMatGrayDatainfloat(flowrtheta[1]);
    return;

【问题讨论】:

您的代码或多或少没问题,因为您没有进行任何需要 16 字节对齐的加载或存储。但是使用_mm_set_ps 效率非常低 - 您应该使用_mm_loadu_ps 加载连续未对齐的数据,然后使用例如将元素随机排列到所需的顺序中。 _mm_shuffle_ps. 让我探索更多,并会和你在一起。我的 r 和 theta 是我需要写回 2 通道 Mat flowresult 的结果。最好的方法是什么?谢谢 您可以使用_mm_storeu_ps将结果写回内存。 @PaulR 我已经更新了。我比较了 if 和 else 中的两个进程。 else 0.411msec 和 if 中的过程是 0.419msec。为什么 if 中的过程没有快四倍?是不是因为内存不对齐? 编译器可能无论如何都在向量化此代码,因此您可能无法从显式向量化中获得任何收益 - 查看为您的标量分支生成的代码,看看它是否包含 SSE 指令。另请注意,旧 CPU 上未对齐的加载/存储非常昂贵(如果这是例如 Core i7,您应该没问题)。 【参考方案1】:

编译器可能无论如何都在向量化此代码,因此您可能无法从显式向量化中获得任何收益 - 查看为您的标量分支生成的代码并查看它是否包含 SSE 指令。另请注意,旧 CPU 上未对齐的加载/存储非常昂贵(如果这是例如 Core i7,您应该没问题)。

【讨论】:

以上是关于OpenCV 中的 Mat 矩阵和 SSE 的 16 字节对齐的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV中的MAT类矩阵的各种基本运算及示例代码(加减乘点乘点除乘方累加转置等)

opencv中如何将两个类型为Mat的矩阵合为一个矩阵?

OpenCV学习:Mat结构中的数据共享机制

OPenCV中累加一个三通道矩阵中的所有元素:

Mat的单通道作为矩阵Opencv

opencv怎么给mat赋值