不调用MPI_Comm_create_keyval中提供的回调

时间:2014-08-08 07:42:41

标签: c callback parallel-processing mpi hpc

我正在阅读"使用MPI"并尝试自己执行代码。第6.2章中有一个非阻塞广播代码。我尝试使用自己的回调而不是MPI_NULL_COPY_FNMPI_NULL_DELETE_FN。这是我的代码,它与书中的代码非常相似,但不会调用回调。我不知道为什么。使用-Wall进行编译时没有警告或错误。请问你能帮帮我吗?非常感谢。

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

static int ibcast_keyval=MPI_KEYVAL_INVALID;

typedef struct
{
    MPI_Comm comm;
    int ordering_tag;
} Ibcast_syshandle;

typedef struct
{
    MPI_Request *req_array;
    MPI_Status *stat_array;
    int num_sends;
    int num_recvs;
} Ibcast_handle;

int Ibcast_work(Ibcast_handle *handle)
{
    if(handle->num_recvs==0)
        MPI_Startall(handle->num_sends, handle->req_array);
    else
        MPI_Startall(handle->num_recvs, &(handle->req_array[handle->num_sends]));
    return MPI_SUCCESS;
}

int Ibcast_copy(MPI_Comm oldcomm, int keyval, void *extra, void *attr_in, void *attr_out, int *flag)
{
    Ibcast_syshandle *syshandle=(Ibcast_syshandle *)attr_in;
    Ibcast_syshandle *new_syshandle;
    printf("keyval=%d\n", keyval);
    fflush(stdout);
    if((keyval==MPI_KEYVAL_INVALID)||(keyval!=ibcast_keyval)||(syshandle==NULL))
        return 1;
    new_syshandle=(Ibcast_syshandle *)malloc(sizeof(Ibcast_syshandle));
    new_syshandle->ordering_tag=0;
    MPI_Comm_dup(syshandle->comm, &(new_syshandle->comm));
    {
        int rank;
        MPI_Comm_rank(new_syshandle->comm, &rank);
        printf("Ibcast_copy called from %d\n", rank);
        fflush(stdout);
    }
    *(void **)attr_out=(void *)new_syshandle;
    *flag=1;
    return MPI_SUCCESS;
}

int Ibcast_delete(MPI_Comm comm, int keyval, void *attr_val, void *extra)
{
    Ibcast_syshandle *syshandle=(Ibcast_syshandle *)attr_val;
    {
        int rank;
        MPI_Comm_rank(syshandle->comm, &rank);
        printf("Ibcast_delete called from %d\n", rank);
        fflush(stdout);
    }
    if((keyval==MPI_KEYVAL_INVALID)||(keyval!=ibcast_keyval)||(syshandle==NULL))
        return 1;
    MPI_Comm_free(&(syshandle->comm));
    free(syshandle);
    return MPI_SUCCESS;
}

int Ibcast(void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm, Ibcast_handle **handle_out)
{
    Ibcast_syshandle *syshandle;
    Ibcast_handle *handle;
    int flag, mask, relrank;
    int retn, size, rank;
    int req_no=0;

    MPI_Comm_size(comm, &size);
    MPI_Comm_rank(comm, &rank);

    if(size==1)
    {
        (*handle_out)=NULL;
        return MPI_SUCCESS;
    }
    if(ibcast_keyval==MPI_KEYVAL_INVALID)
//      MPI_Keyval_create(MPI_NULL_COPY_FN, MPI_NULL_DELETE_FN, &ibcast_keyval, NULL);
        MPI_Comm_create_keyval(Ibcast_copy, Ibcast_delete, &ibcast_keyval, NULL);
    MPI_Comm_get_attr(comm, ibcast_keyval, (void **)&syshandle, &flag);
    if(flag==0)
    {
        syshandle=(Ibcast_syshandle *)malloc(sizeof(Ibcast_syshandle));
        syshandle->ordering_tag=0;
        MPI_Comm_dup(comm, &(syshandle->comm));
        MPI_Comm_set_attr(comm, ibcast_keyval, (void *)syshandle);
    }
    handle=(Ibcast_handle *)malloc(sizeof(Ibcast_handle));
    handle->num_sends=handle->num_recvs=0;
    mask=0x1;
    relrank=(rank-root+size)%size;
    while((mask&relrank)==0 && mask<size)
    {
        if((relrank|mask)<size)
            ++handle->num_sends;
        mask<<=1;
    }
    if(mask<size)
        ++handle->num_recvs;
    handle->req_array=(MPI_Request *)malloc(sizeof(MPI_Request)*(handle->num_sends+handle->num_recvs));
    handle->stat_array=(MPI_Status *)malloc(sizeof(MPI_Status)*(handle->num_sends+handle->num_recvs));
    mask=0x1;
    relrank=(rank-root+size)%size;
    while((mask&relrank)==0 && mask<size)
    {
        if((relrank|mask)<size)
            MPI_Send_init(buf, count, datatype, ((relrank|mask)+root)%size, syshandle->ordering_tag, syshandle->comm, &(handle->req_array[req_no++]));
        mask<<=1;
    }
    if(mask<size)
        MPI_Recv_init(buf, count, datatype, ((relrank & (~mask))+root)%size, syshandle->ordering_tag, syshandle->comm, &(handle->req_array[req_no++]));
    retn=Ibcast_work(handle);
    ++(syshandle->ordering_tag);
    (*handle_out)=handle;
    return retn;
}

