OpenMP线程似乎是串行执行的

时间:2011-04-03 10:23:28

标签: c++ visual-studio-2008 openmp

我有一个应用程序,它应该基本上并行地评估数学表达式的反向抛光符号。我的问题是我在使用OpenMP时没有看到任何性能提升。 (我使用的是VS2008,设置了/ openmp编译器选项。)

我的主循环看起来像这样:

int nMaxThreads = std::min(omp_get_max_threads(), s_MaxNumOpenMPThreads);
int nThreadID;
omp_set_num_threads(nMaxThreads);

#pragma omp parallel for schedule(static) private(nThreadID)
for (i=0; i<nBulkSize; ++i)
{
  nThreadID = omp_get_thread_num();
  printf("Thread %d Idx %d start",nThreadID, i);
  results[i] = EvalRPNInParallel(i, nThreadID);
  printf(" -- %d Idx %d end\n",nThreadID, i);
}

printfs仅用于调试目的,以查看是否发生了任何并行操作(应该在4个线程之间混合它们)。从调试输出我可以看到确实产生了多个线程。每个线程都获得一个循环的某个块,但线程似乎并不执行。线程0正在计算其循环的块,然后线程1计算其块,依此类推。没有任何并行执行。执行时间就像openmp甚至没有活动一样。 EvalRPNInParallel是执行RPN计算的成员函数。我没有在这个函数中使用任何锁,互斥锁omp障碍。

double Foo::EvalRPNInParallel(int nOffset, int nThreadID) const
{
  double *Stack = &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / 4);
  for (const SToken *pTok = m_pRPN;  ; ++pTok)
  {
    switch (pTok->Cmd)
    {
      case  cmADD:  --sidx; Stack[sidx] += Stack[1+sidx]; continue;
      case  cmSUB:  --sidx; Stack[sidx] -= Stack[1+sidx]; continue;
      case  cmMUL:  --sidx; Stack[sidx] *= Stack[1+sidx]; continue;
      case  cmVAR:  Stack[++sidx] = *(pTok->Val.ptr + nOffset);  continue;
      // ...
      // ...
      // ...
      case  cmEND:  return Stack[m_nFinalResultIdx];  
    }
  }
}

奇怪的是,如果我故意用不必要的for循环减慢EvalRPNInParallel,我确实看到了EvalRPNInParallel的并行执行,正如我所料。有没有人知道为什么我没有看到使用OpenMP获得任何好处?

[更新] 我也尝试了以下openMP结构,没有人显示任何并行执行:

int nIterationsPerThread = nBulkSize/nMaxThreads;
#pragma omp parallel for private(nThreadID, j, k) shared(nMaxThreads, nIterationsPerThread) ordered
for (i=0; i<nMaxThreads; ++i)
{
  for (j=0; j<nIterationsPerThread; ++j)
  {
    nThreadID = omp_get_thread_num();
    k = i*nIterationsPerThread + j;
    printf("Thread %d Idx %d start",nThreadID, k);
    results[k] = ParseCmdCodeBulk(k, nThreadID);
    printf(" -- %d Idx %d end\n",nThreadID, k);
  }
}

使用部分:

#pragma omp parallel shared(nBulkSize) private(nThreadID, i)
{
  #pragma omp sections nowait
  {
    #pragma omp section
    for (i=0; i<(nBulkSize/2); ++i)
    {
      nThreadID = omp_get_thread_num();
      printf("Thread %d Idx %d start",nThreadID, i);
      results[i] = ParseCmdCodeBulk(i, nThreadID);
      printf(" -- %d Idx %d end\n",nThreadID, i);
    } // end of section

    #pragma omp section
    for (i=nBulkSize/2; i<nBulkSize; ++i)
    {
      nThreadID = omp_get_thread_num();
      printf("Thread %d Idx %d start",nThreadID, i);
      results[i] = ParseCmdCodeBulk(i, nThreadID);
      printf(" -- %d Idx %d end\n",nThreadID, i);
    } // end of section
  }
} // end of sections

1 个答案:

答案 0 :(得分:3)

Classic Heisenberg,观察线程会影响其行为。 printf()函数很慢,肯定比表达式求值器慢得多。并且必须获取一个锁以防止字符串中的字符与其他线程请求的控制台输出混合。多个线程可以同时进入EvalRPNInParallel函数 的几率不是很好。使用诊断程序无法观察到这一点。

通常的建议适用,只有在 之后优化你的代码三次,以找出瓶颈可能是什么。如果它需要超过几微秒,我会感到惊讶。在这种情况下你无法获胜,启动线程已经花费更长时间。您找到瓶颈所做的相同测量也会告诉您线程是否能让您领先。