Похоже, что потоки OpenMP выполняются последовательно - PullRequest
2 голосов
/ 03 апреля 2011

У меня есть приложение, которое должно по существу оценивать обратную польскую запись математического выражения параллельно временам мая.Моя проблема в том, что я не вижу какого-либо выигрыша в производительности при использовании 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];  
    }
  }
}

Странно, если я намеренно замедляю EvalRPNInParallel с ненужным циклом for, я действительно вижу параллельное выполнение EvalRPNInParallelкак я и ожидал.У кого-нибудь есть идея, почему я не вижу никакой выгоды от использования OpenMP her?

[update] Я также попробовал следующие конструкции 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 Ответ

3 голосов
/ 03 апреля 2011

Классический Гейзенберг, наблюдение за потоком влияет на его поведение. Функция printf () работает медленно, конечно, намного медленнее, чем ваш оценщик выражений. И должен получить блокировку, чтобы символы в строке не смешивались с выводом консоли, запрошенным другими потоками. Вероятность того, что более чем один поток сможет сделать это с функцией EvalRPNInParallel одновременно , просто не очень хороша. Что вы не можете наблюдать с помощью вашей диагностики между прочим.

И обычный совет применим, только оптимизируйте свой код после , когда вы измерили его три раза, чтобы выяснить, какое может быть узкое место. Я был бы удивлен, если это займет больше пары микросекунд. Вы не можете выиграть в этом случае, запуск потока уже занимает больше времени. То же измерение, которое вы сделаете, чтобы найти узкое место, также скажет вам, продвинет ли вас поток вперед.

...