OpenMP:并行不做任何事情

OpenMP: parallel for doesn't do anything

我正在尝试在 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)。我预计会出现以下一个或多个结果:

  1. >52ms的开销给了一个新的parallel for
  2. <52ms给定的增益parallel for
  3. 结果中存在某种不一致,因为如您所见,共享向量 hist 是同时更新的。这一切都没有发生:结果仍然正确,并且没有使用 atomiccritical

我是一个 OpenMP 新手,但从我的角度来看,这个内部 parllel for 就像被忽略了一样。为什么会这样?

注意:所有报告的时间都是相同输入 10.000 次的平均时间。

更新: 我试图删除第一个 parallel for,将第一个留在 calcSIFTDescriptor 中,结果如我所料:由于缺少任何线程,已观察到 不一致 -安全机制。在更新 hist 之前引入 #pragma omp critical(dataupdate) 再次提供了一致性 但现在的表现很糟糕: 245ms 平均。

我认为这是因为 calcSIFTDescriptorparallel for 给出的开销,不值得并行化 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 占用了所有可能的线程,第二个将遇到的线程本身作为团队。所以什么也没有发生。

为自己干杯!