11

我有 3 个问题,都与 MPI(C 语言)有关。我认为前两个有相同的答案,但我并不肯定。


问题 1

用于MPI_Dims_create创建二维网格时,返回的维度中哪个维度是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

在二维笛卡尔网格上使用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

前 2 个问题背后的根本问题是我试图将 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

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

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

4

1 回答 1

6

Q1 和 Q2。MPI 中没有维度 X 和维度 Y 之类的东西 - 这些是您为标准中使用的抽象维度提供的标签。MPI 使用编号维度并遵循 C 行主要编号的等级,即在2x3笛卡尔拓扑中(0,0)映射到 rank 0(0,1)映射到 rank 1(0,2)映射到 rank 2(1,0)映射到 rank3等等。

请注意,维度0对应于坐标元组中最右边的元素。这与 C 数组中元素的编号相反,通常是混乱的根源。要创建2x3笛卡尔拓扑,size必须将数组初始化为:

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

您可以将抽象编号的维度映射到您的问题。您可以选择维度0为 X,或者您可以选择维度1- 没关系。

至于MPI_DIMS_CREATE,标准说:

使用适当的可分性算法将维度设置为尽可能接近。

对于dims[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_CREATE

这意味着当MPI_DIMS_CREATE将 的集合分解nprocs为多维网格时,它会将最大的乘数分配给维度的大小0,将下一个分配给维度的大小1,依此类推。像6因素一样2*3,那么MPI_DIMS_CREATE就会回归{ 3, 2 }。如果您MPI_CART_CREATE直接使用 的结果调用MPI_DIMS_CREATE,它将创建一个2x3具有坐标和等级的拓扑:

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

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

  • remain_dims[] = { 1, 0 }将保留维度0并导致 2 个不重叠的 1-d 通信器,每个通信器有 3 个进程;
  • remain_dims[] = { 0, 1 }将保留维度1导致 3 个不重叠的一维通信器,每个通信器有 2 个进程;
  • remain_dims[] = { 0, 0 }不会保留两个维度中的任何一个,并将导致 6 个不重叠的零维通信器,每个通信器都有一个进程。

哪个分区可以称为行分区,哪个分区可以称为列分区,这取决于您和您对笛卡尔维度的标记。


需要注意的一件事是,人们经常在由联网 SMP 或 NUMA 多核节点组成的系统上运行 MPI 代码。在这种情况下,合适的 2D 网格将是nodes x cores. 如果内核的数量是已知的,那么可以很容易地在调用中修复它MPI_DIMS_CREATE

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

MPI_Dims_create(numProcs, 2, size);

这比除以和检查可除性更方便numProcsncores因为如果不除MPI_DIMS_CREATE会发出错误信号。然后是保持维度的分区,即一个与ncoresnumProcs0

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

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

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

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


请注意,在您的代码中,您为in1 (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,因此不应使用它来获取过程坐标和执行拆分。

于 2012-12-09T00:13:50.090 回答