我正在使用monte carlo方法来计算pi并使用并行编程和openmp做基本经验
问题是,当我使用1个线程,x次迭代时,总是比n次线程,x次迭代运行得更快。谁能告诉我为什么?
例如,代码运行如下“a.out 1 1000000”,其中1是线程,1000000是迭代
include <omp.h>
include <stdio.h>
include <stdlib.h>
include <iostream>
include <iomanip>
include <math.h>
using namespace std;
int main (int argc, char *argv[]) {
double arrow_area_circle, pi;
float xp, yp;
int i, n;
double pitg= atan(1.0)*4.0; //for pi error
cout << "Number processors: " << omp_get_num_procs() << endl;
//Number of divisions
iterarions=atoi(argv[2]);
arrow_area_circle = 0.0;
#pragma omp parallel num_threads(atoi(argv[1]))
{
srandom(omp_get_thread_num());
#pragma omp for private(xp, yp) reduction(+:arrow_area_circle) //*,/,-,+
for (i = 0; i < iterarions; i++) {
xp=rand()/(float)RAND_MAX;
yp=rand()/(float)RAND_MAX;
if(pow(xp,2.0)+pow(yp,2.0)<=1) arrow_area_circle++;
}
}
pi = 4*arrow_area_circle / iterarions;
cout << setprecision(18) << "PI = " << pi << endl << endl;
cout << setprecision(18) << "Erro = " << pitg-pi << endl << endl;
return 0;
}
答案 0 :(得分:10)
如果你在更多线程中完成工作,那么像这样的CPU密集型任务会比系统中的CPU更慢。如果您在单个CPU系统上运行它,您肯定会看到有多个线程的减速。这是因为操作系统必须在各种线程之间切换 - 这是纯粹的开销。理想情况下,您应该像这样的任务具有相同数量的线程。
另一个问题是在线程之间共享arrow_area_circle。如果在每个核心上运行了一个线程,则递增arrow_area_circle将使其他核心的高速缓存中的副本无效,从而导致它们必须重新获取。 arrow_area_circle ++应该花费几个周期才需要几十个或几百个周期。尝试为每个线程创建一个arrow_area_circle并在最后组合它们。
编辑:Joe Duffy刚刚发布了blog entry关于在线程之间共享数据的成本。
答案 1 :(得分:7)
看起来您正在使用某种自动并行化编译器。我假设您的系统中有超过1个核心/ CPU(因为这太明显了 - 并且Pentium 4上的超线程不会被视为拥有两个内核,无论英特尔的市场营销会让您相信什么。)我看到有两个问题。第一个是微不足道的,可能不是你的问题:
如果变量arrow_area_circle在您的进程之间共享,那么执行arrow_area_circle ++的行为将导致使用互锁指令以原子声音的方式同步该值。你应该增加一个“私有”变量,然后在结尾处将该值仅添加到公共arrow_area_circle变量一次,而不是在内循环中增加arrow_area_circle。
rand()函数,为了正常运行,必须在内部执行临界区。原因是它的内部状态/种子是一个静态共享变量;如果不是这样,那么两个不同的进程有可能以非常高的概率从rand()获得相同的输出,只是因为它们几乎同时调用了rand()。这意味着rand()运行缓慢,特别是当更多线程/进程同时调用它时。与arrow_area_circle变量(只需要原子增量)不同,rand()必须调用真正的临界区,因为它的状态更新更复杂。要解决此问题,您应该获取自己的随机数生成器的源代码,并将其与私有种子或状态一起使用。大多数编译器中标准rand()实现的源代码都可以广泛使用。
我还想指出你使用pow(,)函数执行与x * x相同的操作。后者比前者快300倍。虽然这一点与您提出的问题无关。 :)
答案 2 :(得分:2)
上下文切换。
答案 3 :(得分:2)
rand()是阻塞功能。这意味着它内部有关键部分。
答案 4 :(得分:1)
请注意,在并行设置中使用随机数时必须非常小心。实际上你应该使用像SPRNG
这样的东西无论你做什么,请确保每个帖子都没有使用相同的随机数。