У меня есть фрагмент кода C с использованием MPI следующим образом:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
int main(int argc, char *argv[])
{
float **p=NULL, **buffer=NULL;
int it, nt=3, i, j, k, NP, MYID, nx=1, nz=2, nsrc=3, isrc;
MPI_Init ( &argc, &argv );
MPI_Comm_size ( MPI_COMM_WORLD, &NP );
MPI_Comm_rank ( MPI_COMM_WORLD, &MYID );
p = (float **)calloc(nz,sizeof(float *));
for (i=0;i<nz;i++) p[i] = (float *)calloc(nx,sizeof(float));
buffer = (float **)calloc(nz,sizeof(float *));
for (i=0;i<nz;i++) buffer[i] = (float *)calloc(nx,sizeof(float));
for (it=0; it<nt; it++){
for (isrc=MYID; isrc<nsrc; isrc+=NP){
for (j=0; j<nz; j++){
for (i=0; i<nx; i++){
p[j][i] += 1.5 + (float)(isrc) + (float)(j);
}
}
}
for (k=0;k<nsrc-1;k++){
if (MYID==k){
buffer = p; /*swap pointer*/
}
MPI_Barrier(MPI_COMM_WORLD);
MPI_Bcast(&buffer[0][0],nx*nz,MPI_FLOAT,k,MPI_COMM_WORLD);
MPI_Barrier(MPI_COMM_WORLD);
for (j=0; j<nz; j++){
for (i=0; i<nx; i++){
printf("it=%d,k=%d,Node %d,buffer[%d][%d]=%f\n",it,k,MYID,j,i,buffer[j][i]);
}
}
}
}
MPI_Finalize();
exit(0);
}
Если вы запустите его с 3-мя ядрами mpirun -np 3 ./main
, он выдаст неверные результаты:
it=0,k=0,Node 0,buffer[0][0]=1.500000
it=0,k=0,Node 0,buffer[1][0]=2.500000
it=0,k=1,Node 0,buffer[0][0]=2.500000
it=0,k=1,Node 0,buffer[1][0]=3.500000
it=0,k=0,Node 1,buffer[0][0]=1.500000
it=0,k=0,Node 1,buffer[1][0]=2.500000
it=0,k=1,Node 1,buffer[0][0]=2.500000
it=0,k=1,Node 1,buffer[1][0]=3.500000
it=1,k=0,Node 1,buffer[0][0]=4.000000
it=1,k=0,Node 1,buffer[1][0]=6.000000
it=0,k=0,Node 2,buffer[0][0]=1.500000
it=0,k=0,Node 2,buffer[1][0]=2.500000
it=0,k=1,Node 2,buffer[0][0]=2.500000
it=0,k=1,Node 2,buffer[1][0]=3.500000
it=1,k=0,Node 2,buffer[0][0]=4.000000
it=1,k=0,Node 2,buffer[1][0]=6.000000
it=1,k=1,Node 2,buffer[0][0]=4.000000
it=1,k=0,Node 0,buffer[0][0]=4.000000
it=1,k=0,Node 0,buffer[1][0]=6.000000
it=1,k=1,Node 0,buffer[0][0]=4.000000
it=1,k=1,Node 0,buffer[1][0]=6.000000
it=1,k=1,Node 1,buffer[0][0]=4.000000
it=1,k=1,Node 1,buffer[1][0]=6.000000
it=2,k=0,Node 1,buffer[0][0]=5.500000
it=1,k=1,Node 2,buffer[1][0]=6.000000
it=2,k=0,Node 2,buffer[0][0]=5.500000
it=2,k=0,Node 2,buffer[1][0]=8.500000
it=2,k=0,Node 0,buffer[0][0]=5.500000
it=2,k=0,Node 0,buffer[1][0]=8.500000
it=2,k=0,Node 1,buffer[1][0]=8.500000
it=2,k=1,Node 1,buffer[0][0]=5.500000
it=2,k=1,Node 0,buffer[0][0]=5.500000
it=2,k=1,Node 0,buffer[1][0]=8.500000
it=2,k=1,Node 1,buffer[1][0]=8.500000
it=2,k=1,Node 2,buffer[0][0]=5.500000
it=2,k=1,Node 2,buffer[1][0]=8.500000
Однако, если я изменю строки /*swap pointer*/
на следующие:
for (j=0; j<nz; j++){
for (i=0; i<nx; i++){
buffer[j][i] = p[j][i];
}
}
код сразу дает правильные результаты:
it=0,k=0,Node 0,buffer[0][0]=1.500000
it=0,k=0,Node 0,buffer[1][0]=2.500000
it=0,k=0,Node 1,buffer[0][0]=1.500000
it=0,k=0,Node 1,buffer[1][0]=2.500000
it=0,k=0,Node 2,buffer[0][0]=1.500000
it=0,k=0,Node 2,buffer[1][0]=2.500000
it=0,k=1,Node 0,buffer[0][0]=2.500000
it=0,k=1,Node 0,buffer[1][0]=3.500000
it=0,k=1,Node 1,buffer[0][0]=2.500000
it=0,k=1,Node 1,buffer[1][0]=3.500000
it=0,k=1,Node 2,buffer[0][0]=2.500000
it=0,k=1,Node 2,buffer[1][0]=3.500000
it=1,k=0,Node 2,buffer[0][0]=3.000000
it=1,k=0,Node 0,buffer[0][0]=3.000000
it=1,k=0,Node 0,buffer[1][0]=5.000000
it=1,k=0,Node 1,buffer[0][0]=3.000000
it=1,k=0,Node 1,buffer[1][0]=5.000000
it=1,k=0,Node 2,buffer[1][0]=5.000000
it=1,k=1,Node 2,buffer[0][0]=5.000000
it=1,k=1,Node 0,buffer[0][0]=5.000000
it=1,k=1,Node 0,buffer[1][0]=7.000000
it=1,k=1,Node 1,buffer[0][0]=5.000000
it=1,k=1,Node 1,buffer[1][0]=7.000000
it=1,k=1,Node 2,buffer[1][0]=7.000000
it=2,k=0,Node 2,buffer[0][0]=4.500000
it=2,k=0,Node 2,buffer[1][0]=7.500000
it=2,k=0,Node 0,buffer[0][0]=4.500000
it=2,k=0,Node 0,buffer[1][0]=7.500000
it=2,k=0,Node 1,buffer[0][0]=4.500000
it=2,k=0,Node 1,buffer[1][0]=7.500000
it=2,k=1,Node 0,buffer[0][0]=7.500000
it=2,k=1,Node 1,buffer[0][0]=7.500000
it=2,k=1,Node 2,buffer[0][0]=7.500000
it=2,k=1,Node 2,buffer[1][0]=10.500000
it=2,k=1,Node 0,buffer[1][0]=10.500000
it=2,k=1,Node 1,buffer[1][0]=10.500000
Мой вопрос: почему я просто изменил способ присвоения значений, чтобы изменить правильность выходных данных?
Нет необходимости использовать calloc()
, если вы собираетесь сразу инициализировать все элементы массива. Он инициализирует их все до нуля, в этом нет необходимости, поскольку вы собираетесь их заменить.
for (i<0;i<nz;i++)
это правда? Цикл начинается с i
0 или 1.
@WeatherVane, почему нет смысла? Сначала я использовал buffer = p
, но получил неправильный вывод; но при назначении значений по одному с помощью цикла for дает правильный ответ. Вам не кажется, что мы должны понимать почему? И поэтому я прошу сюда обратиться за вашей помощью.
Потому что i
не был инициализирован. Итак, сравнение это неопределенное поведение.
@WeatherVane Извините, это опечатка. Я изменил i = 0
Теперь вы изменили вопрос. Пожалуйста, опубликуйте Минимальный, полный и проверяемый пример, который показывает проблему. Тогда вы не будете страдать от проблем с опечатками или «чего-то вроде моего кода», которые тратят время впустую.
@Barmar Но инициализация - хорошая привычка, не так ли?
Делать что-то без раздумий - плохая привычка. Я вижу много кода вроде char *foo = NULL;
, а на несколько строк позже - foo = callSomeFunction();
. Инициализация предполагает, что переменную можно было использовать до присвоения, но это было сделано просто по привычке.
С другой стороны, если у вас есть выбор между ненужной инициализацией и забыванием инициализировать что-то, что в ней нуждается, первое предпочтительнее, так как это не приведет к ошибочному поведению.
Однако обратите внимание, что calloc()
не гарантирует создание нулевых указателей. Он заполняет память нулевыми битовыми шаблонами, что не гарантирует того, как представлены нулевые указатели (хотя это бывает в большинстве распространенных архитектур).
Думаю, ваш вопрос сводится к тому, почему
buffer = p;
отличается от
for (j=0; j<nz; j++){
for (i=0; i<nx; i++){
buffer[j][i] = p[j][i];
}
}
buffer = p
- это неглубокая копия, а цикл for - глубокая копия. В мелкой копии мы меняем место в буфере, где хранятся все его элементы. В глубокой копии мы сохраняем место в буфере, где хранятся все его элементы, а затем копируем сюда все элементы p.
Причина, по которой поведение является неожиданным, заключается в том, что в случае неглубокого копирования, когда buffer и p store все их элементы перекрываются, поэтому широковещательные передачи и назначения записываются в одну и ту же память.
Но во втором цикле it
значения p
также меняются, почему buffer
все еще сохраняет исходные значения?
Во-первых, вам нужно разместить ваши (2D) массивы в непрерывной памяти.
В противном случае вы транслируете только первую строку и вызываете переполнение буфера, что приводит к неопределенному поведению.
Имейте в виду, что вы уже отправили кучу вопросов, и основная причина всегда одна и та же: вам нужно выделить свои массивы в непрерывную память (а на SO есть множество примеров).