【发布时间】:2016-07-05 10:34:35
【问题描述】:
我正在尝试在 OpenCV 中创建并行版本的 SIFT 算法。
尤其是sift.cpp:
static void calcDescriptors(const std::vector<Mat>& gpyr, const std::vector<KeyPoint>& keypoints,
Mat& descriptors, int nOctaveLayers, int firstOctave )
{
...
#pragma omp parallel for
for( size_t i = 0; i < keypoints.size(); i++ )
{
...
calcSIFTDescriptor(img, ptf, angle, size*0.5f, d, n, descriptors.ptr<float>((int)i));
...
}
已经在四核机器上提供了从 84ms 到 52ms 的加速。规模不大,但添加1行代码已经是不错的结果了。
无论如何,循环内的大部分计算都是由calcSIFTDescriptor() 执行的,但无论如何它平均需要100us。所以大部分计算时间是由calcSIFTDescriptor() 被调用的非常高 次(数千次)给出的。因此,将所有这些100us 累加起来会产生几个ms。
无论如何,我正在尝试优化calcSIFTDescriptor() 的性能。特别是代码介于两个for 和以下平均60us 之间:
for( k = 0; k < len; k++ )
{
float rbin = RBin[k], cbin = CBin[k];
float obin = (Ori[k] - ori)*bins_per_rad;
float mag = Mag[k]*W[k];
int r0 = cvFloor( rbin );
int c0 = cvFloor( cbin );
int o0 = cvFloor( obin );
rbin -= r0;
cbin -= c0;
obin -= o0;
if( o0 < 0 )
o0 += n;
if( o0 >= n )
o0 -= n;
// histogram update using tri-linear interpolation
float v_r1 = mag*rbin, v_r0 = mag - v_r1;
float v_rc11 = v_r1*cbin, v_rc10 = v_r1 - v_rc11;
float v_rc01 = v_r0*cbin, v_rc00 = v_r0 - v_rc01;
float v_rco111 = v_rc11*obin, v_rco110 = v_rc11 - v_rco111;
float v_rco101 = v_rc10*obin, v_rco100 = v_rc10 - v_rco101;
float v_rco011 = v_rc01*obin, v_rco010 = v_rc01 - v_rco011;
float v_rco001 = v_rc00*obin, v_rco000 = v_rc00 - v_rco001;
int idx = ((r0+1)*(d+2) + c0+1)*(n+2) + o0;
hist[idx] += v_rco000;
hist[idx+1] += v_rco001;
hist[idx+(n+2)] += v_rco010;
hist[idx+(n+3)] += v_rco011;
hist[idx+(d+2)*(n+2)] += v_rco100;
hist[idx+(d+2)*(n+2)+1] += v_rco101;
hist[idx+(d+3)*(n+2)] += v_rco110;
hist[idx+(d+3)*(n+2)+1] += v_rco111;
}
所以我尝试在它之前添加#pragma omp parallel for private(k),然后奇怪的事情发生了:什么都没有发生!!!
引入此parallel for 使代码计算平均为53ms(相对于之前的52ms)。我会期待以下一个或多个结果:
- 采用新的
parallel for的开销提供的>52ms - 以
<52ms给出的增益由parallel for获得 - 结果中存在某种不一致,因为您可以看到共享向量
hist是同时更新的。这一切都没有发生:结果仍然正确,并且没有使用atomic或critical。
我是 OpenMP 新手,但从我看来,这种内部 parllel for 就像被忽略了。为什么会这样?
注意:所有报告的时间都是相同输入 10.000 次的平均时间。
更新:
我试图删除第一个 parallel for,将一个留在 calcSIFTDescriptor 中,结果正如我所料:由于缺乏任何线程安全机制,已观察到 inconsistency。在更新hist 之前引入#pragma omp critical(dataupdate) 再次提供了一致性但现在性能很糟糕:平均而言245ms。
我认为这是因为parallel for 在calcSIFTDescriptor 中给出的开销,不值得并行化30us。
但问题仍然存在:为什么第一个版本(有两个 parallel for)没有产生任何变化(在性能和一致性方面)?
【问题讨论】:
-
因此,您正在并行执行外循环,使每个内核都有自己的
calcSIFTDescriptor执行,然后您想将内循环分配给...什么?所有核心都已经很忙了。 -
感谢您的评论,您在我的 UPDATE 前一秒发布了它(检查一下)。无论如何,正如我所说,我是 OpenMP 新手,但内部
parallel for是一种常见做法。事实上,`#pragma omp parallel for collapse(2)` 是针对两个内部for指令引入的(如“折叠子句”部分下的 here 所述) -
(@Revolver_Ocelot BTW 我不得不说:蛇?SNAAAAAAAKE)
-
好的,我自己找到了你的答案,并将其发布为答案。
标签: c++ opencv parallel-processing openmp