简体   繁体   English

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

[英]Understanding dimensions, coordinates and ordering of processes in a 2D MPI grid

I have 3 questions, all related to MPI (in C). 我有3个问题,都与MPI有关(在C中)。 I think the first 2 have the same answer, but I'm not positive. 我认为前两个有相同的答案,但我不是积极的。


Question 1 问题1

When using MPI_Dims_create to create a 2D grid, which dimension of the returned dimensions is X and which is Y? 使用MPI_Dims_create创建2D网格时,返回尺寸的哪个尺寸为X,哪个是Y?

For example: 例如:

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;

Should it be: 应该是:

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

Or this: 或这个:

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

I tried running this with numbers of processes that seemed like they would give me the answer (eg 6). 我尝试使用多个进程运行它,看起来他们会给我答案(例如6)。 With 6 processes, MPI_Dims_create should either create a grid with 3 rows and 2 columns or 2 rows and 3 columns (unless I've misunderstood the documentation). 使用6个进程, MPI_Dims_create应该创建一个包含3行2列或2行3列的网格(除非我误解了文档)。 For process 3 (of 6), I found that size[0] = 1 and size[1] = 0 (I believe this corresponds to x = 0, y = 1). 对于过程3(6),我发现size[0] = 1size[1] = 0 (我相信这对应于x = 0,y = 1)。 This seems to indicate to me that MPI_Dims_create is creating a grid with 3 rows and 2 columns (because it was 2 rows and 3 columns, process 2 (of 6) should have x = 2, y = 0). 这似乎向我表明MPI_Dims_create正在创建一个包含3行和2列的网格(因为它是2行3列,进程2(6)应该有x = 2,y = 0)。 Any confirmation someone could provide on this would be greatly appreciated. 任何人可以提供的确认将非常感谢。


Question 2 问题2

When using MPI_Cart_coords on a 2D Cartesian grid, which of the returned dimensions is X and which is Y? 在2D笛卡尔网格上使用MPI_Cart_coords时,哪些返回的尺寸是X,哪个是Y?

For example: 例如:

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);

Similar to question 1, my question is, should it be like this 与问题1类似,我的问题是,它应该是这样的

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

or like this 或者像这样

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

I've been searching through the documentation and previous questions on here, but I can't seem to find a direct answer to this question. 我一直在搜索文档和之前的问题,但我似乎无法找到这个问题的直接答案。

The documentation seems to indicate that the first of the two approaches is the correct one, but it doesn't definitively state it. 文档似乎表明这两种方法中的第一种方法是正确的,但它并没有明确说明它。


Question 3 问题3

The underlying question behind the first 2 questions is that I'm trying to split the 2D grid into rows and columns. 前两个问题背后的基本问题是我正在尝试将2D网格分成行和列。 However, when I use MPI_Comm_rank to check the row and column IDs of each process after doing so, I'm getting the processes ordered in an order that doesn't match with what I think the answers to the above questions are. 但是,当我在执行此操作后使用MPI_Comm_rank检查每个进程的行ID和列ID时,我得到的处理顺序与我认为上述问题的答案不符。

Based on above, I expect the processes to be ordered like this: 基于以上所述,我希望这些流程的排序方式如下:

P0 P1
P2 P3
P4 P5

However, using this code (which comes after the above code in my program, so all the above code is accessible from it: I'm trying to separate out my code to make it easier to isolate my questions): 但是,使用此代码(在我的程序中使用上面的代码之后,所以上面的所有代码都可以从它中访问:我正在尝试分离我的代码以便更容易地隔离我的问题):

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);

I'm seeing the following printed: 我看到以下印刷品:

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

which seems to correspond to the following order of processes: 这似乎对应于以下过程顺序:

P0 P2 P4
P1 P3 P5

which is the opposite matrix dimensions (2 rows, 3 columns) from what I was expecting from MPI_Dims_create . 这是与我期望的MPI_Dims_create相反的矩阵维度(2行,3列)。

