高性能计算-openmp编程-(探究 for/collapse)(11)

1. 目标:探究嵌套循环 for 和 collapse 编程

2. 内容

(1). for 并行区默认对最近外层的循环控制变量私有,并对其划分并行,不必指明 private,内层循环体入口的循环控制变量声明及或定义[ for (int i=0;)]默认私有;如果在并行区外声明或定义的内层循环控制变量默认 shared,应显式 pravate该变量,否则可能导致线程数据竞争,结果错误。如下代码,应注意:

a. 使用collapse(3) ,C[r][c]的计算数据不同数据在不同线程之间,会造成数据竞争,计算错误;如果使用临界区,速度过慢。

// 并行计算矩阵相乘
void gemm_2(int* A,int* B,long* C,int N,int NT)
{
    struct timeval start,end;
    float time;
    //串行计算代码
    gettimeofday(&start,NULL);  //开始时间
    omp_set_num_threads(NT);  
    // 使用collapse(3) C[r][c] 的计算数据在不同进程之间,会造成数据竞争,需要原子操作,造成速度过慢。   
    // 使用collapse(3) for 默认循环指标变量私有,如果在循环外声明定义k,需要private(k)
    #pragma omp parallel for collapse(2) schedule(guided) proc_bind(close)
    for(int r=0;r<N;r++)//A 行遍历
    {
        for(int c=0;c<N;c++)//B 列遍历
        {
            #if 0   //临界区+collapse(3)
            for(int k=0;k<N;k++)//A B K方向遍历
            {
                #pragma omp critical
                    C[r*N+c] += A[r*N+k] * B[k*N+c];
            }
            #else
            long sum=0; //使用局部变量减少传入参数引用
            for(int k=0;k<N;k++)//A B K方向遍历
                sum += A[r*N+k] * B[k*N+c];
            C[r*N+c] = sum;
            #endif
        }
    }
    gettimeofday(&end,NULL);
    time = end.tv_sec-start.tv_sec+(end.tv_usec-start.tv_usec)/1e6;
    printf("func %s N %d threads_num %d sum %ld useSec %f\n",__func__,N,NT,sum(C,N),time);
}

(2). collapse(n),最外n层的循环控制变量默认私有。在并行区内声明的变量默认 pravite。如下是一个 collapse(2) + collapse(2) + reduction 的示例:

a. 外两层循环步进为4,影响每个循环控制变量在每个线程中的起始值。内两层步进量内部循环。

//矩阵一维行列分块 A块 4*N,B块 N*4,并行,K在内层,collapse(2+2) + reduction
void gemm_8(int* A,int* B,long* C,int N,int NT)
{
    struct timeval start,end;
    float time;
    int r,c,k;
    //串行计算代码
    gettimeofday(&start,NULL);  //开始时间
    omp_set_num_threads(NT);
    #pragma omp parallel for collapse(2) private(r,c) schedule(guided) proc_bind(close)
    for(r=0;r<N;r+=4)//A 行遍历
    {
        for(c=0;c<N;c+=4)//B 列遍历
        {
            #pragma omp parallel for collapse(2) schedule(guided) proc_bind(close)
            for(int nr=0;nr<4;nr++) //A块内行遍历
            {
                for(int nc=0;nc<4;nc++) //B快内列遍历
                {
                    long sum =0;
                    #pragma omp parallel for reduction(+:sum)  schedule(guided) proc_bind(close)
                    for(int k=0;k<N;k++) //K方向遍历
                        sum += A[(r+nr)*N+k] * B[k*N+c+nc];
                    C[(r+nr)*N+c+nc] = sum;
                }
            }
        }
    }
    gettimeofday(&end,NULL);
    time = end.tv_sec-start.tv_sec+(end.tv_usec-start.tv_usec)/1e6;
    printf("func %s N %d threads_num %d sum %ld useSec %f\n",__func__,N,NT,sum(C,N),time);
    // print_matrix(C,N,N);
}
posted @ 2024-11-16 10:12  安洛8  阅读(198)  评论(0)    收藏  举报