我正在使用下面编写的简单代码进行一些测试。问题是在四核机器中,我只得到了 75% 的负载。第四个核心处于空闲状态,无所事事。代码有一个 omp 并行,然后是一个 omp 单,线程在其中生成一个任务。该任务会生成许多孙任务。该任务将在屏障中等待,直到其所有子项(单个区域中线程的孙子项)完成,并且执行单个区域的线程在另一个屏障上等待,直到其直接后代任务完成。问题是执行单个区域的线程不执行任何孙任务。鉴于我正在使用的块大小,我正在创建数千个任务,因此这不是可用并行性的问题。
我是否误解了 OpenMP 任务?它与仅等待直接孩子的任务等待有关吗?如果是这样,我怎样才能让空闲线程执行可用的工作?想象一下,我想像在 OpenMP 4.0 中一样创建具有依赖关系的任务,那么我将无法利用具有依赖关系的所有可用线程。需要父任务中的障碍,因为我不想在所有子任务完成之前释放依赖它的下一个任务。
#include <iostream>
#include <cstdlib>
#include <omp.h>
using namespace std;
#define VECSIZE 200000000
float* A;
float* B;
float* C;
void LoopDo(int start, int end) {
for (int i = start; i < end; i++)
{
C[i] += A[i]*B[i];
A[i] *= (B[i]+C[i]);
B[i] = C[i] + A[i];
C[i] *= (A[i]*C[i]);
C[i] += A[i]*B[i];
C[i] += A[i]*B[i];
....
}
void StartTasks(int bsize)
{
int nthreads = omp_get_num_threads();
cout << "bsize is: " << bsize << endl;
cout << "nthreads is: " << nthreads << endl;
#pragma omp task default(shared)
{
for (int i =0; i <VECSIZE; i+=bsize)
{
#pragma omp task default(shared) firstprivate(i,bsize)
LoopDo(i,i+bsize);
if (i + bsize >= VECSIZE) bsize = VECSIZE - i;
}
cerr << "Task creation ended" << cerr;
#pragma omp taskwait
}
#pragma omp taskwait
}
int main(int argc, char** argv)
{
A = (float*)malloc(VECSIZE*sizeof(float));
B = (float*)malloc(VECSIZE*sizeof(float));
C = (float*)malloc(VECSIZE*sizeof(float));
int bsize = atoi(argv[1]);
for (int i = 0; i < VECSIZE; i++)
{
A[i] = i; B[i] = i; C[i] = i;
}
#pragma omp parallel
{
#pragma omp single
{
StartTasks(bsize);
}
}
free(A);
free(B);
free(C);
return 0;
}
编辑:
我使用 ICC 15.0 进行了测试,它使用了我机器的所有内核。尽管 ICC 分叉了 5 个线程,而不是 GCC 那样的 4 个。第五个 ICC 线程保持空闲。
编辑 2: 以下更改,添加与线程一样多的顶级任务的循环,使所有线程都获得任务。如果顶级任务 < ntthreads,那么在某些执行中,主线程将不会执行任何任务,并且会像以前一样保持空闲状态。ICC 一如既往地会生成一个允许使用所有内核的二进制文件。
for (int i = 0; i<nthreads;i++)
{
#pragma omp task default(shared)
{
for (int i =0; i <VECSIZE; i+=bsize)
{
#pragma omp task default(shared) firstprivate(i,bsize)
LoopDo(i,i+bsize);
if (i + bsize >= VECSIZE) bsize = VECSIZE - i;
}
cerr << "Task creation ended" << cerr;
#pragma omp taskwait
}
}
#pragma omp taskwait