奇怪的是MPI和OMP放慢了速度

时间:2013-02-08 04:14:00

标签: 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]-Zo[i])*(Z[i]-Zo[i]);
                ed += U1[i]*U1[i] + U2[i]*U2[i] + U3[i]*U3[i];
            }
        }

/////////////////////////////////////////////// ///////////////////////////////////////

double rk = 0, epri = 0, ex = 0, ez = 0;
            #pragma omp parallel shared(X,Z) private(i) reduction(+: rk, ex,ez)
            {
                #pragma omp for
                for(i = 0; i<imgDim; i++)
                {
                    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];
                }
            }

Z,Zo,U1,U2,U3,X都是大矩阵。 imgDim是400万。加速速度不如预期。在16核机器上,这两个小代码的加速只有两倍。我不明白为什么OMP会出现这种行为,因为这两个代码只会增加一些内容。这应该是OMP擅长的。

当我尝试使用MPI并行处理这些代码时,更奇怪的行为是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);

np是处理器的数量,pid是当前处理器的id。在我使用32或甚至64个处理器后,它没有显示任何加速。它甚至比顺序代码慢。我不懂为什么。这些代码只是添加内容。 OMP和MPI应该擅长。任何人都可以帮我一把吗?

1 个答案:

答案 0 :(得分:1)

您的代码受内存限制 - 您在每次迭代时加载大量数据并对其进行简单(即快速)计算。如果imgDim为400万,则即使ZZoU1U2U3的每个元素都短至4字节(例如,它们是floatint数组),它们的总大小将是80 MiB,即使给定双插槽系统,这也不适合最后一级CPU缓存。如果这些数组保持double值(由于你减少到double个变量的事实暗示),事情会更糟,因为它会使内存大小增加两倍。此外,如果您使用一个能够对代码进行矢量化的合适编译器(例如icc默认执行,GCC需要-ftree-vectorize),即使单个线程也能够使内存带宽饱和。 CPU套接字然后使用多个线程运行将不会带来任何好处。

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

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