【发布时间】:2011-04-03 10:23:28
【问题描述】:
我有一个应用程序,它基本上应该并行计算数学表达式的反向波兰符号。我的问题是我在使用 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 计算的成员函数。我没有在这个函数中使用任何锁、互斥锁和障碍。
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
【问题讨论】:
-
创建/声明线程有一些开销 - 这不是微不足道的。如果您的
EvalRPN函数足够快,那么它可能会在第二个线程有机会启动时结束。 -
msvc 的 printf 包含一个临界区。如果您使用 OutputDebugString 或删除它们会改变行为吗?
-
循环被分成 4 个块,每个线程得到多个循环,而不仅仅是一个循环。线程不是在每个循环轮次中创建的,它们每个都执行 for 循环的大部分。我看到线程 1 计算 500 个循环,然后我看到线程 2 计算比线程 3 几百个循环。我永远不会看到两个线程同时工作。 printf 可能有一个锁,但这不会阻止两个 printf 之间的线程切换。手动设置块大小也不起作用。
标签: c++ visual-studio-2008 openmp