了解2D MPI网格中的过程的尺寸,坐标和顺序

时间:2012-12-08 22:43:40

标签: c mpi

我有3个问题,都与MPI有关(在C中)。我认为前两个有相同的答案,但我不是正面的。


问题1

使用MPI_Dims_create创建2D网格时,返回尺寸的哪个尺寸为X,哪个为Y?

例如:

int numProcs = -1, myProcID = -1;
MPI_Comm_rank(MPI_COMM_WORLD, &myProcID);
MPI_Comm_size(MPI_COMM_WORLD, &numProcs);

int size[2];
size[0] = size[1] = 0;
MPI_Dims_create(numProcs, 2, size);
int numProcs_y = -1, numProcs_x = 1;

应该是:

numProcs_y = size[0];
numProcs_x = size[1];

或者这个:

numProcs_x = size[0];
numProcs_y = size[1];

我尝试使用多个进程来运行它,似乎他们会给我答案(例如6)。对于6个进程,MPI_Dims_create应该创建一个包含3行2列或2行3列的网格(除非我误解了文档)。对于过程3(6),我发现size[0] = 1size[1] = 0(我相信这对应于x = 0,y = 1)。这似乎向我表明MPI_Dims_create正在创建一个包含3行和2列的网格(因为它是2行3列,进程2(6)应该有x = 2,y = 0)。任何有关此人可以提供的确认将非常感谢。


问题2

在2D笛卡尔网格上使用MPI_Cart_coords时,哪些返回的尺寸为X,哪个为Y?

例如:

int periodic[2];
periodic[0] = periodic[1] = 0; // no wrap around
MPI_Comm cart_comm;
int coords[2];

// using size from question 1
MPI_Cart_create(MPI_COMM_WORLD, 2, size, periodic, 1, &cart_comm);
MPI_Cart_coords(cart_comm, myProcID, 2, coords);

与问题1类似,我的问题是,它应该是这样吗

myProcID_y = coords[0];
myProcID_x = coords[1];

或者像这样

myProcID_x = coords[0];
myProcID_y = coords[1];

我一直在搜索文档和之前的问题,但我似乎无法直接回答这个问题。

文档似乎表明这两种方法中的第一种方法是正确的,但它并没有明确说明它。


问题3

前两个问题背后的基本问题是我试图将2D网格分成行和列。但是,当我在执行此操作后使用MPI_Comm_rank检查每个流程的行ID和列ID时,我会按照与我认为的答案不匹配的顺序获取流程以上问题是。

基于以上所述,我希望这些流程的排序方式如下:

P0 P1
P2 P3
P4 P5

但是,使用此代码(在我的程序中使用上面的代码之后,所以上面的所有代码都可以从它中访问:我试图分离我的代码以便更容易地分离我的问题):

MPI_Comm row_comm, col_comm;
int row_id = -1, col_id = -1;
// ** NOTE: My use of myProcID_y and myProcID_x here are based 
// on my understanding of the previous 2 questions ... if my 
// understanding to one/both of those is wrong, then obviously the assignments 
// here are wrong too.
// create row and column communicators based on my location in grid
MPI_Comm_split(cart_comm, myProcID_y, myProcID_x, &row_comm);
MPI_Comm_split(cart_comm, myProcID_x, myProcID_y, &col_comm);

// get row and column ID for each process
MPI_Comm_rank(row_comm, &row_id);
MPI_Comm_rank(col_comm, &col_id);
printf("Process: %d\trowID: %d\tcolID: %d\n", myProcID, row_id, col_id);

我看到以下内容打印出来了:

Process: 0 rowID: 0        colID: 0
Process: 1 rowID: 1        colID: 0
Process: 2 rowID: 0        colID: 1
Process: 3 rowID: 1        colID: 1
Process: 4 rowID: 0        colID: 2
Process: 5 rowID: 1        colID: 2

似乎对应于以下过程顺序:

P0 P2 P4
P1 P3 P5

这是与我期望的MPI_Dims_create相反的矩阵维度(2行,3列)。

假设我对前两个问题的理解是正确的(即Y是这些函数返回的第一个维度),为什么这个过程(貌似)在这一步中以不同的顺序排序?

1 个答案:

答案 0 :(得分:5)

Q1和Q2。 MPI中没有尺寸X和尺寸Y这样的东西 - 这些是您给标准中使用的抽象尺寸的标签。 MPI使用编号维度并遵循排名的C行主要编号,即在2x3笛卡尔拓扑(0,0)地图中排名0(0,1)映射到排名{ {1}},1映射到排名(0,2)2映射到排名(1,0),依此类推。

请注意,维度3对应于坐标元组中的最右侧元素。这是 reverse 到C数组中元素的编号,通常是混淆的来源。要创建0笛卡尔拓扑,必须将2x3数组初始化为:

size

您可以将抽象编号尺寸映射到您的问题。您可以选择维度int size[2] = { 3, 2 }; 为X,或者您可以选择维度0 - 这无关紧要。

至于1,标准说:

  

使用适当的可分性算法将尺寸设置为尽可能接近彼此。

     

对于通过调用设置的MPI_DIMS_CREATEdims[i]将按非递增顺序排序。

此操作只返回具有以下属性的元素dims[i]数组(除非通过在调用dims[i]之前设置非零值来修复一个或多个维的大小dims[]):

  • MPI_DIMS_CREATE
  • dims[0] >= dims[1] >= dims[2] >= ...,其中dims[0] * dims[1] * dims[2] == nprocs是指定为nprocs的进程数。

这意味着当MPI_DIMS_CREATEMPI_DIMS_CREATE的集合分解为多维网格时,它会将最大的乘数分配给维nprocs的大小,下一个大小为维度0,依此类推。由于16因素,2*3会返回MPI_DIMS_CREATE。如果您使用{ 3, 2 }的结果直接致电MPI_CART_CREATE,则会创建一个包含坐标和排名的MPI_DIMS_CREATE拓扑:

2x3

Q3。 MPI提供了一个特殊的例程来划分笛卡尔拓扑 - (0,0)=0 (0,1)=1 (0,2)=2 (1,0)=3 (1,1)=4 (1,2)=5 。它需要一组逻辑标志(C中的整数),在标准中名为MPI_CART_SUB。每个非零remain_dims表示该维度remain_dims[i]应保留在生成的分区中,而为非保留维度的任何可能组合创建单独的子通信器。例如,给定i拓扑:

  • 2x3会保留维度remain_dims[] = { 1, 0 }并导致2个非重叠的1-d通信器,每个3个进程;
  • 0会将尺寸remain_dims[] = { 0, 1 }保留在3个非重叠的1-d传播者中,每个传播者有2个进程;
  • 1不会保留这两个维度中的任何一个,并且会导致6个不重叠的零维通信器,每个维度都包含一个进程。

您将逐行调用哪个分区以及您将其称为逐列分区的分区取决于您和您对笛卡尔维度的标记。


需要注意的一点是,通常在由网络SMP或NUMA多核节点组成的系统上运行MPI代码。在这种情况下,合适的2D网格将是remain_dims[] = { 0, 0 }。如果已知核心数,则可以在调用nodes x cores

时轻松修复核心数
MPI_DIMS_CREATE

这比int size[2] = { ncores, 0 }; MPI_Dims_create(numProcs, 2, size); numProcsncores更方便,如果MPI_DIMS_CREATE没有ncoresnumProcs,则0会发出错误信号。然后是一个保持维度int remain_dims[2] = { 1, 0 }; 的分区,即一个带

的分区
int remain_dims[2] = { 0, 1 };

将创建包含同一节点上的进程的子通信器,而

1 (true)

将创建不包含来自同一节点的两个进程的子通信器。


请注意,在您的代码中,您为reorder中的MPI_CART_CREATE参数指定了值MPI_COMM_WORLD。这可能导致进程在MPI_Cart_coords(cart_comm, myProcID, 2, coords); ^^^^^^^^ ... printf("Process: %d\trowID: %d\tcolID: %d\n", myProcID, row_id, col_id); ^^^^^^^^ 和笛卡尔传播者中具有不同的等级。因此,无法保证以下代码行符合您的预期:

myProcID

MPI_COMM_WORLD是从cart_comm获得的,可能实际上与{{1}}中的新排名不同,因此不应该用它来获取流程坐标和执行拆分。