Assuming my understanding to the first 2 questions is correct (ie that Y is the first dimension returned by those functions), why are the processes (seemingly) being ordered in a different order at this step? 假设我对前两个问题的理解是正确的(即Y是这些函数返回的第一个维度),为什么这个过程(貌似)在这个步骤中以不同的顺序排序?

Q1 and Q2. Q1和Q2。 There are no such things as dimension X and dimension Y in MPI - these are labels that you give to the abstract dimensions used in the standard. MPI中没有尺寸X和尺寸Y这样的东西 - 这些是您给标准中使用的抽象尺寸的标签。 MPI works with numbered dimensions and follows the C row-major numbering of the ranks, ie in a 2x3 Cartesian topology (0,0) maps to rank 0 , (0,1) maps to rank 1 , (0,2) maps to rank 2 , (1,0) maps to rank 3 , and so on. MPI使用编号尺寸并遵循C行主要编号,即在2x3笛卡尔拓扑(0,0)映射到等级0(0,1)映射到等级1(0,2)映射到等级2(1,0)映射到等级3 ,依此类推。

Note that dimension 0 corresponds to the rightmost element in the coordinate tuple. 请注意,维度0对应于坐标元组中最右边的元素。 This is in reverse to the numbering of elements in C arrays and often a source of confusion. 这与C数组中元素的编号相反 ,并且通常是混淆的来源。 To create a 2x3 Cartesian topology the size array would have to be initialised as: 要创建2x3笛卡尔拓扑,必须将size数组初始化为:

int size[2] = { 3, 2 };

It is up to you to map the abstract numbered dimensions to your problem. 您可以将抽象编号尺寸映射到您的问题。 You may chose dimension 0 to be X, or you may chose dimension 1 - it doesn't matter. 您可以选择尺寸0为X,或者您可以选择尺寸1 - 无关紧要。

As for MPI_DIMS_CREATE , the standard says: 至于MPI_DIMS_CREATE ,标准说:

The dimensions are set to be as close to each other as possible, using an appropriate divisibility algorithm. 使用适当的可分性算法将尺寸设置为尽可能彼此接近。

For dims[i] set by the call, dims[i] will be ordered in non-increasing order. 对于dims[i]设定好的电话, dims[i]将在非递增顺序进行排序。

This operation simply returns an array of elements dims[i] that have the following properties (unless the size of one or more dimensions is fixed by setting a non-zero value(s) in dims[] before calling MPI_DIMS_CREATE ): 此操作只返回具有以下属性的元素数组dims[i] (除非通过在调用MPI_DIMS_CREATE之前在dims[]设置非零值来修复一个或多个维度的大小):

  • dims[0] >= dims[1] >= dims[2] >= ...
  • dims[0] * dims[1] * dims[2] == nprocs , where nprocs is the number of processes, specified to MPI_DIMS_CREATE . dims[0] * dims[1] * dims[2] == nprocs ,其中nprocs是指定为MPI_DIMS_CREATE的进程数。

This means that as MPI_DIMS_CREATE decomposes the set of nprocs into an multidimensional grid, it would assign the biggest multiplier to the size of dimension 0 , the next one to the size of dimension 1 , and so on. 这意味着当MPI_DIMS_CREATEnprocs集合分解为多维网格时,它会将最大乘数分配给维度0的大小,下一个乘法器分配给维度1的大小,依此类推。 As 6 factors like 2*3 , then MPI_DIMS_CREATE would return { 3, 2 } . 由于6因素如2*3 ,因此MPI_DIMS_CREATE将返回{ 3, 2 } If you call MPI_CART_CREATE directly with the result from MPI_DIMS_CREATE , it would create a 2x3 topology with coordinates and ranks: 如果直接使用MPI_DIMS_CREATE的结果调用MPI_CART_CREATE ,它将创建一个带坐标和等级的2x3拓扑:

(0,0)=0 (0,1)=1 (0,2)=2
(1,0)=3 (1,1)=4 (1,2)=5

