【问题标题】:What is the displs argument in MPI_Scatterv?MPI_Scatterv 中的 displs 参数是什么?
【发布时间】:2017-05-09 22:15:33
【问题描述】:

来自MPI_Scatterv() 函数的displs 参数被称为“整数数组(长度为组大小)。条目i 指定位移(相对于从中获取传出的sendbuf要处理 i 的数据”。 假设我有sendcounts 参数

int sendcounts[7] = {3, 3, 3, 3, 4, 4, 4};

我的推理方式是displs 数组应始终以值 0 开头,因为第一个条目的位移相对于sendbuf 为 0,因此在我上面的示例中,displs 应该看起来像:

int displs[7] = {0, 3, 6, 9, 13, 17, 21};

正确吗?我知道这是一个微不足道的问题,但由于某种原因,网络根本没有帮助。那里没有很好的例子,因此我的问题。

【问题讨论】:

    标签: c++ c mpi scatterview


    【解决方案1】:

    是的,您的推理是正确的 - 对于连续数据。 MPI_Scatterv 中的displacements 参数的意义在于还允许跨步 数据,这意味着块之间的sendbuf 中存在未使用的内存间隙。

    这是example for contigous data。官方文档里其实包含了很好的examples for strided data

    【讨论】:

      【解决方案2】:

      是的,displacements 为根信息提供了有关将哪些项目发送到特定任务的信息 - 起始项目的偏移量。因此,在最简单的情况下(例如,您会使用 MPI_Scatter,但计数不会均分),这可以立即从计数信息中计算出来:

      displs[0] = 0;              // offsets into the global array
      for (size_t i=1; i<comsize; i++)
          displs[i] = displs[i-1] + counts[i-1];
      

      但没必要这样;唯一的限制是您发送的数据不能重叠。你也可以从后面数数:

      displs[0] = globalsize - counts[0];                 
      for (size_t i=1; i<comsize; i++)
          displs[i] = displs[i-1] - counts[i];
      

      或者任意顺序也可以。

      通常计算会更复杂,因为发送缓冲区和接收缓冲区的类型必须一致但不一定相同 - 如果您发送多维数组,您经常会遇到这种情况例如切片。

      作为简单案例的例子,下面是前向和后向的例子:

      #include <iostream>
      #include <vector>
      #include "mpi.h"
      
      int main(int argc, char **argv) {
          const int root = 0;             // the processor with the initial global data
      
          size_t globalsize;
          std::vector<char> global;       // only root has this
      
          const size_t localsize = 2;     // most ranks will have 2 items; one will have localsize+1
          char local[localsize+2];        // everyone has this
          int  mynum;                     // how many items 
      
          MPI_Init(&argc, &argv); 
      
          int comrank, comsize;
          MPI_Comm_rank(MPI_COMM_WORLD, &comrank);
          MPI_Comm_size(MPI_COMM_WORLD, &comsize);
      
          // initialize global vector
          if (comrank == root) {
              globalsize = comsize*localsize + 1;
              for (size_t i=0; i<globalsize; i++) 
                  global.push_back('a'+i);
          }
      
          // initialize local
          for (size_t i=0; i<localsize+1; i++) 
              local[i] = '-';
          local[localsize+1] = '\0';
      
          int counts[comsize];        // how many pieces of data everyone has
          for (size_t i=0; i<comsize; i++)
              counts[i] = localsize;
          counts[comsize-1]++;
      
          mynum = counts[comrank];
          int displs[comsize];
      
          if (comrank == 0) 
              std::cout << "In forward order" << std::endl;
      
          displs[0] = 0;              // offsets into the global array
          for (size_t i=1; i<comsize; i++)
              displs[i] = displs[i-1] + counts[i-1];
      
          MPI_Scatterv(global.data(), counts, displs, MPI_CHAR, // For root: proc i gets counts[i] MPI_CHARAs from displs[i] 
                       local, mynum, MPI_CHAR,                  // I'm receiving mynum MPI_CHARs into local */
                       root, MPI_COMM_WORLD);                   // Task (root, MPI_COMM_WORLD) is the root
      
          local[mynum] = '\0';
          std::cout << comrank << " " << local << std::endl;
      
          std::cout.flush();
          if (comrank == 0) 
              std::cout << "In reverse order" << std::endl;
      
          displs[0] = globalsize - counts[0];                 
          for (size_t i=1; i<comsize; i++)
              displs[i] = displs[i-1] - counts[i];
      
          MPI_Scatterv(global.data(), counts, displs, MPI_CHAR, // For root: proc i gets counts[i] MPI_CHARAs from displs[i] 
                       local, mynum, MPI_CHAR,                  // I'm receiving mynum MPI_CHARs into local */
                       root, MPI_COMM_WORLD);                   // Task (root, MPI_COMM_WORLD) is the root
      
          local[mynum] = '\0';
          std::cout << comrank << " " << local << std::endl;
      
          MPI_Finalize();
      }
      

      跑步给出:

      In forward order
      0 ab
      1 cd
      2 ef
      3 ghi
      
      In reverse order
      0 hi
      1 fg
      2 de
      3 abc
      

      【讨论】:

      • 我注意到您在 global 中输入来自根级别的值。这样做之后,您不需要向所有级别广播global 吗?我这样想是因为如果global 被用在Scatterv 中,不是所有等级都需要知道global 中的数据吗?
      • 匹配数据类型的正确术语是congruent。一致对我来说听起来很奇怪,但是,我不是以英语为母语的人。
      • @user7331538,MPI_Scatterv 的前四个参数仅供根级别使用。其余的等级在他们的接收缓冲区中接收块,他们不需要访问global 中的数据(它来自根)。
      • 哦,是的,这当然是有道理的。非常感谢!几天前我发布了一个关于分散问题的相关问题,我真的很想得到一些帮助和审查。如果你有时间,如果你能看一下@HristoIliev,我将不胜感激。这里是stackoverflow.com/questions/41317672/…
      猜你喜欢
      • 2013-01-22
      • 2018-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-27
      • 2014-12-30
      • 2021-06-09
      相关资源
      最近更新 更多