MPI_Recv中不寻常的案例!收到的数组的最后三个元素是错误的?

时间:2017-02-04 15:10:49

标签: c mpi openmpi

更新版本

我发现了导致问题的部分,这在前面已经解释过了。我也想和你分享一下情况。我意识到我做的只有一个荒​​谬的错误。但是,我想知道这个问题是如何发生的,即使我犯了一个大错误:

我有一个结构定义如下;

#define FP_TYPE double

/* Struct : Nonzero */
struct nonzero{
    int row_index;
    int column_index;
    FP_TYPE value;
};

/* Typedef struct Nonzero */
typedef struct nonzero Nonzero;

我有一个Nonzeros数组要在主处理器的处理器之间分配。为此,我刚刚创建了一个新的数据类型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);

最后,我详细分发了以下问题的旧版本中解释的不同非零数组。

现在,问题是基于Nonzero结构和非零数据类型的定义。您可能已经意识到,我在定义非零数据类型时错误地使用了MPI_FLOAT,而在Nonzero结构的值成员中使用非零结构的double。这可能是一个问题,但是只能以简单的格式从文件中读取值,例如1.2,2.0 ......如何导致一个大问题,例如发送数组的最后一些部分是错误的?另外,为什么只有最后三个元素是错误的?

问题的旧版本

我只是希望主处理器将不同的阵列发送到其他处理器。每个处理器都知道传入数组的大小是多少,并且master也知道它将向其他元素发送多少元素。我有一个数组的分散函数,它保持总元素的数量将被发送到每个处理器,如下所示,

/* 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个处理器和master(rank = 0)处理器,处理器保留处理器在其阵列中保留10,11和11个元素,如下所示,

Print语句代表下一行;

处理器[rank]: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数组,以检查是否存在将数组拆分为子数组的错误;没有错......

1 个答案:

答案 0 :(得分:1)

导致错误的原因是float / double的数据大小不匹配。有了这个错误信息,MPI会将数据写入内存中的错误位置。

这基本上是未定义的行为,任何事情都可能发生,包括鼻子恶魔。实际上 - 只有数据的十分之一是错误的,其原因在于以下内容。 MPI实际上并不一定关心字节的个别含义。由于数据是连续的,它只关心整体大小 - 因此它只传输太少的数据。您在发送方和接收方以相同的方式解释数据,传输的部分看起来很好。

我不禁提到这是一个很好的例子,在你的问题中加入Minimal, Complete, and Verifiable example是多么重要。