int Ibcast_wait(Ibcast_handle **handle_out)
{
    Ibcast_handle *handle=(*handle_out);
    int retn, i;
    if(handle==NULL)
        return MPI_SUCCESS;
    if(handle->num_recvs!=0)
    {
        MPI_Waitall(handle->num_recvs, &handle->req_array[handle->num_sends], &handle->stat_array[handle->num_sends]);
        MPI_Startall(handle->num_sends, handle->req_array);
    }
    retn=MPI_Waitall(handle->num_sends, handle->req_array, handle->stat_array);
    for(i=0; i<(handle->num_sends+handle->num_recvs);i++)
        MPI_Request_free(&(handle->req_array[i]));
    free(handle->req_array);
    free(handle->stat_array);
    free(handle);
    *handle_out=NULL;
    return retn;
}

int main( int argc, char *argv[] )
{
    int buf1[10], buf2[20];
    int rank, i;
    Ibcast_handle *ibcast_handle_1, *ibcast_handle_2;

    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
    if (rank == 0) {
    for (i=0; i<10; i++) buf1[i] = i;
    for (i=0; i<20; i++) buf2[i] = -i;
    }
    Ibcast( buf1, 10, MPI_INT, 0, MPI_COMM_WORLD, &ibcast_handle_1 );
    Ibcast( buf2, 20, MPI_INT, 0, MPI_COMM_WORLD, &ibcast_handle_2 );
    Ibcast_wait( &ibcast_handle_1 );
    Ibcast_wait( &ibcast_handle_2 );
    for (i=0; i<10; i++) {
    if (buf1[i] != i) printf( "buf1[%d] = %d on %d\n", i, buf1[i], rank );
    }
    for (i=0; i<20; i++) {
    if (buf2[i] != -i) printf( "buf2[%d] = %d on %d\n", i, buf2[i], rank );
    }
    MPI_Finalize();
    return 0;
}

1 个答案:

答案 0 :(得分:3)

当复制或删除通信器时,或者仅在删除属性时,回调函数用于复制和删除创建的属性。回调函数是必需的,因为属性可以完全是任意的。

所以这里是 工作的代码的简化版本(创建这样的minimal example是一种有用的方法,可以跟踪问题并获取SO等网站的帮助):

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

static int ibcast_keyval;

int Ibcast_copy(MPI_Comm oldcomm, int keyval, void *extra, void *attr_in, void *attr_out, int *flag)
{
    printf("In ibcast_copy: keyval = %d\n", keyval);
    *flag = 1;
    return MPI_SUCCESS;
}

int Ibcast_delete(MPI_Comm comm, int keyval, void *attr_val, void *extra)
{
    printf("In ibcast_delete: keyval = %d\n", keyval);
    return MPI_SUCCESS;
}

int main( int argc, char *argv[] )
{
    int rank, i;
    int attr=2;

    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );

    MPI_Comm duped_comm;

    MPI_Comm_create_keyval(Ibcast_copy, Ibcast_delete, &ibcast_keyval, NULL);
    MPI_Comm_set_attr( MPI_COMM_WORLD, ibcast_keyval, &attr);
    MPI_Comm_dup( MPI_COMM_WORLD, &duped_comm);
    MPI_Comm_free( &duped_comm );
    MPI_Comm_delete_attr( MPI_COMM_WORLD, ibcast_keyval );

    MPI_Finalize();
    return 0;
}

在这里,我们使用回调创建keyval,设置与密钥对应的属性,然后复制MPI_COMM_WORLD(调用复制回调),然后释放dup-ed通信器并从COMM_WORLD中删除该属性(调用delete回调两次:)

$ mpirun -np 1 ./comm-attr
In ibcast_copy: keyval = 10
In ibcast_delete: keyval = 10
In ibcast_delete: keyval = 10

在您的代码中,您在设置属性之前复制Ibcast中的通信器,以便永远不会调用复制回调(因为没有要复制的内容)。您可以通过在dup之前设置属性来修复该部分,但是还有另一个问题 - 您在回调中调用dup和free,这是错误的方式;这些函数是(间接地)调用回调,反之亦然。