MPI_Recv 中的异常情况!接收到的数组最后三个元素错误?
Unusual case in MPI_Recv! The last three elements of received array are wrong?
更新版本
我找到了导致问题的部分,之前在下面进行了解释。我也想和你分享一下情况。我意识到我只犯了一个荒谬的错误。但是,我想知道即使我犯了如下大错误,这个问题是怎么发生的;
我有一个结构定义如下;
#define FP_TYPE double
/* Struct : Nonzero */
struct nonzero{
int row_index;
int column_index;
FP_TYPE value;
};
/* Typedef struct Nonzero */
typedef struct nonzero Nonzero;
我有一个非零数组,要在主处理器的处理器之间分配。为此,我刚刚创建了一个新的数据类型 MY_MPI_NONZERO,如下所示,
#define MPI_FP_TYPE MPI_FLOAT
/**
* Declare an MPI data type for
* + Nonzero Structure
* */
const int number_of_items = 3;
int block_lengths[3] = {1, 1, 1};
MPI_Datatype data_types[3] = {MPI_INT, MPI_INT, MPI_FP_TYPE};
MPI_Datatype MY_MPI_NONZERO;
MPI_Aint offsets[3];
/* Set Offset Array */
offsets[0] = offsetof(Nonzero, row_index);
offsets[1] = offsetof(Nonzero, column_index);
offsets[2] = offsetof(Nonzero, value);
/* Create the Point Struct and Commit it */
MPI_Type_create_struct(number_of_items, block_lengths,
offsets, data_types, &MY_MPI_NONZERO);
MPI_Type_commit(&MY_MPI_NONZERO);
最终,我分发了不同的 Nonzeros 数组,如以下问题的旧版本中详细解释的那样。
现在,问题是基于非零结构和非零数据类型的定义。您可能已经意识到,我在定义 Nonzero 数据类型时错误地使用了 MPI_FLOAT,而在 Nonzero 结构中将 double 用于 Nonzero 结构的值成员。这可能是个问题,但是值只是以一种简单的格式从文件中读取的,比如 1.2、2.0 ...这怎么会导致一个大问题,比如发送数组的最后一些部分是错误的?另外,为什么只有最后三个元素是错误的?
问题的旧版本
我只想让主处理器向其他处理器发送不同的数组。每个处理器都知道传入数组的大小,也知道它将向其他处理器发送多少元素。我有一个数组的分散函数,它保持将发送到每个处理器的元素总数如下,
/* Scatter number of nonzeros per each proc */
MPI_Scatter(no_dist_nonzero, 1, MPI_INT,
&my_no_nonzeros, 1, MPI_INT, MASTER, MPI_COMM_WORLD);
/* Define nonzero array */
if ( my_rank != MASTER )
{
nonzero = (Nonzero *) malloc(
sizeof(Nonzero) * my_no_nonzeros);
}
/**
* Declare an MPI data type for
* + Nonzero Structure
* */
const int number_of_items = 3;
int block_lengths[3] = {1, 1, 1};
MPI_Datatype data_types[3] = {MPI_INT, MPI_INT, MPI_FP_TYPE};
MPI_Datatype MY_MPI_NONZERO;
MPI_Aint offsets[3];
/* Set Offset Array */
offsets[0] = offsetof(Nonzero, row_index);
offsets[1] = offsetof(Nonzero, column_index);
offsets[2] = offsetof(Nonzero, value);
/* Create the Point Struct and Commit it */
MPI_Type_create_struct(number_of_items, block_lengths,
offsets, data_types, &MY_MPI_NONZERO);
MPI_Type_commit(&MY_MPI_NONZERO);
还有代码的剩余部分如下;
if ( my_rank == MASTER )
{
int mem_index = 0;
for ( i = 0; i < comm_size; i++ )
{
if ( i != MASTER )
{
/* Calculate count and size */
int sub_count = no_dist_nonzero[i];
int sub_size = sub_count * sizeof(Nonzero);
Nonzero *sub_nonzero =
(Nonzero *) malloc(sub_size);
/* Divide nonzero array */
mem_index += no_dist_nonzero[i-1];
memcpy(sub_nonzero,
nonzero + mem_index, sub_size);
/* Send nonzeros */
MPI_Send(sub_nonzero, sub_count,
MPI_NONZERO, i,
MASTER, MPI_COMM_WORLD);
}
}
}else
{
MPI_Recv(nonzero, my_no_nonzeros, MPI_NONZERO,
MASTER, MASTER, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
for ( i = 0; i < my_no_nonzeros; i ++ )
{
printf("P[%d] : nonzero[%d] = %.2f\t(%d,%d)\n",
my_rank, i, nonzero[i].value,
nonzero[i].row_index, nonzero[i].column_index);
}
}
这类似于广播,但每个处理器的元素数量不同。现在,当我打印出接收到的元素时,只有每个处理器中每个接收到的数组的最后三个元素 是错误的,例如 0 或不同类型的数字。我创建的一个示例场景用于解释 4 个处理器,主处理器(等级 = 0)除外,其余处理器在其数组中保留 10、11 和 11 个元素,如下所示,
打印语句代表下一行;
处理器[等级]:receivedNonzero[index] = nonzero.value (nonzero.row, nonzero.column)
P[1] : nonzero[0] = 4.00 (5,0)
P[1] : nonzero[1] = 1.00 (5,7)
P[1] : nonzero[2] = 1.00 (6,1)
P[1] : nonzero[3] = 9.00 (6,4)
P[1] : nonzero[4] = 1.00 (7,2)
P[1] : nonzero[5] = 8.00 (7,7)
P[1] : nonzero[6] = 3.00 (8,3)
P[1] : nonzero[7] = 0.00 (8,5)
P[1] : nonzero[8] = 0.00 (1,-2147483648)
P[1] : nonzero[9] = 0.00 (180366288,32731)
P[2] : nonzero[0] = 9.00 (10,2)
P[2] : nonzero[1] = 2.00 (10,3)
P[2] : nonzero[2] = 2.00 (11,5)
P[2] : nonzero[3] = 2.00 (12,0)
P[2] : nonzero[4] = 2.00 (12,7)
P[2] : nonzero[5] = 2.00 (13,1)
P[2] : nonzero[6] = 1.00 (13,3)
P[2] : nonzero[7] = 6.00 (13,5)
P[2] : nonzero[8] = 0.00 (14,32715)
P[2] : nonzero[9] = 0.00 (1215315376,32715)
P[2] : nonzero[10] = 0.00 (1215319296,32715)
P[3] : nonzero[0] = 4.00 (15,0)
P[3] : nonzero[1] = 2.00 (15,4)
P[3] : nonzero[2] = 2.00 (16,6)
P[3] : nonzero[3] = 3.00 (17,0)
P[3] : nonzero[4] = 7.00 (17,3)
P[3] : nonzero[5] = 9.00 (18,1)
P[3] : nonzero[6] = 3.00 (18,4)
P[3] : nonzero[7] = 3.00 (18,7)
P[3] : nonzero[8] = 1141143300351626597783743016932944640301310822732232512436170973423802137351962278027655782681814493455862954554635505069706412465354938627437900810355923222434815569775088619100027795823768424096546808505779224664332855111823098875222717104128.00 (19,1645150208)
P[3] : nonzero[9] = 0.00 (825110830,302186544)
P[3] : nonzero[10] = 0.00 (1,8108)
你知道这种情况出了什么问题吗?即使我在 MPI_Send() 之前打印出 sub_nonzero 数组来检查将数组拆分为子数组是否有错误;没有错...
导致错误的原因是 float
/ double
的数据大小不匹配。使用此错误信息,MPI 将数据写入内存中的错误位置。
这基本上是未定义的行为,任何事情都可能发生,包括鼻恶魔。为什么 - 在实践中 - 只有数据的尾端是错误的,原因如下。 MPI 实际上并不一定关心字节的个别含义。由于数据是连续的,它只关心整体大小——所以它只是传输太少的数据。您在发送方和接收方以相同的方式解释数据,传输的部分看起来很好。
我忍不住要提到这是一个很好的例子,在您的问题中包含 Minimal, Complete, and Verifiable example 是多么重要。
更新版本
我找到了导致问题的部分,之前在下面进行了解释。我也想和你分享一下情况。我意识到我只犯了一个荒谬的错误。但是,我想知道即使我犯了如下大错误,这个问题是怎么发生的;
我有一个结构定义如下;
#define FP_TYPE double
/* Struct : Nonzero */
struct nonzero{
int row_index;
int column_index;
FP_TYPE value;
};
/* Typedef struct Nonzero */
typedef struct nonzero Nonzero;
我有一个非零数组,要在主处理器的处理器之间分配。为此,我刚刚创建了一个新的数据类型 MY_MPI_NONZERO,如下所示,
#define MPI_FP_TYPE MPI_FLOAT
/**
* Declare an MPI data type for
* + Nonzero Structure
* */
const int number_of_items = 3;
int block_lengths[3] = {1, 1, 1};
MPI_Datatype data_types[3] = {MPI_INT, MPI_INT, MPI_FP_TYPE};
MPI_Datatype MY_MPI_NONZERO;
MPI_Aint offsets[3];
/* Set Offset Array */
offsets[0] = offsetof(Nonzero, row_index);
offsets[1] = offsetof(Nonzero, column_index);
offsets[2] = offsetof(Nonzero, value);
/* Create the Point Struct and Commit it */
MPI_Type_create_struct(number_of_items, block_lengths,
offsets, data_types, &MY_MPI_NONZERO);
MPI_Type_commit(&MY_MPI_NONZERO);
最终,我分发了不同的 Nonzeros 数组,如以下问题的旧版本中详细解释的那样。
现在,问题是基于非零结构和非零数据类型的定义。您可能已经意识到,我在定义 Nonzero 数据类型时错误地使用了 MPI_FLOAT,而在 Nonzero 结构中将 double 用于 Nonzero 结构的值成员。这可能是个问题,但是值只是以一种简单的格式从文件中读取的,比如 1.2、2.0 ...这怎么会导致一个大问题,比如发送数组的最后一些部分是错误的?另外,为什么只有最后三个元素是错误的?
问题的旧版本
我只想让主处理器向其他处理器发送不同的数组。每个处理器都知道传入数组的大小,也知道它将向其他处理器发送多少元素。我有一个数组的分散函数,它保持将发送到每个处理器的元素总数如下,
/* Scatter number of nonzeros per each proc */
MPI_Scatter(no_dist_nonzero, 1, MPI_INT,
&my_no_nonzeros, 1, MPI_INT, MASTER, MPI_COMM_WORLD);
/* Define nonzero array */
if ( my_rank != MASTER )
{
nonzero = (Nonzero *) malloc(
sizeof(Nonzero) * my_no_nonzeros);
}
/**
* Declare an MPI data type for
* + Nonzero Structure
* */
const int number_of_items = 3;
int block_lengths[3] = {1, 1, 1};
MPI_Datatype data_types[3] = {MPI_INT, MPI_INT, MPI_FP_TYPE};
MPI_Datatype MY_MPI_NONZERO;
MPI_Aint offsets[3];
/* Set Offset Array */
offsets[0] = offsetof(Nonzero, row_index);
offsets[1] = offsetof(Nonzero, column_index);
offsets[2] = offsetof(Nonzero, value);
/* Create the Point Struct and Commit it */
MPI_Type_create_struct(number_of_items, block_lengths,
offsets, data_types, &MY_MPI_NONZERO);
MPI_Type_commit(&MY_MPI_NONZERO);
还有代码的剩余部分如下;
if ( my_rank == MASTER )
{
int mem_index = 0;
for ( i = 0; i < comm_size; i++ )
{
if ( i != MASTER )
{
/* Calculate count and size */
int sub_count = no_dist_nonzero[i];
int sub_size = sub_count * sizeof(Nonzero);
Nonzero *sub_nonzero =
(Nonzero *) malloc(sub_size);
/* Divide nonzero array */
mem_index += no_dist_nonzero[i-1];
memcpy(sub_nonzero,
nonzero + mem_index, sub_size);
/* Send nonzeros */
MPI_Send(sub_nonzero, sub_count,
MPI_NONZERO, i,
MASTER, MPI_COMM_WORLD);
}
}
}else
{
MPI_Recv(nonzero, my_no_nonzeros, MPI_NONZERO,
MASTER, MASTER, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
for ( i = 0; i < my_no_nonzeros; i ++ )
{
printf("P[%d] : nonzero[%d] = %.2f\t(%d,%d)\n",
my_rank, i, nonzero[i].value,
nonzero[i].row_index, nonzero[i].column_index);
}
}
这类似于广播,但每个处理器的元素数量不同。现在,当我打印出接收到的元素时,只有每个处理器中每个接收到的数组的最后三个元素 是错误的,例如 0 或不同类型的数字。我创建的一个示例场景用于解释 4 个处理器,主处理器(等级 = 0)除外,其余处理器在其数组中保留 10、11 和 11 个元素,如下所示,
打印语句代表下一行;
处理器[等级]:receivedNonzero[index] = nonzero.value (nonzero.row, nonzero.column)
P[1] : nonzero[0] = 4.00 (5,0)
P[1] : nonzero[1] = 1.00 (5,7)
P[1] : nonzero[2] = 1.00 (6,1)
P[1] : nonzero[3] = 9.00 (6,4)
P[1] : nonzero[4] = 1.00 (7,2)
P[1] : nonzero[5] = 8.00 (7,7)
P[1] : nonzero[6] = 3.00 (8,3)
P[1] : nonzero[7] = 0.00 (8,5)
P[1] : nonzero[8] = 0.00 (1,-2147483648)
P[1] : nonzero[9] = 0.00 (180366288,32731)
P[2] : nonzero[0] = 9.00 (10,2)
P[2] : nonzero[1] = 2.00 (10,3)
P[2] : nonzero[2] = 2.00 (11,5)
P[2] : nonzero[3] = 2.00 (12,0)
P[2] : nonzero[4] = 2.00 (12,7)
P[2] : nonzero[5] = 2.00 (13,1)
P[2] : nonzero[6] = 1.00 (13,3)
P[2] : nonzero[7] = 6.00 (13,5)
P[2] : nonzero[8] = 0.00 (14,32715)
P[2] : nonzero[9] = 0.00 (1215315376,32715)
P[2] : nonzero[10] = 0.00 (1215319296,32715)
P[3] : nonzero[0] = 4.00 (15,0)
P[3] : nonzero[1] = 2.00 (15,4)
P[3] : nonzero[2] = 2.00 (16,6)
P[3] : nonzero[3] = 3.00 (17,0)
P[3] : nonzero[4] = 7.00 (17,3)
P[3] : nonzero[5] = 9.00 (18,1)
P[3] : nonzero[6] = 3.00 (18,4)
P[3] : nonzero[7] = 3.00 (18,7)
P[3] : nonzero[8] = 1141143300351626597783743016932944640301310822732232512436170973423802137351962278027655782681814493455862954554635505069706412465354938627437900810355923222434815569775088619100027795823768424096546808505779224664332855111823098875222717104128.00 (19,1645150208)
P[3] : nonzero[9] = 0.00 (825110830,302186544)
P[3] : nonzero[10] = 0.00 (1,8108)
你知道这种情况出了什么问题吗?即使我在 MPI_Send() 之前打印出 sub_nonzero 数组来检查将数组拆分为子数组是否有错误;没有错...
导致错误的原因是 float
/ double
的数据大小不匹配。使用此错误信息,MPI 将数据写入内存中的错误位置。
这基本上是未定义的行为,任何事情都可能发生,包括鼻恶魔。为什么 - 在实践中 - 只有数据的尾端是错误的,原因如下。 MPI 实际上并不一定关心字节的个别含义。由于数据是连续的,它只关心整体大小——所以它只是传输太少的数据。您在发送方和接收方以相同的方式解释数据,传输的部分看起来很好。
我忍不住要提到这是一个很好的例子,在您的问题中包含 Minimal, Complete, and Verifiable example 是多么重要。