MPI_Fetch_and_op

时间:2017-05-23 15:09:11

标签: mpi mutual-exclusion mpi-rma

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

在该示例中,等级0的过程等待,直到过程1..4在进行之前每个都将结果的值增加1。

对于函数0中使用的断言的默认值MPI_Win_lock_all(),我有时(十分之一)得到无限循环,即更新MASTER中result[0]的值值为3.终端输出类似于以下代码片段:

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

根据文档,函数MPI_Fetch_and_op是原子的。

  

此操作相对于其他"累积"是原子操作。   操作

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

如果我将assert的值更改为MPI_MODE_NOCHECK,它似乎可以正常工作

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

一起使用

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

  

MPI_MODE_NOCHECK

     

当调用者持有窗口锁定时,没有其他进程成立,或者将尝试获取冲突的锁定。这很有用   互斥是通过其他方式实现的,但是连贯性   可能附加到锁定和解锁呼叫的操作仍然是   必需的。

提前致谢!

示例程序:

#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

1 个答案:

答案 0 :(得分:1)

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

  • 您对测试result[0] != 4中的进程数进行了硬编码
  • 您将主值硬编码为MPI_Fetch_and_op(&one, &result, MPI_INT, 0
  • 传递与更新和结果相同的地址对我来说很危险:MPI_Fetch_and_op(&result, &result
  • 我的编译器抱怨第一个参数,因为它实际上是int**(实际上是int (*)[1]
  • 我不确定您为什么对第二个参数没有同样的抱怨
  • ....但是我还是对第二个参数感到不满意,因为获取操作会将您指定为窗口缓冲区的内容写入内存。我想这里缺乏连贯性可以为您省钱。
  • 您使用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

好吧,所以这比理想的编程还少。不过,在我的设置中它仍然有效。 (有时。有时它也会挂起。)因此,您可能需要稍微清理一下代码。您的逻辑是正确的,但实际的实现却不正确。