【问题标题】:simple OpenMP parallel for loop slower than serial computation简单的 OpenMP 并行 for 循环比串行计算慢
【发布时间】:2015-10-25 00:25:27
【问题描述】:

我是并行化的新手,希望不会浪费任何人的时间。我已经问过几个已经用过 openMP 的朋友了,但是他们帮不了我。所以我猜想我的案例对其他人来说可能也很有趣,至少出于教育目的,我试图尽可能地记录它。这是两个示例,其中一个 100% 来自 Tim Mattson 在 youtube 上的教程,另一个以某种方式简化了,但我猜仍然是一种标准方法。在这两种情况下,计算时间都随着线程数的多少而变化,但对于非常多的迭代,计算时间似乎收敛到相同的数量。这当然是错误的,因为我希望计算时间在少数迭代中是相似的,并且确实针对大量迭代进行了优化。

这里是两个例子,都是用

编译的

g++ -fopenmp main.cpp -o out

线程模型:posix gcc 版本 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04),在 Ubuntu 14.04 上 并带有以下标题:

#include <omp.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <chrono>
#include <iostream>

using namespace std;


#define NUMBER_OF_THREADS 2
static long num_steps = 1000000000;

现在,我正在使用的计算机上的内核数为 8(intel i7),因此我预计 2 到 4 之间的任何线程数都会在计算时间方面带来一些巨大的优势。

示例 1:

int main() { 

omp_set_num_threads(NUMBER_OF_THREADS);
double step = 1.0/(double) num_steps, pi=0.0;

auto begin = chrono::high_resolution_clock::now();

#pragma omp parallel 
{ 
    int i, ID, nthrds;
    double x, sum = 0; 

    ID = omp_get_thread_num();
    nthrds = omp_get_num_threads();

    for (i=ID; i<num_steps; i=i+nthrds) { 
        x = (i+0.5)*step; 
        sum = sum + 4.0/(1.0+x*x); 
    } 

    #pragma omp critical
    pi += step*sum; 
} 

auto end = chrono::high_resolution_clock::now();
cout << chrono::duration_cast<chrono::nanoseconds>(end-begin).count()/1e6 << "ms\n";

return 0; 

}

示例 2:

int main() { 

    omp_set_num_threads(NUMBER_OF_THREADS);
    double pi=0, sum = 0; 
    const double step = 1.0/(double) num_steps;

    auto begin = chrono::high_resolution_clock::now();

    // #pragma omp parallel 
    { 
        #pragma omp parallel for reduction(+:sum)
        for (int i=0; i<num_steps; i++) { 
            double x = (i+0.5)*step; 
            sum += 4.0/(1.0+x*x); 
        } 
    } 

    pi += step*sum; 

    auto end = std::chrono::high_resolution_clock::now();
    cout << chrono::duration_cast<chrono::nanoseconds>(end-begin).count()/1e6 << "ms\n";

    return 0; 

}

现在,我一开始以为示例 2 会因变量的减少而变慢,这会干扰并行化,但在示例 1 中几乎没有共享任何内容。让我知道我是否在做一些非常愚蠢的事情,或者我是否可以指定问题的更多方面。谢谢大家。

【问题讨论】:

  • 使用omp_get_wtime(),而不是clock(),前者返回经过的挂墙时间,而后者返回当前线程及其子线程的CPU时间
  • 嗨 Gilles,感谢您的提示,我确实不知道。显然 chrono 应该对 high_resolution_clock::now(); 做同样的事情欢呼
  • 嗨 Gilles,显然这正是我的问题,现在有了这个测量,它就可以工作了;)非常感谢!
  • @PeterCordes,快速数学是个好建议。由于依赖链,OpenMP 不会在没有它的情况下对循环进行矢量化。我认为 OP 也可以做#pragma omp parallel for simd reduction(+:sum)。在没有快速数学选项的情况下,这也应该对循环进行矢量化。快速数学可以实现许多其他优化,它适用于可能需要也可能不需要的整个翻译单元,而 omp simd 只允许关联数学并且仅适用于它适用的并行块。
  • @PeterCordes,是的,我刚刚检查了组件以确保。除非使用 omp simd 或启用快速数学运算,否则 OpenMP 不会对缩减进行矢量化。另一方面,OpenMP 已经假设关联数学,否则它将无法并行化循环。

标签: c++ performance parallel-processing openmp slowdown


【解决方案1】:

正如 gilles 在 cmets 中所发布的,问题是我正在使用 clock() 来测量时间,它将所有核心的抽动加起来。 与

chrono::high_resolution_clock::now();

我得到了预期的加速。

对我来说,这个问题已经解决了,但也许我们可以把它作为一个例子,供像我这样的未来新手参考。如果某些模组认为否则该帖子可以被删除。 再次感谢您的帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多