【发布时间】: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