有两个非常常见的原因导致 OpenMP 代码未能表现出比串行代码更好的性能:
所做的工作不足以超过并行计算的开销。想象一下,建立一个线程团队,为他们分配工作,从他们那里收集结果是有成本的。除非此成本小于通过并行计算节省的时间,否则即使正确,OpenMP 代码也不会显示任何加速,并且可能会显示相反的结果。您还没有向我们展示数字,因此请自行计算。
程序员对并行程序施加串行操作,可能是通过将数据访问包装在内存栅栏内,可能是通过访问本质上是串行的平台资源。我怀疑(但我对 C 的了解很糟糕)你的写作cout
可能会无意中序列化你计算的那部分。
当然,您可能会同时遇到这两个问题,即序列化过多而工作量不足,从而导致性能令人失望。
进一步阅读英特尔网站上的此页面很有用,而且不仅适用于初学者。
不过,我认为您的代码存在比并行性能差更严重的问题。OpenMP 版本是否生成正确的sum
?由于您没有做出sum
所有线程共享的特定规定,因此它们将竞相访问它。default(none)
在学习 OpenMP 时,将子句附加到并行区域并负责定义每个区域中每个变量的共享/私有状态是一个非常好的主意。然后,一旦您精通 OpenMP,您就会知道为什么继续使用该default(none)
子句是有意义的。
即使您回答“是”,代码确实会产生正确的结果,数据竞争存在并且您的程序不可信。数据竞赛很有趣,它们不会出现在您运行的所有测试中,一旦您将代码部署到生产环境中,砰!和鸡蛋在你的脸上。
但是,您似乎在自己动手reduction
,OpenMP 提供了执行此操作的工具。调查reduction
OpenMP 引用中的子句。如果我正确阅读了您的代码,并考虑到上面的建议,您可以将循环重写为
#pragma omp parallel for default(none) shared(sum, arraySize, testArray) private(i) reduction(+:sum)
for (i = 0; i < arraySize; ++i)
{
sum += testArray[i];
}
简而言之,使用 reduce 子句告诉 OpenMP 解决从跨线程分布的工作中求和单个值的问题,避免竞争条件等。
由于 OpenMP 默认情况下将循环迭代变量设为私有,因此您可以private(i)
在指令中省略该子句而不会带来太大风险。更好的方法可能是在 for 语句中声明它:
#pragma omp parallel for default(none) shared(sum, arraySize, testArray) reduction(+:sum)
for (int i = 0; i < arraySize; ++i)
在并行区域内声明的变量(撇开一些特殊情况)始终是私有的。