Эквивалентно MPI_Reduce_scatter, но с разбросом по подмножеству процессоров.

Существует ли функция MPI, эквивалентная MPI_Reduce_scatter, которая выполняет рассеяние только среди подмножества процессоров? В случае отсутствия такой функции, какая последовательность вызовов MPI является наиболее эффективной?


Для ясности предположим, что операция редукции является суммой. Из

proc    sendbuf
1       a1 | b1
2       a2 | b2
3       a3 | b3
4       a4 | b4 

я хочу получить

proc    recvbuf
1       a
2       b
3       
4       

Где a = a1 + a2 + a3 + a4 и b = b1 + b2 + b3 + b4.


Обходные пути

  • Используя два MPI_Reduce. Первый уменьшает as и имеет 1 в качестве корневого процессора. Второй уменьшает bs и имеет 2 в качестве корневого процессора. Однако становится тяжело, когда много "букв" (и много процессоров).
  • MPI_Reduce_scatter и набор recv_count[proc] отличен от нуля только в том случае, если proc принадлежит подмножеству процессоров. Однако при разбрасывании сообщения будет n_proc квадратных рукопожатий (большинство из них бесполезны, поскольку на самом деле сообщение не отправляется).

mpi
person dPol    schedule 18.06.2014    source источник


Ответы (2)


Я согласен с тем, что два вызова MPI_REDUCE, вероятно, лучше, чем вы собираетесь получить, если вы хотите, чтобы MPI выполнял сокращение за вас (что вы, вероятно, делаете в масштабе).

person Wesley Bland    schedule 19.06.2014
comment
Не могли бы вы также прокомментировать мое второе предположение? - person dPol; 19.06.2014
comment
Ах. Это лучший способ сделать это. Я забыл об этом. - person Wesley Bland; 19.06.2014

Возможно, вы слишком много думаете об этом — в приведенном выше случае вы могли бы сделать это с помощью одного сокращения и одной пары отправки/получения:

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

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

    int data[2], result[2];
    int rank, size;
    const int amaster=0, bmaster=1;

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

    data[0] = 2*rank;
    data[1] = 3*rank;

    MPI_Reduce(data, result, 2, MPI_INT, MPI_SUM, amaster, MPI_COMM_WORLD);
    if (rank == amaster) {
        int tota = result[0];
        printf("%d: Total a = %d, expected = %d\n", rank, tota, 2*size*(size-1)/2);
        MPI_Send(&(result[1]), 1, MPI_INT, bmaster, 0, MPI_COMM_WORLD);
    }
    if (rank == bmaster) {
        int totb;
        MPI_Recv(&totb, 1, MPI_INT, amaster, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        printf("%d: Total b = %d, expected = %d\n", rank, totb, 3*size*(size-1)/2);
    }

    MPI_Finalize();
    return 0;
}

И естественным обобщением для нескольких фрагментов данных является разброс, поэтому ваша интуиция относительно MPI_Reduce_scatter верна, но здесь, поскольку две группы задач перекрываются (поэтому вы не можете использовать коммуникатор), но не совпадают, у вас будет сделать скаттер с последующим скаттером, причем скаттер должен быть на другом коммуникаторе:

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

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

    const int ndata = 5;
    int data[ndata], result[ndata];
    int rank, size;
    const int amaster=0;
    MPI_Comm  scattercomm;

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

    if (size < ndata) {
        if (rank == 0) fprintf(stderr,"Too few ranks; exiting\n");
        MPI_Abort(MPI_COMM_WORLD,1);
    }

    for (int i=0; i<ndata; i++)
        data[i] = (i+2)*rank;

    /* create scatter communicator; all  of comm world must participate */
    MPI_Group basegrp, scattergrp;

    MPI_Comm_group(MPI_COMM_WORLD, &basegrp);

    int mpiranks[ndata];
    for (int i=0; i<ndata; i++)
        mpiranks[i] = i;

    MPI_Group_incl(basegrp, ndata, mpiranks, &scattergrp);
    MPI_Comm_create(MPI_COMM_WORLD, scattergrp, &scattercomm);

    MPI_Reduce(data, result, ndata, MPI_INT, MPI_SUM, amaster, MPI_COMM_WORLD);
    if (rank < ndata) {
        int item;
        MPI_Scatter(result, 1, MPI_INT, &item, 1, MPI_INT, amaster, scattercomm);

        printf("%d: Total = %d, expected = %d\n", rank, item, (rank+2)*size*(size-1)/2);
    }

    MPI_Finalize();
    return 0;
}
person Jonathan Dursi    schedule 19.06.2014