MPI Получить процессор с минимальным значением

В MPI я выполняю операцию уменьшения (минимум) значения. Это прекрасно работает, но как мне получить номер процессора, из которого получен минимум, и запросить у этого процессора дополнительную информацию (или отправить дополнительные данные с помощью операции сокращения)?


person James Cotter    schedule 14.02.2012    source источник


Ответы (1)


Если вы не возражаете против локального сопоставления каждого значения с целочисленным индексом (в данном случае заполненным значением локального ранга), вы можете использовать MPI_MINLOC или MPI_MAXLOC встроенные операции для сокращения; или довольно легко написать свой собственный оператор сокращения MPI, включающий такие вещи, как несколько индексов и т. д.

Добавлено добавление: со встроенными операторами MINLOC или MAXLOC вместо передачи одного значения для поиска минимума вы передаете его плюс целочисленный индекс. Этот индекс может иметь любое значение, которое вы хотите, но он «следует» за другим значением. MPI имеет встроенные "парные" типы данных - MPI_DOUBLE_INT для двойного числа + целое число или MPI_2INT для двух целых чисел, которые вы можете использовать.

Допустим, вы хотите найти минимум целочисленного массива и в какой задаче 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.
person Jonathan Dursi    schedule 14.02.2012
comment
Можете ли вы уточнить или привести пример? - person James Cotter; 15.02.2012
comment
Спасибо, это так помогло! Можно ли определить что-то вроде MPI_DOUBLE_2INT, чтобы я мог отправлять более одного ключа на двойное число? - person James Cotter; 15.02.2012
comment
Я думаю, что для всего, кроме встроенных типов, вам придется написать свою собственную операцию, но это будет не так сложно. - person Jonathan Dursi; 15.02.2012
comment
@JamesCotter: Хорошо, выше приведен пример нашего собственного с двойным и двумя целыми числами. - person Jonathan Dursi; 15.02.2012
comment
@JonathanDursi: Как в определяемой пользователем функции можно перейти от void* к dbl_twoindex* без приведения? - person SAAD; 09.05.2014