Parallel processing 使用MPI和OMP进行奇怪的减速

Parallel processing 使用MPI和OMP进行奇怪的减速,parallel-processing,mpi,openmp,Parallel Processing,Mpi,Openmp,我有两个非常简单的代码。我试图将它们进行如下比较: double sk = 0, ed = 0; #pragma omp parallel shared(Z,Zo,U1,U2,U3) private(i) reduction(+: sk, ed) { #pragma omp for for (i=0;i<imgDim;i++) { sk += (Z[i]-

我有两个非常简单的代码。我试图将它们进行如下比较:

double sk = 0, ed = 0;
        #pragma omp parallel shared(Z,Zo,U1,U2,U3) private(i) reduction(+: sk, ed)
        {
            #pragma omp for
            for (i=0;i<imgDim;i++)
            {
                sk += (Z[i]-Zo[i])*(Z[i]-Zo[i]);
                ed += U1[i]*U1[i] + U2[i]*U2[i] + U3[i]*U3[i];
            }
        }
int startval = imgDim*pid/np;
int endval = imgDim*(pid+1)/np-1;
int ierr;
double p_sum_sk = 0;
double p_sum_ed = 0;

for (i=startval;i<=endval;i++)
{
    sk += (Z[i]-Zo[i])*(Z[i]-Zo[i]);
    ed += U1[i]*U1[i] + U2[i]*U2[i] + U3[i]*U3[i];
}

MPI_Reduce(&sk, &p_sum_sk, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&ed, &p_sum_ed, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Bcast(&sk, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Bcast(&ed, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
double-sk=0,ed=0;
#pragma omp并行共享(Z,Zo,U1,U2,U3)私有(i)缩减(+:sk,ed)
{
#pragma omp for

对于(i=0;i您的代码是内存受限的-您在每次迭代中加载大量数据并对其进行简单(即快速)计算。如果
imgDim
为400万,那么即使
Z
Zo
U1
U2
U3
的每个元素都短至4字节(例如,它们是
float
int
数组),它们的总大小将是80 MiB,即使给定双套接字系统,这也不适合最后一级CPU缓存。如果这些数组包含
double
值,情况会更糟(这一事实表明,将
double
变量缩减为
变量),因为它会将内存大小增加两倍。此外,如果您使用一个像样的编译器,它能够对代码进行矢量化(例如,
icc
默认情况下这样做,GCC需要
-ftree矢量化
),即使是单个线程也会使CPU插槽的内存带宽饱和,然后使用多个线程运行也不会带来任何好处

我想说,您在16核系统上观察到的2x OpenMP加速是因为该系统有两个CPU插槽,并且是NUMA,即每个插槽上都有一个单独的内存控制器,因此当使用16个线程运行时,您使用的内存带宽是单个插槽的两倍。如果您运行代码,可以验证这一点只有两个线程,但以不同的方式绑定:同一套接字上的每个内核一个线程,或不同套接字上的每个内核一个线程。在第一种情况下,速度不应该加快,而在第二种情况下,速度应该是2倍左右。将线程绑定到内核(尚未)依赖于实现-如果您碰巧使用英特尔编译器,您可以查看GCC

MPI的情况也是如此。现在有进程而不是线程,但内存带宽限制仍然存在。情况更糟,因为现在还增加了通信开销,如果问题规模太小,可能会超过计算时间(该比率取决于网络互连-使用更快和更少的潜在互连(如QDR InfiniBand fabric)时,该比率较低)。但使用MPI,您可以访问更多的CPU插槽,从而获得更高的总内存带宽。您可以使用每个插槽一个MPI进程来启动代码,以尽可能获得系统的最佳性能。在这种情况下,进程绑定(或英特尔术语中的固定)也很重要

int startval = imgDim*pid/np;
int endval = imgDim*(pid+1)/np-1;
int ierr;
double p_sum_sk = 0;
double p_sum_ed = 0;

for (i=startval;i<=endval;i++)
{
    sk += (Z[i]-Zo[i])*(Z[i]-Zo[i]);
    ed += U1[i]*U1[i] + U2[i]*U2[i] + U3[i]*U3[i];
}

MPI_Reduce(&sk, &p_sum_sk, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&ed, &p_sum_ed, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Bcast(&sk, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Bcast(&ed, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
int startval = imgDim*pid/np;
int endval = imgDim*(pid+1)/np-1;
double p_sum_rk = 0.;
double p_sum_ex = 0.;
double p_sum_ez = 0.;


for(i = startval; i<=endval; i++)
{
    rk = rk + (X[0][i]-Z[i])*(X[0][i]-Z[i]) + (X[1][i]-Z[i])*(X[1][i]-Z[i]) + (X[2][i]-Z[i])*(X[2][i]-Z[i]);
    ex += X[0][i]*X[0][i] + X[1][i]*X[1][i] + X[2][i]*X[2][i];
    ez += Z[i]*Z[i];
}

MPI_Reduce(&rk,&p_sum_rk,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
MPI_Reduce(&ex,&p_sum_ex,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
MPI_Reduce(&ez,&p_sum_ez,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
MPI_Bcast(&rk,1,MPI_INT,0,MPI_COMM_WORLD);
MPI_Bcast(&rk,1,MPI_INT,0,MPI_COMM_WORLD);
MPI_Bcast(&epri,1,MPI_INT,0,MPI_COMM_WORLD);