Q3. Q3。 MPI provides a special routine to partition Cartesian topologies - MPI_CART_SUB . MPI提供了一个特殊的例程来划分笛卡尔拓扑MPI_CART_SUB It takes an array of logical flags (integers in C), named remain_dims in the standard. 它需要一组逻辑标志(C中的整数),在标准中名为remain_dims Each non-zero remain_dims[i] means that that dimension i should be retained in the resulting partitioning while separate subcommunicators would be created for any possible combination of the non-retained dimensions. 每个非零的remain_dims[i]意味着应该在结果分区中保留该维度i ,同时为非保留维度的任何可能组合创建单独的子通信器。 For example, given the 2x3 topology: 例如,给定2x3拓扑:

  • remain_dims[] = { 1, 0 } would retain dimension 0 and result in 2 non-overlapping 1-d communicators with 3 processes each; remain_dims[] = { 1, 0 }将保留维度0并导致2个非重叠的1-d通信器,每个3个进程;
  • remain_dims[] = { 0, 1 } would retain dimension 1 result in 3 non-overlapping 1-d communicators with 2 processes each; remain_dims[] = { 0, 1 }将维度1结果保留在3个非重叠的1-d通信器中,每个通信器有2个进程;
  • remain_dims[] = { 0, 0 } would not retain any of the two dimensions and will result in 6 non-overlapping zero-dimensional communicators with a single process in each. remain_dims[] = { 0, 0 }不会保留这两个维度中的任何一个,并且将导致6个非重叠的零维通信器,每个维度中只有一个进程。

Which partitioning you would call row-wise and which one you would call a column-wise partitioning is up to you and your labelling of the Cartesian dimensions. 您可以按行调用哪个分区以及哪个分区称为逐列分区取决于您和笛卡尔维度的标签。


One thing to note is that one often runs MPI codes on systems that consist of networked SMP or NUMA multicore nodes. 需要注意的一点是,通常在由网络SMP或NUMA多核节点组成的系统上运行MPI代码。 In this case a suitable 2D grid would be nodes x cores . 在这种情况下,合适的2D网格将是nodes x cores If the number of cores is known, then one can easily fix it in the call to MPI_DIMS_CREATE : 如果核心数已知,那么可以在调用MPI_DIMS_CREATE轻松修复它:

int size[2] = { ncores, 0 };

MPI_Dims_create(numProcs, 2, size);

This is a more convenient than dividing numProcs by ncores and checking for divisibility as MPI_DIMS_CREATE would signal an error if ncores does not divide numProcs . 这比将numProcs除以ncores并检查可分性更方便,因为如果ncores不分割numProcsMPI_DIMS_CREATE将发出错误信号。 Then a partitioning that keeps dimension 0 , ie one with 然后是一个保持维度0的分区,即一个分区

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

would create subcommunicators that contain processes on the same node, while 会创建包含同一节点上的进程的子通信器

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

would create subcommunicators that contain no two processes from the same node. 将创建不包含来自同一节点的两个进程的子通信器。


Note that in your code you have specified a value of 1 (true) to the reorder parameter in MPI_CART_CREATE . 请注意,在代码中,您已为MPI_CART_CREATEreorder参数指定值1 (true) This might lead to processes having different ranks in MPI_COMM_WORLD and in the Cartesian communicator. 这可能会导致进程在MPI_COMM_WORLD和笛卡尔通信器中具有不同的等级。 Hence there is no guarantee that the following lines of code do what you expect them to: 因此,无法保证以下代码行符合您的预期:

MPI_Cart_coords(cart_comm, myProcID, 2, coords);
                           ^^^^^^^^
...
printf("Process: %d\trowID: %d\tcolID: %d\n", myProcID, row_id, col_id);
                                              ^^^^^^^^

myProcID was obtained from MPI_COMM_WORLD and might actually differ from the new rank in cart_comm , hence it should not be used to obtain the process coordinates and to perform the splits. myProcID是从MPI_COMM_WORLD获得的,实际上可能与MPI_COMM_WORLD的新排名cart_comm ,因此它不应该用于获取进程坐标和执行拆分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM