【问题标题】:sending c struct via MPI fails partially通过 MPI 发送 c 结构部分失败
【发布时间】:2013-08-29 11:44:42
【问题描述】:

我正在使用MPI_Type_create_struct() 发送一个(粒子)结构,例如here,或者详细解释here。 我正在收集所有将进入特定进程的粒子,memcpy() 将它们放入发送缓冲区,MPI_Isend() 它们。

到目前为止,一切都很好。 MPI_Iprob()'ing 消息给了我正确的粒子数。 所以我MPI_Recv() 缓冲区并提取数据(现在甚至通过一个一个复制结构)。不管我发送多少粒子,只有第一个粒子的数据是正确的。

可能出现三种错误:

  1. MPI_Type_create_struct() 没有为我的结构创建正确的映射,因为我在第一个链接中使用了offset of()。也许我的结构包含一个不可见的填充,如第二个链接中所述。
  2. 在将粒子复制到发送缓冲区并从接收缓冲区返回时,我犯了一些简单的错误(我确实打印了发送缓冲区 - 它有效 - 但也许我忽略了一些东西)
  3. 完全不同的东西。

(对不起,代码的呈现非常丑陋,我无法以下降的方式呈现它。你会发现代码 here - 该行已经被标记 - 在 Github 上也是如此!)

这里是mpi数据类型的构造,

typedef struct {
        int                 ID;
        double              x[DIM];
} pchase_particle_t;

 const int           items = 2;
 int                 block_lengths[2] = {1, DIM};
 MPI_Datatype        mpi_types[2] = {MPI_INT, MPI_DOUBLE};
 MPI_Aint            offsets[2];
 offsets[0] = offsetof(pchase_particle_t, ID);
 offsets[1] = offsetof(pchase_particle_t, x);
 MPI_Type_create_struct(items, block_lengths, offsets, mpi_types, &W->MPI_Particle);
 MPI_Type_commit(&W->MPI_Particle);

发送

/* handle all mpi send/recv status data */
MPI_Request        *send_request = P4EST_ALLOC(MPI_Request, W->p4est->mpisize);
MPI_Status         *recv_status = P4EST_ALLOC(MPI_Status, W->p4est->mpisize);
/* setup send/recv buffers */
pchase_particle_t **recv_buf = P4EST_ALLOC(pchase_particle_t *, num_senders);
pchase_particle_t **send_buf = P4EST_ALLOC(pchase_particle_t *, num_receivers);
int                 recv_count = 0, recv_length, flag, j;

/* send all particles to their belonging procs */
for (i = 0; i < num_receivers; i++) {
  /* resolve particle list for proc i */
  sc_list_t          *tmpList = *((sc_list_t **) sc_array_index(W->particles_to, receivers[i]));
  pchase_particle_t * tmpParticle;
  int                 send_count = 0;

  /* get space for the particles to be sent */
  send_buf[i] = P4EST_ALLOC(pchase_particle_t, tmpList->elem_count);

  /* copy all particles into the send buffer and remove them from this proc */
  while(tmpList->first != NULL){
    tmpParticle = sc_list_pop(tmpList);
    memcpy(send_buf[i] + send_count * sizeof(pchase_particle_t), tmpParticle, sizeof(pchase_particle_t));
    /* free particle */
    P4EST_FREE(tmpParticle);
    /* update particle counter */ 
    send_count++;
  }

  /* print send buffer */
  for (j = 0; j < send_count; j++) {
    pchase_particle_t  *tmpParticle = send_buf[i] + j * sizeof(pchase_particle_t);
    printf("[pchase %i sending] particle[%i](%lf,%lf)\n", W->p4est->mpirank, tmpParticle->ID, tmpParticle->x[0], tmpParticle->x[1]);
  }

  printf("[pchase %i sending] particle count: %i\n", W->p4est->mpirank, send_count);
  /* send particles to right owner */
  mpiret = MPI_Isend(send_buf[i], send_count, W->MPI_Particle, receivers[i], 13, W->p4est->mpicomm, &send_request[i]);
  SC_CHECK_MPI(mpiret);
}

