У меня возник вопрос при изучении примера функции mpi send и recv

#c #ubuntu #mpi

#c #ubuntu #mpi

Вопрос:

Приведенный ниже код показывает пример pingpong при изучении функции отправки и ответа. Но я не понимаю из parter_rank .

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

int main(int argc, char** argv) {
const int PING_PONG_LIMIT = 10;

// Initialize the MPI environment
MPI_Init(NULL, NULL);
// Find out rank, size
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, amp;world_rank);
int world_size;
MPI_Comm_size(MPI_COMM_WORLD, amp;world_size);
 
// We are assuming 2 processes for this task
if (world_size != 2) {
fprintf(stderr, "World size must be two for %sn", argv[0]);
MPI_Abort(MPI_COMM_WORLD, 1);
}

int ping_pong_count = 0;
int partner_rank = (world_rank   1) % 2;
while (ping_pong_count < PING_PONG_LIMIT) {
  if (world_rank == ping_pong_count % 2) {
  // Increment the ping pong count before you send it
  ping_pong_count  ;
  MPI_Send(amp;ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD);
  printf("%d sent and incremented ping_pong_count %d to %dn", world_rank, ping_pong_count, partner_rank);
  } else {
  MPI_Recv(amp;ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD,
           MPI_STATUS_IGNORE);
  printf("%d received ping_pong_count %d from %dn", 
         world_rank, ping_pong_count, partner_rank);}
  }
  MPI_Finalize();
}
  

Q1. Очевидно, что MPI_Comm_rank выше определяет его как world_rank, но я не понимаю, что означает partner_rank ниже.
В чем разница между двумя рангами?

Q2. Когда я не понимаю if (world_rank == ping_pong_count % 2) , не можем ли мы просто написать «rank == 0» и «rank == 1»? Зачем вы ввели туда арифметический оператор?

Я был бы признателен за ваш комментарий.

Комментарии:

1. если вы этого не if (world_rank == ping_pong_count %2) сделаете, ваша программа (в том виде, в котором она написана в настоящее время) будет выполнять пинг-пинг вместо пинг-понга. что касается стиля, я думаю, что эта программа немного запутана, но может быть легко адаптирована для выполнения любого четного числа задач MPI.

2. Первый счет = 0, и поэтому, если (world_rank=(pingpong_count 1)%2) становится world_rank =0 . Становится ли этот рейтинг партнера равным 1?

3. partner_rank является константой: 0 партнер является 1 и 1 партнер является 0 . Чтобы выполнить пинг-понг, ранг 0 должен отправлять, recv, отправлять, recv, …, а ранг 1 должен отправлять, отправлять, отправлять, отправлять, отправлять… Это обоснование для этого if

4. спасибо за ваш комментарий, могу я задать еще один вопрос? Как мне реализовать этот код без partner_rank? Когда мы удалили parter_rank и запустили параметр, который включал parter_rank , только rank =0 отправил сообщение, и программа не завершилась должным образом.

5. мой совет — не делайте этого. но если вы действительно этого хотите, вам нужно будет развернуть свой цикл, например if (rank == 0) { MPI_Send(); MPI_Recv();} else {MPI_Recv(); MPI_Send();}

Ответ №1:

Причиной этой, казалось бы, ненужной арифметики и введения дополнительных переменных ранга является симметрия кода. То есть, поскольку наиболее распространенный тип программ MPI — это тот, который выполняется как несколько копий одной и той же программы, симметричный код означает код, в котором нет условных операторов, сравнивающих ранг с конкретными константами. Почему это важно? Потому что это делает код более гибким и понятным.

Сравните следующие две эквивалентные спецификации того, как выглядит двухуровневая программа MPI для пинг-понга:

Спецификация 1

  • ранг 0 отправляет сообщение рангу 1
  • затем ранг 1 отправляет сообщение обратно в ранг 0
  • процесс повторяется N раз

Реализация этой спецификации в псевдокоде, подобном C, может быть:

 loop N times {
   if (rank == 0) {
      MPI_Send to 1
      MPI_Recv from 1
   }
   else if (rank == 1) {
      MPI_Recv from 0
      MPI_Send to 0
   }
}
  

Спецификация 2

  • каждый ранг получает от другого ранга
  • затем каждый ранг отправляется в другой ранг
  • процесс повторяется N-1 раз
  • кроме того, ранг 0 запускает процесс, отправляя другому рангу, и завершает процесс, получая последнее сообщение от другого ранга

Возможная реализация в псевдокоде:

 other_rank = 1 - rank

if (rank == 0) {
   MPI_Send to other_rank
}

loop N-1 times {
   MPI_Recv from other_rank
   MPI_Send to other_rank
}

if (rank == 0) {
   MPI_Recv from other_rank
}
  

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

Что, если мы хотим расширить систему и иметь не два, а три ранга, передающие сообщения в кольце. Мы хотим, чтобы ранг 0 передавал сообщение в ранг 1, который затем передает его в ранг 2, который, в свою очередь, возвращает его в ранг 0. Расширение спецификации 1 приводит к:

  • ранг 0 отправляет на ранг 1
  • ранг 0 получает из ранга 2
  • ранг 1 получает из ранга 0
  • ранг 1 отправляет на ранг 2
  • ранг 2 получает из ранга 1
  • ранг 2 отправляет на ранг 0

В псевдокоде:

 loop N times {
   if (rank == 0) {
      MPI_Send to 1
      MPI_Recv from 2
   }
   else if (rank == 1) {
      MPI_Recv from 0
      MPI_Send to 2
   }
   else if (rank == 2) {
      MPI_Recv from 1
      MPI_Send to 0
   }
}
  

Попробуйте расширить это до четырех рангов, затем до пяти.

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

  • каждый ранг получает от предыдущего ранга
  • каждый ранг отправляет на следующий ранг
  • ранг 0 запускает процесс путем отправки первого сообщения и завершает процесс получением последнего сообщения

В псевдокоде:

 prev_rank = (rank - 1   #ranks) % #ranks
next_rank = (rank   1) % #ranks

if (rank == 0) {
   MPI_Send to next_rank
}

loop N-1 times {
   MPI_Recv from prev_rank
   MPI_Send to next_rank
}

if (rank == 0) {
   MPI_Recv from prev_rank
}
  

Стоит отметить, что спецификация 2 — это не что иное, как конкретный случай этой общей спецификации с #ranks равным 2 . В этом случае prev_rank и next_rank оба равны (rank 1) % 2 , т. Е. Имеют один и тот же ранг. Кроме того, (rank 1) % 2 и 1 - rank одинаковы, когда ранг принимает значения, которые являются либо 0 или 1 .

Надеюсь, теперь вы видите мотивацию, стоящую не за жестким кодированием конкретных действий для конкретных рангов, а за использованием локальной арифметики для определения того, что делать. В вашем случае каждое четное значение сообщения ping увеличивается на ранг 0, а каждое нечетное значение сообщения ping — на ранг 1, но что, если вы расширите его до кольца рангов? if (rank == ping_value % #ranks) ping_value ; вроде бы все правильно и работает с любым количеством рангов.

Комментарии:

1. Спасибо за ваш любезный ответ. Спасибо вам, это помогло мне понять код!!!!!

2. Чтобы задать еще один вопрос, можно ли понимать значение ‘local’ как то же значение, что и ‘regional’, используемое в ‘local variable’?

3. Локально, поскольку не включает знания о программе MPI в целом, только текущий ранг и его непосредственное окружение.