2016-07-05 55 views
1

我想在OpenCV中制作SIFT算法的并行版本。OpenMP:并行for不会做任何事

特别地,在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行代码已经是一个很好的结果。

无论如何,循环内的大部分计算是由​​执行的,但无论如何它平均需要100us。所以大部分的计算时间是由真的很高​​被称为(数千次)的次数。因此,将所有这些100us结果填入若干ms

无论如何,我正试图优化​​的表现。尤其是代码为平均60us devide两者之间for和下面的一个观点:

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

  • 获得某种不一致的增益给出了一个新的parallel for
  • <52ms开销给出

    1. >52ms结果,因为您可以看到共享矢量hist被同时更新。没有发生这种情况:结果仍然正确,并且没有使用atomiccritical

    我是OpenMP的新手,但是从我看到的就像是这样的内在parllel for就好像被忽略了一样。为什么会发生?

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

    UPDATE: 我试图删除第一parallel for,留下一个在calcSIFTDescriptor和它发生在我所期待的:不一致已经观察到,由于缺乏任何线程安全机制。在更新之前引入#pragma omp critical(dataupdate)hist再次给出一致性但现在的表现是可怕的:245ms平均。

    我认为这是因为parallel forcalcSIFTDescriptor给出的开销,这不值得并行化30us

    但问题依然存在:为什么第一个版本(有两个parallel for)没有产生任何变化(无论是在性能和​​一致性)?

  • +0

    因此,你正在并行地执行外部循环,使每个核心都得到自己的'calcSIFTDescriptor'来执行,然后你想把内部循环分配到...什么?所有内核都已经很忙。 –

    +0

    感谢您的评论,您在我的** UPDATE **之前仅发布了一秒钟(请查看)。无论如何,正如我所说我是OpenMP的新手,但内在的“并行”是一种常见的做法。实际上'#pragma omp parallel for collapse(2)'正好引入了两个内部'for'指令(如[在此解释](http://bisqwit.iki.fi/story/howto/openmp/#Abstract)) “崩溃条款”部分) – justHelloWorld

    +0

    (@Revolver_Ocelot顺便说一句我必须这样说:Snake?SNAAAAAAAKE) – justHelloWorld

    回答

    1

    我找到了答案由我自己:第二(嵌套)parallel for不作此处描述的原因,任何效果:可以相互嵌套

    OpenMP并行区域。如果嵌套 并行性被禁用,则线程 在并行区域内遇到并行构造的新团队仅包含遇到线程的 。如果启用嵌套并行,则 ,则新团队可能包含多个线程。

    因此,由于第一个parallel for需要所有可能的线程,第二个线程与团队遇到线程本身相同。所以没有任何反应

    为我自己欢呼!

    +0

    默认情况下禁用了嵌套并行操作,并且必须通过将环境中的“OMP_NESTED”设置为true或通过调用'omp_set_nested(1);'来显式启用嵌套并行操作。 –