简单的 OpenMP 并行 for 循环比串行计算慢
simple OpenMP parallel for loop slower than serial computation
我是并行化的新手,希望我没有在浪费任何人的时间。我已经问过一些已经使用过 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
以及以下 header:
#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 中几乎没有任何共享。如果我正在做一些非常愚蠢的事情,或者我是否可以详细说明问题的更多方面,请告诉我。感谢大家。
正如吉尔斯在评论中 post 所说,问题是我用 clock() 测量时间,它把核心的所有动作加起来。
与
chrono::high_resolution_clock::now();
我得到了预期的加速。
对我来说,这个问题已经解决了,但也许我们可以把这个作为一个例子,供未来像我这样的菜鸟参考。如果有人 mod 不这么认为,那么 post 可以被淘汰。
再次感谢您的帮助
我是并行化的新手,希望我没有在浪费任何人的时间。我已经问过一些已经使用过 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 以及以下 header:
#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 中几乎没有任何共享。如果我正在做一些非常愚蠢的事情,或者我是否可以详细说明问题的更多方面,请告诉我。感谢大家。
正如吉尔斯在评论中 post 所说,问题是我用 clock() 测量时间,它把核心的所有动作加起来。 与
chrono::high_resolution_clock::now();
我得到了预期的加速。
对我来说,这个问题已经解决了,但也许我们可以把这个作为一个例子,供未来像我这样的菜鸟参考。如果有人 mod 不这么认为,那么 post 可以被淘汰。 再次感谢您的帮助