我正在尝试在 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
)。我预计会出现以下一种或多种结果:
- 参加
>52ms
由新的开销给出parallel for
- 参加
<52ms
由parallel for
获得的增益给出 - 结果中存在某种不一致,因为如您所见,共享 vector
hist
同时更新。这一切都没有发生:结果仍然正确,没有atomic
或critical
被使用。
我是一个 OpenMP 新手,但从我看来是这样的内部 parllel for
就像被忽略了。为什么会这样?
注意:所有报告的时间都是相同输入 10.000 次的平均时间。
更新:
我试图删除第一个 parallel for
, 留下 calcSIFTDescriptor
中的那个事情的发生正如我所料:由于缺乏任何线程安全机制,已经观察到不一致。介绍#pragma omp critical(dataupdate)
更新前hist
再次保持一致性但现在表现很糟糕: 245ms
平均而言。
我认为这是因为 parallel for
给出的开销在calcSIFTDescriptor
,这不值得并行化 30us
.
但问题仍然存在:为什么第一个版本(有两个 parallel for
)没有产生任何变化(在性能和一致性方面)?
最佳答案
我自己找到了答案:第二个(嵌套的)parallel for
没有产生任何效果,原因如下:
OpenMP parallel regions can be nested inside each other. If nested parallelism is disabled, then the new team created by a thread encountering a parallel construct inside a parallel region consists only of the encountering thread. If nested parallelism is enabled, then the new team may consist of more than one thread.
因此,由于第一个 parallel for
获取所有可能的线程,第二个将遇到的线程本身作为一个团队。所以什么也没有发生。
为自己干杯!
关于c++ - OpenMP:并行不做任何事情,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38201219/