0

我现在正在研究 OpenMP,我有一个问题。以下代码和没有并行部分的相同代码的工作时间在统计上是相等的,尽管所有线程都在访问该函数。我试图查看互联网上的一些指南,但没有帮助。所以问题是,这个平行部分有什么问题?

int sumArrayParallel( )
{

    int i = 0;
    int sum = 0;
    #pragma omp parallel for
    for (i = 0; i < arraySize; ++i)
    {
        cout << omp_get_thread_num() << " ";
        sum += testArray[i];
    }
    return sum;
}
4

1 回答 1

4

有两个非常常见的原因导致 OpenMP 代码未能表现出比串行代码更好的性能:

  1. 所做的工作不足以超过并行计算的开销。想象一下,建立一个线程团队,为他们分配工作,从他们那里收集结果是有成本的。除非此成本小于通过并行计算节省的时间,否则即使正确,OpenMP 代码也不会显示任何加速,并且可能会显示相反的结果。您还没有向我们展示数字,因此请自行计算。

  2. 程序员对并行程序施加串行操作,可能是通过将数据访问包装在内存栅栏内,可能是通过访问本质上是串行的平台资源。我怀疑(但我对 C 的了解很糟糕)你的写作cout可能会无意中序列化你计算的那部分。

当然,您可能会同时遇到这两个问题,即序列化过多而工作量不足,从而导致性能令人失望。

进一步阅读英特尔网站上的此页面很有用,而且不仅适用于初学者。

不过,我认为您的代码存在比并行性能差更严重的问题。OpenMP 版本是否生成正确的sum?由于您没有做出sum所有线程共享的特定规定,因此它们将竞相访问它。default(none)在学习 OpenMP 时,将子句附加到并行区域并负责定义每个区域中每个变量的共享/私有状态是一个非常好的主意。然后,一旦您精通 OpenMP,您就会知道为什么继续使用该default(none)子句是有意义的。

即使您回答“是”,代码确实会产生正确的结果,数据竞争存在并且您的程序不可信。数据竞赛很有趣,它们不会出现在您运行的所有测试中,一旦您将代码部署到生产环境中,砰!和鸡蛋在你的脸上。

但是,您似乎在自己动手reduction,OpenMP 提供了执行此操作的工具。调查reductionOpenMP 引用中的子句。如果我正确阅读了您的代码,并考虑到上面的建议,您可以将循环重写为

#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)

在并行区域内声明的变量(撇开一些特殊情况)始终是私有的。

于 2013-08-06T08:10:11.370 回答