c - 使用 MPI_Put 的异步有限差分方案

标签 c asynchronous parallel-processing mpi mpi-rma

Donzis 和 Aditya 的一篇论文表明,可以使用可能在模板中产生延迟的有限差分方案。这是什么意思? FD 方案可用于求解热方程并读取(或对其进行一些简化)

u[t+1,i] = u[t,i] + c (u[t,i-1]-u[t,i+1])

意思是,下一个时间步的值取决于上一个时间步相同位置及其邻居的值。

这个问题可以很容易地通过将(在我们的例子中是一维的)域拆分到不同的处理器上来解决。但是,在处理器上计算边界节点时我们需要通信,因为元素 u[t,i+-1] 仅在另一个处理器上可用。

下图说明了该问题,该图摘自所引用的论文。

enter image description here

MPI 实现可能使用 MPI_SendMPI_Recv 进行同步计算。 由于计算本身相当容易,因此通信可能成为瓶颈。

论文给出了问题的解决方案:

不使用同步过程,只取可用的边界注释,尽管它可能是较早时间步长的值。然后该方法仍然收敛(在某些假设下)

对于我的工作,我想实现异步 MPI 案例(这不是论文的一部分)。使用 MPI_SendMPI_Recv 的同步部分工作正常。我将内存扩展了两个元素作为相邻元素的幽灵单元,并通过发送和接收发送所需的值。下面的代码基本上是上图的实现,在计算之前的每个时间步执行。

MPI_Send(&u[NpP],1,MPI_DOUBLE,RIGHT,rank,MPI_COMM_WORLD);
MPI_Recv(&u[0],1,MPI_DOUBLE,LEFT,LEFT,MPI_COMM_WORLD,MPI_STATUS_IGNORE);

MPI_Send(&u[1],1,MPI_DOUBLE,LEFT,rank,MPI_COMM_WORLD);
MPI_Recv(&u[NpP+1],1,MPI_DOUBLE,RIGHT,RIGHT,MPI_COMM_WORLD,MPI_STATUS_IGNORE);

现在,我绝不是 MPI 专家。我想通了,MPI_Put 可能是我在异步情况下所需要的,稍微阅读一下,我想到了以下实现。

时间循环之前:

MPI_Win win;
double *boundary;
MPI_Alloc_mem(sizeof(double) * 2, MPI_INFO_NULL, &boundary);
MPI_Info info;
MPI_Info_create(&info);
MPI_Info_set(info,"no_locks","true");
MPI_Win_create(boundary, 2*sizeof(double), sizeof(double), info, MPI_COMM_WORLD, &win);

在时间循环内:

MPI_Put(&u[1],1,MPI_DOUBLE,LEFT,1,1,MPI_DOUBLE,win);
MPI_Put(&u[NpP],1,MPI_DOUBLE,RIGHT,0,1,MPI_DOUBLE,win);
MPI_Win_fence(0,win);
u[0] = boundary[0];
u[NpP+1] = boundary[1];

将所需的元素放在窗口中,即相邻处理器上的 boundary(具有两个元素的数组),并取值 u[0] u[NpP+1] 来自 boundary 数组本身。 此实现有效,我得到与 MPI_Send/Recv 相同的结果。但是,这并不是真正的异步,因为我仍在使用 MPI_Win_fence,据我所知,它可以确保同步。

问题是:如果我取出 MPI_Win_fenceboundary 内的值永远不会更新并保持初始值。我的理解是,如果没有 MPI_Win_fence,您将获取 boundary 内可用的任何值,这些值可能(或可能不会)已被相邻处理器更新。

有没有人有想法避免使用 MPI_Win_fence 同时解决 boundary 内的值永远不会更新的问题?

我也不确定我提供的代码是否足以理解我的问题或提供任何提示。如果是这样,请随时提问,因为我会尝试添加所有缺失的部分。

最佳答案

