Поток для межпроцессного взаимодействия в OpenMP - PullRequest
1 голос
/ 13 марта 2012

У меня есть распараллеленная программа OpenMP, которая выглядит так:

[...]
#pragma omp parallel
{
//initialize threads

#pragma omp for
for(...)
  {
  //Work is done here

  }

}

Теперь я добавляю поддержку MPI. Что мне понадобится, так это поток, который обрабатывает связь, в моем случае все время вызывает GatherAll и заполняет / очищает связанный список для получения / отправки данных из других процессов. Этот поток должен отправлять / получать, пока не будет установлен флаг. Так что сейчас в примере нет MPI, мой вопрос касается реализации этой подпрограммы в OpenMP. Как мне реализовать такую ​​ветку? Например, я попытался ввести здесь одну директиву:

[...]
int kill=0
#pragma omp parallel shared(kill)
{
//initialize threads
#pragma omp single nowait
 {
  while(!kill)
   send_receive(); 
 }
#pragma omp for
for(...)
  {
  //Work is done here

  }
kill=1

} 

но в этом случае программа застревает, потому что неявный барьер после цикла for ожидает поток в цикле while выше.

Спасибо, rugermini.

Ответы [ 3 ]

0 голосов
/ 13 марта 2012

Хммм. Если вы действительно добавляете поддержку MPI в вашу программу, то вам следует использовать mpi_allgather, так как mpi_gatherall не существует. Обратите внимание, что mpi_allgather является коллективной операцией, то есть все процессы в коммуникаторе вызывают ее. Вы не можете иметь процесс сбора данных, в то время как другие процессы делают то, что они делают. Что вы можете сделать, это использовать MPI односторонние коммуникации для реализации вашей идеи; это будет немного сложнее, но не более того, если один процесс только читает память других процессов.

Я озадачен тем, что вы используете термин «нить» с MPI. Боюсь, что вы путаете OpenMP и MPI, один из вариантов которого называется OpenMPI. Несмотря на это название, он так же отличается от OpenMP, как мел от сыра. Программы MPI написаны с точки зрения процессов, а не потоков. Типичная реализация OpenMP действительно использует потоки, хотя детали, как правило, хорошо скрыты от программиста.

Я серьезно впечатлен тем, что вы пытаетесь или, кажется, пытаетесь использовать MPI «внутри» вашего кода OpenMP. Это полная противоположность работе, которую я выполняю, и вижу, как другие делают на некоторых больших компьютерах. Стандартный режим для такого «гибридного» распараллеливания - это написание программ MPI, которые вызывают код OpenMP. Многие из сегодняшних очень больших компьютеров составляют коллекции того, что фактически является многоядерными блоками. Типичный подход к программированию одного из них состоит в том, чтобы на каждом блоке работал один процесс MPI, а для каждого из этих процессов - использовать один поток OpenMP для каждого ядра в блоке.

0 голосов
/ 13 марта 2012

Вы должны быть осторожны, потому что вы не можете просто заставить свой вызывающий поток MPI "пропускать" цикл omp for; все потоки в команде потоков должны пройти цикл for.

Существует несколько способов сделать это: с помощью вложенного параллизма и задач вы можете запустить одну задачу для передачи сообщений и другую функцию для вызова рабочей подпрограммы, в которой есть параллельная omp:

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

void work(int rank) {
    const int n=14;
    #pragma omp parallel for
    for (int i=0; i<n; i++) {
        int tid = omp_get_thread_num();
        printf("%d:%d working on item %d\n", rank, tid, i);
    }
}

void sendrecv(int rank, int sneighbour, int rneighbour, int *data) {
    const int tag=1;
    MPI_Sendrecv(&rank, 1, MPI_INT, sneighbour, tag,
                  data, 1, MPI_INT, rneighbour, tag,
                  MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}

int main(int argc, char **argv) {
    int rank, size;
    int sneighbour;
    int rneighbour;
    int data;
    int got;

    MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &got);
    MPI_Comm_size(MPI_COMM_WORLD,&size);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);

    omp_set_nested(1);
    sneighbour = rank + 1;
    if (sneighbour >= size) sneighbour = 0;
    rneighbour = rank - 1;
    if (rneighbour <0 ) rneighbour = size-1;

    #pragma omp parallel 
    {
        #pragma omp single
        {
            #pragma omp task 
            {
                sendrecv(rank, sneighbour, rneighbour, &data);
                printf("Got data from %d\n", data);
            }

            #pragma omp task
            work(rank);
        }
    }


    MPI_Finalize();
    return 0;
}

В качестве альтернативы, вы могли бы сделать свой цикл omp for schedule(dynamic), чтобы другие потоки могли получить некоторую слабину во время отправки основного потока, а главный поток мог бы продолжить некоторую работу, когда это будет сделано:

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

void sendrecv(int rank, int sneighbour, int rneighbour, int *data) {
    const int tag=1;
    MPI_Sendrecv(&rank, 1, MPI_INT, sneighbour, tag,
                  data, 1, MPI_INT, rneighbour, tag,
                  MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}

int main(int argc, char **argv) {
    int rank, size;
    int sneighbour;
    int rneighbour;
    int data;
    int got;
    const int n=14;

    MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &got);
    MPI_Comm_size(MPI_COMM_WORLD,&size);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);

    omp_set_nested(1);
    sneighbour = rank + 1;
    if (sneighbour >= size) sneighbour = 0;
    rneighbour = rank - 1;
    if (rneighbour <0 ) rneighbour = size-1;

    #pragma omp parallel 
    {
            #pragma omp master 
            {
                sendrecv(rank, sneighbour, rneighbour, &data);
                printf("Got data from %d\n", data);
            }

            #pragma omp for schedule(dynamic)
            for (int i=0; i<n; i++) {
                int tid = omp_get_thread_num();
                printf("%d:%d working on item %d\n", rank, tid, i);
            }
    }


    MPI_Finalize();
    return 0;
}
0 голосов
/ 13 марта 2012

Вы можете попробовать добавить предложение nowait к вашей конструкции single:

РЕДАКТИРОВАТЬ : ответ на первый комментарий

Если вы включите вложенный параллелизм дляOpenMP, вы можете достичь желаемого, сделав два уровня параллелизма.На верхнем уровне у вас есть две параллельные параллельные секции, одна для связи MPI, другая для локальных вычислений.Этот последний раздел сам может быть распараллелен, что дает вам второй уровень распараллеливания.Только барьеры, выполняющие этот уровень, будут подвержены влиянию барьеров в нем.

#include <iostream>
#include <omp.h>

int main()
{
  int kill = 0;
#pragma omp parallel sections
  {
#pragma omp section
    {
      while (kill == 0){
        /* manage MPI communications */
      }
    }

#pragma omp section
    {
#pragma omp parallel
#pragma omp for
      for (int i = 0; i < 10000 ; ++i) {
        /* your workload */
      }
      kill = 1;
    }
  }
}

Однако вы должны знать, что ваш код сломается, если у вас нет как минимум двух потоков, что означает, что вы 'нарушая предположение о том, что последовательные и распараллеленные версии кода должны делать одно и то же.

Было бы намного понятнее заключить ядро ​​OpenMP в более глобальную схему связи MPI (потенциально используя асинхронную связь для перекрытия связей).с вычислениями).

...