和接收。

recv_count = 0;
/* check for messages until all arrived */
while (recv_count < num_senders) {
  /* probe if any of the sender has already sent his message */
  for (i = 0; i < num_senders; i++) {
    MPI_Iprobe(senders[i], MPI_ANY_TAG, W->p4est->mpicomm,
        &flag, &recv_status[i]);
    if (flag) {
      /* resolve number of particles receiving */
      MPI_Get_count(&recv_status[i], W->MPI_Particle, &recv_length);
      printf("[pchase %i receiving message] %i particles arrived from sender %i with tag %i\n",
          W->p4est->mpirank, recv_length, recv_status[i].MPI_SOURCE, recv_status[i].MPI_TAG);
      /* get space for the particles to be sent */
      recv_buf[recv_count] = P4EST_ALLOC(pchase_particle_t, recv_length);
      /* receive a list with recv_length particles */ 
      mpiret = MPI_Recv(recv_buf[recv_count], recv_length, W->MPI_Particle, recv_status[i].MPI_SOURCE,
          recv_status[i].MPI_TAG, W->p4est->mpicomm, &recv_status[i]);
      SC_CHECK_MPI(mpiret);

      /*
       * insert all received particles into the
       * push list
       */
      pchase_particle_t  *tmpParticle;
      for (j = 0; j < recv_length; j++) {
        /*
         * retrieve all particle details from
         * recv_buf
         */
        tmpParticle = recv_buf[recv_count] + j * sizeof(pchase_particle_t);
        pchase_particle_t *addParticle = P4EST_ALLOC(pchase_particle_t,1);
        addParticle->ID=tmpParticle->ID;
        addParticle->x[0] = tmpParticle->x[0];
        addParticle->x[1] = tmpParticle->x[1];

        printf("[pchase %i receiving] particle[%i](%lf,%lf)\n",
            W->p4est->mpirank, addParticle->ID, addParticle->x[0], addParticle->x[1]);
        /* push received particle to push list and update world counter */
        sc_list_append(W->particle_push_list, addParticle);
        W->n_particles++;
      }
      /* we received another particle list */
      recv_count++;
    }
  }
}

编辑:重新缩进.. 编辑:只有第一个粒子的数据是正确的,意味着它的所有属性(ID 和坐标)与发送粒子的属性相同。然而,其他的用零初始化,即 ID=0,x[0]=0.0,x[1]=0.0。也许这是解决方案的提示。

【问题讨论】:

  • 这并不能真正回答您的问题,但如果您仍要复制数据,那么我建议您改用MPI_Pack()

标签: c struct mpi


【解决方案1】:

您的指针算术存在错误。 send_buf[i] 已经是pchase_particle_t * 类型,因此send_buf[i] + j * sizeof(pchase_particle_t) 不指向i-th 缓冲区的j-th 元素,而是指向j * sizeof(pchase_particle_t)-th 元素。因此,您的粒子不会连续存储在内存中,而是由 sizeof(pchase_particle_t) - 1 空数组元素分隔。这些被发送而不是正确的粒子,因为MPI_Send 调用连续访问缓冲内存。这同样适用于接收方的代码。

您在发送方代码中看不到错误,因为您的调试打印使用了相同的错误指针算法,因此使用相同的步幅访问内存。我猜您的发送计数很小,并且您在数据段堆上分配了内存,否则您应该在数据打包过程的早期就收到SIGSEGV 用于越界数组访问(例如在memcpy 部分) .

解决方法:不要将数组索引乘以sizeof(pchase_particle_t)

【讨论】:

    猜你喜欢
    • 2012-10-23
    • 2014-06-02
    • 2013-12-12
    • 2018-11-09
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    • 2012-08-10
    • 2010-12-16
    相关资源
    最近更新 更多