从正确执行的角度来看,以下作品似乎对我有用 - 从我们的一个教程中提取的一个小的 1d 热方程,用于 RMA 内容:

MPI_Win_lock( MPI_LOCK_EXCLUSIVE, left, 0, rightwin );
MPI_Put(&(temperature[current][1]),         1, MPI_FLOAT, left,  0, 1, MPI_FLOAT, rightwin);
MPI_Win_unlock( left, rightwin );

MPI_Win_lock( MPI_LOCK_EXCLUSIVE, right, 0, leftwin );
MPI_Put(&(temperature[current][locpoints]), 1, MPI_FLOAT, right, 0, 1, MPI_FLOAT, leftwin);
MPI_Win_unlock( right, leftwin );

MPI_Win_lock( MPI_LOCK_EXCLUSIVE, rank, 0, leftwin );
temperature[current][0]           = *leftgc;
MPI_Win_unlock( rank, leftwin );

MPI_Win_lock( MPI_LOCK_EXCLUSIVE, rank, 0, rightwin );
temperature[current][locpoints+1] = *rightgc;
MPI_Win_unlock( rank, rightwin );

在代码中,我什至让等级在每个时间步额外等待 10 毫秒,以确保事情不同步;但是从痕迹上看,实际上看起来事情仍然非常同步。我不知道这种高度同步是否可以通过调整代码来解决,或者是实现的限制(IntelMPI 5.0.1),或者只是因为计算中传递的时间太少和通信时间太少而发生占主导地位(但就最后一个而言,增加 sleep 间隔似乎没有效果)。

#define _BSD_SOURCE     /* usleep */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#include <mpi.h>


