2

我试图通过一个小例子来理解 MPI-Function `MPI_Fetch_and_op() 并遇到了我想理解的奇怪行为。

在该示例中,等级为 0 的进程正在等待,直到进程 1..4 各自将 result 的值加一,然后再继续。

0在函数中使用 assert的默认值时,MPI_Win_lock_all()有时(10 次中有 1 次)会出现无限循环,即将result[0]MASTER 中的值更新为 3。终端输出类似于以下代码片段:

result: 3
result: 3
result: 3
...

根据文档,功能 MPI_Fetch_and_op 是原子的。

此操作相对于其他“累积”操作是原子的。

第一个问题: 为什么不将 的值更新result[0]为 4?


如果我将值更改assertMPI_MODE_NOCHECK它似乎工作

第二个问题: 为什么它与MPI_MODE_NOCHECK

根据文档,我认为这意味着必须以不同的方式组织互斥。有人可以解释文档中的段落MPI_Win_lock_all()吗?

MPI_MODE_NOCHECK

当调用者持有窗口锁时,没有其他进程持有或将尝试获取冲突锁。当通过其他方式实现互斥时,这很有用,但仍需要可能附加到 lock 和 unlock 调用的一致性操作。

提前致谢!

示例程序:

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>

#define MASTER 0

int main(int argc, char *argv[])
{
  MPI_Init(&argc, &argv);
  MPI_Comm comm = MPI_COMM_WORLD;
  int r, p;
  MPI_Comm_rank(comm, &r);
  MPI_Comm_size(comm, &p);
  printf("Hello from %d\n", r);
  int result[1] = {0};
  //int assert = MPI_MODE_NOCHECK;
  int assert = 0;
  int one = 1;
  MPI_Win win_res;
  MPI_Win_allocate(1 * sizeof(MPI_INT), sizeof(MPI_INT), MPI_INFO_NULL, comm, &result[0], &win_res);
  MPI_Win_lock_all(assert, win_res);
  if (r == MASTER) {
    result[0] = 0;
    do{
      MPI_Fetch_and_op(&result, &result , MPI_INT, r, 0, MPI_NO_OP, win_res);  
      printf("result: %d\n", result[0]);
    } while(result[0] != 4);
    printf("Master is done!\n");
  } else {
    MPI_Fetch_and_op(&one, &result, MPI_INT, 0, 0, MPI_SUM, win_res);
  }
  MPI_Win_unlock_all(win_res);
  MPI_Win_free(&win_res);
  MPI_Finalize();
  return 0;
}

使用以下 Makefile 编译:

MPICC = mpicc
CFLAGS = -g -std=c99 -Wall -Wpedantic -Wextra

all: fetch_and

fetch_and: main.c
    $(MPICC) $(CFLAGS) -o $@ main.c

clean:
    rm fetch_and

run: all
    mpirun -np 5 ./fetch_and
4

1 回答 1

1

你的代码对我有用,没有改变。但这可能是巧合。你的代码有很多问题。让我指出我所看到的:

  • 您硬编码了测试中的进程数result[0] != 4
  • 您将主值硬编码为MPI_Fetch_and_op(&one, &result, MPI_INT, 0
  • 传递与更新和结果相同的地址对我来说似乎很危险:MPI_Fetch_and_op(&result, &result
  • 我的编译器抱怨第一个参数,因为它实际上是一个int**(实际上int (*)[1]
  • 我不知道为什么你在第二个参数上没有得到同样的抱怨,
  • ....但无论如何我对第二个参数并不满意,因为 fetch 操作写入您指定为窗口缓冲区的内存中。我想这里缺乏连贯性可以拯救你。
  • 你用初始化窗口,result[0] = 0;但我不认为这与窗口是一致的,所以你可能只是幸运。
  • 我认为这MPI_Win_allocate(1 * sizeof(MPI_INT), sizeof(MPI_INT), MPI_INFO_NULL, comm, &result[0]也将是某种内存损坏,因为result这里是一个输出,但它是一个静态分配的数组。
  • 类似地,Win_free尝试释放内存缓冲区,但如前所述,这是一个静态缓冲区,所以再次:内存损坏。
  • 您的使用Win_lock_all不合适:这意味着一个进程将窗口锁定在所有目标上。没有任何竞争锁!!您仅将窗口锁定在一个进程上,但来自所有可能的来源。我会用普通的锁。
  • 最后,RMA 调用是非阻塞的。通常,一致性由 Win_fence 或 Win_unlock 实现。但是因为您使用的是长寿命锁,所以您需要在后面Fetch_and_op加上MPI_Win_flush_local.

好的,这是十几个,嗯,不太理想的编程案例。尽管如此,在我的设置中它仍然有效。(有时。有时它也会挂起。)所以您可能需要稍微清理一下您的代码。你的逻辑是正确的,但你的实际实现不是。

于 2020-05-30T17:50:24.940 回答