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 字节对齐的主要内容,如果未能解决你的问题,请参考以下文章