MPI获取具有最小值的处理器

15

在MPI中,我对一个值执行了一个reduce操作(最小值)。 这个操作运行良好,但我该如何获取这个最小值来自哪个处理器的信息,并向该处理器获取更多信息(或者将额外的数据与reduce操作一起发送)?

1个回答

27
如果您不介意本地将每个值与整数索引配对(在这种情况下,填充的是本地排名的值),则可以使用MPI_MINLOC或MPI_MAXLOC内置操作进行缩减;或者编写自己的MPI缩减运算符相当容易,以包括多个索引等内容。
更新添加: 使用MINLOC或MAXLOC内置运算符时,不仅要传入要查找最小值的单个值,还要传入一个整数索引。该索引可以具有任何您想要的值,但它会“跟随”其他值。MPI具有内置的“一对”数据类型-MPI_DOUBLE_INT用于双倍+ int,或MPI_2INT用于两个int,您可以使用它们。
因此,假设您要查找整数数组的最小值以及其所在的MPI任务。像往常一样,在每个任务上找到您的本地最小值,并进行缩减;但这次,您还要将其与一个整数配对,即您的排名。
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

int main(int argc, char **argv) {

    int rank, size;
    const int locn=5;
    int localarr[locn];

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

    srand(rank);
    for (int i=0; i<locn; i++) 
        localarr[i] = rand() % 100;

    for (int proc=0; proc<size; proc++) {
        if (rank == proc) {
            printf("Rank %2d has values: ",rank);
            for (int i=0; i<locn; i++)
                printf(" %d ", localarr[i]);
            printf("\n");
        }
        MPI_Barrier(MPI_COMM_WORLD);
    }

    int localres[2];
    int globalres[2];
    localres[0] = localarr[0];
    for (int i=1; i<locn; i++) 
        if (localarr[i] < localres[0]) localres[0] = localarr[i];

    localres[1] = rank;

    MPI_Allreduce(localres, globalres, 1, MPI_2INT, MPI_MINLOC, MPI_COMM_WORLD);

    if (rank == 0) {
        printf("Rank %d has lowest value of %d\n", globalres[1], globalres[0]);
    }

    MPI_Finalize();

    return 0;
}

而运行,则会得到:

$ mpirun -np 5 ./minloc
Rank  0 has values:  83  86  77  15  93 
Rank  1 has values:  83  86  77  15  93 
Rank  2 has values:  90  19  88  75  61 
Rank  3 has values:  46  85  68  40  25 
Rank  4 has values:  1  83  74  26  63 
Rank 4 has lowest value of 1

如果您要减少的值不是整数(比如双精度浮点数),则需要创建一个包含减少值和整数索引的结构体,并使用适当的 MPI 对数据类型(例如 MPI_DOUBLE_INT)。
更新:好吧,只是为了好玩,我们可以通过自己的减少操作和自己的类型实现两个索引。
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

typedef struct dbl_twoindex_struct {
    double val;
    int    rank;
    int    posn;
} dbl_twoindex;


void minloc_dbl_twoindex(void *in, void *inout, int *len, MPI_Datatype *type){
    /* ignore type, just trust that it's our dbl_twoindex type */
    dbl_twoindex *invals    = in;
    dbl_twoindex *inoutvals = inout;

    for (int i=0; i<*len; i++) {
        if (invals[i].val < inoutvals[i].val) {
            inoutvals[i].val  = invals[i].val;
            inoutvals[i].rank = invals[i].rank;
            inoutvals[i].posn = invals[i].posn;
        }
    }

    return;
}


int main(int argc, char **argv) {

    int rank, size;
    const int locn=5;
    double localarr[locn];

    dbl_twoindex local, global;

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

    /* create our new data type */
    MPI_Datatype mpi_dbl_twoindex;
    MPI_Datatype types[3] = { MPI_DOUBLE, MPI_INT, MPI_INT };
    MPI_Aint disps[3] = { offsetof(dbl_twoindex, val),
                     offsetof(dbl_twoindex, rank),
                     offsetof(dbl_twoindex, posn),  };
    int lens[3] = {1,1,1};
    MPI_Type_create_struct(3, lens, disps, types, &mpi_dbl_twoindex);
    MPI_Type_commit(&mpi_dbl_twoindex);

   /* create our operator */
    MPI_Op mpi_minloc_dbl_twoindex;
    MPI_Op_create(minloc_dbl_twoindex, 1, &mpi_minloc_dbl_twoindex);

    srand(rank);
    for (int i=0; i<locn; i++)
        localarr[i] = 1.*rand()/RAND_MAX;

    for (int proc=0; proc<size; proc++) {
        if (rank == proc) {
            printf("Rank %2d has values: ",rank);
            for (int i=0; i<locn; i++)
                printf(" %8.4lf ", localarr[i]);
            printf("\n");
        }
        MPI_Barrier(MPI_COMM_WORLD);
    }

    local.val  = localarr[0];
    local.posn = 0;
    for (int i=1; i<locn; i++)
        if (localarr[i] < local.val) {
                local.val  = localarr[i];
                local.posn = i;
        }
    local.rank = rank;

    MPI_Allreduce(&local, &global, 1, mpi_dbl_twoindex, mpi_minloc_dbl_twoindex, MPI_COMM_WORLD);

    if (rank == 0) {
        printf("Rank %d has lowest value of %8.4lf in position %d.\n", global.rank, global.val, global.posn);
    }

    MPI_Op_free(&mpi_minloc_dbl_twoindex);
    MPI_Type_free(&mpi_dbl_twoindex);
    MPI_Finalize();

    return 0;
}

跑步可以带来

$ mpirun -np 5 ./minloc2
Rank  0 has values:    0.8402    0.3944    0.7831    0.7984    0.9116 
Rank  1 has values:    0.8402    0.3944    0.7831    0.7984    0.9116 
Rank  2 has values:    0.7010    0.8097    0.0888    0.1215    0.3483 
Rank  3 has values:    0.5614    0.2250    0.3931    0.4439    0.2850 
Rank  4 has values:    0.9165    0.1340    0.1912    0.2601    0.2143 
Rank 2 has lowest value of   0.0888 in position 2.

你能详细说明或举例吗? - James Cotter
谢谢,那帮了我很多!能否定义类似MPI_DOUBLE_2INT这样的东西,以便我可以发送一个double类型的多个键? - James Cotter
我认为除了内置类型之外,你需要编写自己的操作,但这并不难。 - Jonathan Dursi
1
@JamesCotter:好的,上面是一个使用双精度和两个整数“自己编写”的示例。 - Jonathan Dursi
@JonathanDursi:在用户定义的函数中,如何在不进行强制转换的情况下从void*转换为dbl_twoindex* - SAAD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接