int main(int argc, char **argv) {
    /* simulation parameters */
    const int totpoints=1000;
    int locpoints;
    const float xleft = -12., xright = +12.;
    float locxleft, locxright;
    const float kappa = 1.;

    const int nsteps=100;

    /* data structures */
    float *x;
    float **temperature;

    /* parameters of the original temperature distribution */
    const float ao=1., sigmao=1.;

    float fixedlefttemp, fixedrighttemp;

    int current, new;
    int step, i;
    float time;
    float dt, dx;
    float rms;

    int rank, size;
    int start,end;
    int left, right;
    int lefttag=1, righttag=2;

    /* MPI Initialization */
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD,&size);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);

    locpoints = totpoints/size;
    start = rank*locpoints;
    end   = (rank+1)*locpoints - 1;
    if (rank == size-1)
        end = totpoints-1;
    locpoints = end-start+1;

    left = rank-1;
    if (left < 0) left = MPI_PROC_NULL;
    right= rank+1;
    if (right >= size) right = MPI_PROC_NULL;

    #ifdef ONESIDED
    if (rank == 0)
        printf("Onesided: Allocating windows\n");
    MPI_Win leftwin, rightwin;
    float *leftgc, *rightgc;
    MPI_Win_allocate(sizeof(float), sizeof(float), MPI_INFO_NULL, MPI_COMM_WORLD, &leftgc,  &leftwin);
    MPI_Win_allocate(sizeof(float), sizeof(float), MPI_INFO_NULL, MPI_COMM_WORLD, &rightgc, &rightwin);
    #endif
    /* set parameters */

    dx = (xright-xleft)/(totpoints-1);
    dt = dx*dx * kappa/10.;

    locxleft = xleft + start*dx;
    locxright = xleft + end*dx;

    x      = (float *)malloc((locpoints+2)*sizeof(float));
    temperature = (float **)malloc(2 * sizeof(float *));
    temperature[0] = (float *)malloc((locpoints+2)*sizeof(float));
    temperature[1] = (float *)malloc((locpoints+2)*sizeof(float));
    current = 0;
    new = 1;

    /* setup initial conditions */

    time = 0.;
    for (i=0; i<locpoints+2; i++) {
        x[i] = locxleft + (i-1)*dx;
        temperature[current][i] = ao*exp(-(x[i]*x[i]) / (2.*sigmao*sigmao));
    }
    fixedlefttemp = ao*exp(-(locxleft-dx)*(locxleft-dx) / (2.*sigmao*sigmao));
    fixedrighttemp= ao*exp(-(locxright+dx)*(locxright+dx)/(2.*sigmao*sigmao));
    #ifdef ONESIDED
    *leftgc  = fixedlefttemp;
    *rightgc = fixedrighttemp;
    #endif

    /* evolve */
    for (step=0; step < nsteps; step++) {
        /* boundary conditions: keep endpoint temperatures fixed. */

        #ifdef ONESIDED
            MPI_Win_lock( MPI_LOCK_EXCLUSIVE, left, 0, rightwin );
            MPI_Put(&(temperature[current][1]),         1, MPI_FLOAT, left,  0, 1, MPI_FLOAT, rightwin);
            MPI_Win_unlock( left, rightwin );

            MPI_Win_lock( MPI_LOCK_EXCLUSIVE, right, 0, leftwin );
            MPI_Put(&(temperature[current][locpoints]), 1, MPI_FLOAT, right, 0, 1, MPI_FLOAT, leftwin);
            MPI_Win_unlock( right, leftwin );

            MPI_Win_lock( MPI_LOCK_EXCLUSIVE, rank, 0, leftwin );
            temperature[current][0]           = *leftgc;
            MPI_Win_unlock( rank, leftwin );

            MPI_Win_lock( MPI_LOCK_EXCLUSIVE, rank, 0, rightwin );
            temperature[current][locpoints+1] = *rightgc;
            MPI_Win_unlock( rank, rightwin );
        #else
            temperature[current][0] = fixedlefttemp;
            temperature[current][locpoints+1] = fixedrighttemp;

            /* send data rightwards */
            MPI_Sendrecv(&(temperature[current][locpoints]), 1, MPI_FLOAT, right, righttag,
                         &(temperature[current][0]), 1, MPI_FLOAT, left,  righttag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

            /* send data leftwards */
            MPI_Sendrecv(&(temperature[current][1]), 1, MPI_FLOAT, left, lefttag,
                         &(temperature[current][locpoints+1]), 1, MPI_FLOAT, right,  lefttag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        #endif

        for (i=1; i<locpoints+1; i++) {
            temperature[new][i] = temperature[current][i] + dt*kappa/(dx*dx) *
                (temperature[current][i+1] - 2.*temperature[current][i] +
                 temperature[current][i-1]) ;
        }

        time += dt;

        if ((rank % 2) == 0)
            usleep(10000u);

        current = new;
        new = 1 - current;
    }

    rms  = 0.;
    for (i=1;i<locpoints+1;i++) {
        rms += (temperature[current][i])*(temperature[current][i]);
    }
    float totrms;
    MPI_Reduce(&rms, &totrms, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);

    if (rank == 0) {
        totrms = sqrt(totrms/totpoints);
        printf("Step = %d, Time = %g, RMS value = %g\n", step, time, totrms);
    }


    #ifdef ONESIDED
    MPI_Win_free(&leftwin);
    MPI_Win_free(&rightwin);
    #endif

    free(temperature[1]);
    free(temperature[0]);
    free(temperature);
    free(x);

    MPI_Finalize();
    return 0;
}

关于c - 使用 MPI_Put 的异步有限差分方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26299348/

相关文章:

c - 可以在函数参数中声明一个数组吗?

c - 如何使用 gettimeofday() 在 c/unix 中打印时间

java - 处理异步响应

c# - 并行类不提供任何加速

c++ - 不完整的多线程 RayTracer 花费的时间是预期的两倍

c - xor 加密期间额外的 0xFFFFFF

javascript - Node.js 中的异步查询是否有助于克服实时数据流中的延迟?

python - asyncio 无法在 Windows 上读取标准输入

sql-server - SqlServer - 选项重新编译并行执行行为

c - 如何让函数体在c中重复