我有一个特定的问题:我有一个2D矩阵,其中包含一些值,这些值对应于应该并行执行的任务。我想显式地将工作负载分配给特定的线程,其中ID对应于此数组的内容。作为测试,为了理解我应该做什么,我编写了以下简单代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>
int main(void)
{
int N=100; // Number of elements per dimension
int A[N][N], B[N][N]; // Matrices to work with
int i,j; // Loop indices
int nt; // Thread ID
int NT=4; //Number of threads
//Filling the arrays
for(i=0;i<N;i++)
for(j=0;j<N;j++)
A[i][j]=(i+j)%NT; //This is the value that corresponds to the task we want to assign the job to
//Perform some operations
#pragma omp parallel private(i,nt) num_threads(NT)
{
for(i=0;i<N;i++)
for(j=0;j<N;j++)
{
nt = omp_get_thread_num();
if(A[i][j]==nt)
{
B[i][j]=nt;
//Some dummy operations to increase the load per thread
for (int k = 0; k < 100000ULL; ++k);
}
}
}
printf("nnnn");
for(i=0;i<N;i++)
for(j=0;j<N;j++)
printf("A[%d][%d]=%d goes to thread %dn",i,j,A[i][j],B[i][j]);
return 1;
}
然而,在执行此操作时,我不确定这是否满足了最初的目的。所以问题是,如何显式地将工作负载分配给特定的线程?例如,矩阵的第一个元素由线程0执行,第二个由线程2执行,第三个由线程0执行,以此类推。根据我的理解,双for循环应该在并行区域之外执行,而不是在每个线程中执行,以减少总体工作量。
提前谢谢你
- 问题是你的代码变量
j
应该是私有的,所以使用private(i,j,nt)
子句。最好将变量定义在最小所需范围内。在并行区域内定义的变量将是私有的。 - 另一个评论是,你必须检查你是否得到了所有你请求的线程。
if (NT != omp_get_num_threads()){
// You have less threads than requested,
// handle this case here
}
const int nt = omp_get_thread_num();
应在for
循环之前。调用这个函数一次就足够了。如果你的目标是以循环的方式为线程分配不同的索引(矩阵):
thread | indices assigned to the thread
-----------------------------------------
thread 0 | 0,0 0,4 0,8 etc...
thread 1 | 0,1 0,5 0,9 etc...
thread 2 | 0,2 0,6 0,10 etc...
thread 3 | 0,3 0,7 0,11 etc...
在这种情况下,你不需要辅助数组,你可以这样使用:
#pragma omp parallel
{
const unsigned int NT = omp_get_num_threads(); // number of total threads
const unsigned int nt = omp_get_thread_num(); // current thread
for(unsigned int index = nt;index < N*N; index += NT)
{
unsigned int i = index / N;
unsigned int j = index % N;
// Do whatever you have to do with indices i and j
printf("Indices i=%d, j=%d goes to thread %dn",i,j,nt);
}
}
- Performance:没有关于你真正的程序做什么的更多细节,我不能告诉你任何关于性能问题的事情。例如,由于错误的共享,像
B[i][j]=...;
这样写内存是一个真正的噩梦。
UPDATE:正如@Jim Cownie指出的,最简单的解决方案是使用schedule(static,1)
子句,因为它以轮循的方式在线程之间分发工作。如果您将它与collapse(2)
子句结合使用,您只需在串行代码中添加一行:
#pragma omp parallel for collapse(2) schedule(static,1) num_threads(NT)
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
{
// Do whatever you have to do with indices i and j
printf("Indices i=%d, j=%d goes to thread %dn",i,j,omp_get_thread_num());
}
然而,这种解决方案有一个严重的缺点:在这种情况下,开销要大得多。