我尝试使用OpenMP编写简单的应用程序。不幸的是我有加速问题。
在这个应用程序中,我有一个while循环。该循环的主体由一些指令组成,这些指令应该顺序完成,一个循环。我使用#pragma omp parallel for
来实现循环并行。这个循环没有太多工作,但经常被调用。
我准备两个版本的for循环,并在1,2和4个核心上运行应用程序 版本1(for循环中的4次迭代):22秒,23秒,26秒 版本2(for循环100000次迭代):20秒,10秒,6秒。
正如您所看到的,当for循环没有太多工作时,2和4核心的时间高于1核心。
我想原因是#pragma omp parallel for
在while循环的每次迭代中创建新线程。所以,我想问你 - 是否有可能创建一次线程(在while循环之前),并确保while循环中的某些作业将按顺序完成?
#include <omp.h>
#include <iostream>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int main(int argc, char* argv[])
{
double sum = 0;
while (true)
{
// ...
// some work which should be done sequentially
// ...
#pragma omp parallel for num_threads(atoi(argv[1])) reduction(+:sum)
for(int j=0; j<4; ++j) // version 2: for(int j=0; j<100000; ++j)
{
double x = pow(j, 3.0);
x = sqrt(x);
x = sin(x);
x = cos(x);
x = tan(x);
sum += x;
double y = pow(j, 3.0);
y = sqrt(y);
y = sin(y);
y = cos(y);
y = tan(y);
sum += y;
double z = pow(j, 3.0);
z = sqrt(z);
z = sin(z);
z = cos(z);
z = tan(z);
sum += z;
}
if (sum > 100000000)
{
break;
}
}
return 0;
}
答案 0 :(得分:9)
大多数OpenMP实现在程序启动时创建许多线程,并在程序的持续时间内保留它们。也就是说,大多数实现在执行期间不会动态创建和销毁线程;这样做会在严重的线程管理成本下达到性能。这种线程管理方法与OpenMP的常用用例一致并且适用。
当你增加OpenMP线程的数量时,你看到的减速很可能是在一个迭代次数很少的循环上施加并行开销。 Hristo的答案涵盖了这一点。
答案 1 :(得分:5)
您可以将并行区域移动到while (true)
循环之外,并使用single
指令使代码的串行部分仅在一个线程中执行。这将消除fork / join模型的开销。 OpenMP在迭代次数很少的thight循环中也不是很有用(比如你的版本1)。您基本上测量OpenMP开销,因为循环内部的工作非常快 - 甚至100000次迭代与超越函数在当前一代CPU上花费不到第二次(在2 GHz和每个FP指令大约100个循环,除了添加,它将会需要~100 ms)。
这就是为什么OpenMP提供了if(condition)
子句,可以用来有选择地关闭小循环的并行化:
#omp parallel for ... if(loopcnt > 10000)
for (i = 0; i < loopcnt; i++)
...
建议将schedule(static)
用于常规循环(即每次迭代需要大约相同时间计算的循环)。