Multithreading 并行执行比顺序执行慢,即使代码是;沉重的;

Multithreading 并行执行比顺序执行慢,即使代码是;沉重的;,multithreading,openmp,Multithreading,Openmp,有一大堆问题会问“为什么我的并行循环比顺序循环慢”,答案是“循环内所需的工作量低;尝试进行更多的迭代”,如 我有一个循环,每次迭代大约需要0.5分钟。我认为这个循环“很重”,与线程相关的任何开销都是微不足道的。结果表明,并行执行速度较慢。我的代码是C语言,我正在使用OpenMP进行并行化。我的代码结构如下: int main() { malloc and calculate group G1 of arrays; #pragma omp parallel { printf("

有一大堆问题会问“为什么我的并行循环比顺序循环慢”,答案是“循环内所需的工作量低;尝试进行更多的迭代”,如

我有一个循环,每次迭代大约需要0.5分钟。我认为这个循环“很重”,与线程相关的任何开销都是微不足道的。结果表明,并行执行速度较慢。我的代码是C语言,我正在使用OpenMP进行并行化。我的代码结构如下:

int main() {
  malloc and calculate group G1 of arrays;
  #pragma omp parallel
  {
    printf("avaible threads: %i\n", omp_get_num_threads());
    malloc group G2 of arrays;
    #pragma omp for
    for (int i = 0; i < N; i++) {
      calculate G2 arrays' elements through numerical integration;
      do some linear algebra with G1 and G2 to assemble the system Ax=b;
      solve Ax = b;
    }
  }
  return 0;
}

实例 下面的代码演示了我在做什么。我试着使它尽可能简单和小。它取决于Netlib的LAPACK和OpenBLAS(使用USE\u OPENMP=1构建)。我的实际程序有数千行

汇编:

  gcc -O2 -m64 -fopenmp test.c -lopenblas -llapack -lpthread -lm
要运行:

  export OMP_NUM_THREADS=3
  ./a.out 30
//test.c
#包括
#包括
#包括
#包括
#包括
#包括
//LAPACK例程
外部空隙
zgesv(int*n,int*nrhs,_复数双*a,int*lda,int*ipiv,
_复数双精度*b,整数*ldb,整数*info);
外部空隙
zgemv(char*trans,int*m,int*n,_复数双*alpha,_复数双*a,
int*lda,_复数双*x,int*incx,_复数双*beta,
_复数双精度*y,整数*incy);
int
主(内部argc,字符**argv)
{
srand(300);
如果(argc!=2){
printf(“必须有1个参数:迭代次数!\n”);
出口(1);
}
常数int nf=atoi(argv[1]);
printf(“循环将有%i次迭代\n”,nf);
时钟开始,结束;
花费的时间加倍;
开始=时钟();
int-ne=2296;
int ne2=ne*ne;
_复杂双*restrict pot=malloc(ne2*sizeof(_复杂双));
对于(int i=0;i0){
printf(“ZL的三角形因子的对角线元素,\n”);
printf(“U(%i,%i)为零,因此ZL是单数;\n”,info,info);
printf(“无法计算解决方案。\n”);
}
zgemv(trans,ne,ne,one,zl,ne,ie,nrhs,zero,ie,nrhs);;
对于(int p=0;p
我有一位教授说

如果实验结果与理论结果不一致,你必须问一个问题:你是如何测量的


正如@Gilles在对问题的评论中指出的那样:使用
omp\u get\u wtime()
而不是
clock()
。这是因为
clock()
返回所有线程的累计CPU时间,而
omp\u get\u wtime()
返回挂钟时间(请参阅)。

您能发布真实代码,将其拆分为一个行为不正常的小内部循环吗?我的第一反应是你们正在破坏三级缓存,但我们还需要更多。@gct我添加了一个示例代码。这不是我的实际代码…在我的机器(Threadripper 1920x)上运行示例代码,1核8核,我看到40秒的运行时间和9.3秒的运行时间,我看不到
perf
有任何明显的缓存问题。你能用
omp_get_wtime()
而不是
clock()
来重试你的实际代码吗?
int-ipiv[n]不在omp部分中,此数组用于求解矩阵。如果所有解算器使用相同的数组,则可能会降低执行速度并改变结果。您能确保并行运行和顺序运行产生相同的结果吗?
  export OMP_NUM_THREADS=3
  ./a.out 30
    // test.c
    #include <complex.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <time.h>
    #include <omp.h>

    // LAPACK routines
    extern void
    zgesv_ (int *n, int *nrhs, _Complex double *a, int *lda, int* ipiv,
            _Complex double *b, int *ldb, int* info);

    extern void
    zgemv_ (char *trans, int *m, int *n, _Complex double *alpha, _Complex double *a,
            int *lda, _Complex double *x, int *incx, _Complex double *beta,
            _Complex double *y, int *incy);

    int
    main (int argc, char **argv)
    {
        srand(300);
        if (argc != 2) {
            printf("must have 1 argument: number of iterations!\n");
            exit(1);
        }
        const int nf = atoi(argv[1]);
        printf("loop will have %i iterations\n", nf);
        clock_t begin, end;
        double time_spent;
        begin = clock();
        int ne = 2296;
        int ne2 = ne * ne;
        _Complex double* restrict pot = malloc(ne2 * sizeof(_Complex double));
        for (int i = 0; i < ne; i++) {
            for (int k = i; k < ne; k++) {
                pot[i * ne + k] = (double) rand() / RAND_MAX;
                pot[i * ne + k] *= I;
                pot[i * ne + k] += (double) rand() / RAND_MAX;
                pot[k * ne + i] = pot[i * ne + k];
            }
        }
        char trans = 'N';
        _Complex double one = 1.0;
        _Complex double zero = 0.0;
        #pragma omp parallel
        {
            int n = ne;
            int ipiv[n]; //pivot indices
            int info;
            int nrhs = 1;
            #pragma omp single
            {
                printf("avaible threads: %i\n", omp_get_num_threads());
            }
            _Complex double* restrict zl = malloc(ne2 * sizeof(_Complex double));
            _Complex double* restrict ie = malloc(ne2 * sizeof(_Complex double));
            _Complex double gpr;
            #pragma omp for
            for (int i = 0; i < nf; i++) {
                printf("i = %i from thread %d\n", i, omp_get_thread_num());
                for (int m = 0; m < ne; m++) {
                    for (int k = m; k < ne; k++) {
                        gpr = cexp(k - m);
                        zl[m * ne + k] = gpr * pot[m * ne + k];
                        zl[k * ne + m] = zl[m * ne + k];
                    }
                }
                ie[0] = 1.0;
                for (int m = 1; m < ne; m++) {
                    ie[m] = 0.0;
                }
                zgesv_(&n, &nrhs, zl, &n, ipiv, ie, &n, &info);
                // Check for the exact singularity
                if (info > 0) {
                    printf("The diagonal element of the triangular factor of ZL,\n");
                    printf("U(%i,%i) is zero, so that ZL is singular;\n", info, info);
                    printf("the solution could not be computed.\n");
                }
                zgemv_(&trans, &ne, &ne, &one, zl, &ne, ie, &nrhs, &zero, ie, &nrhs);
                for (int p = 0; p < ne2; p++) {
                    gpr = 0.0;
                    for (int m = 0; m < ne; m++) {
                        gpr += ie[m] * cexp(-m * 5.4) / 4.1;
                    }
                }
            }
            free(zl);
            free(ie);
        }
        free(pot);
        end = clock();
        time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
        printf("Success. Elapsed time: %.2f minutes\n", time_spent / 60.0);
        return 0;
    }