【问题标题】:Timing a broadcasted message to all processors using MPI_Isend and MPI_Irecv instead of MPI_Bcast使用 MPI_Isend 和 MPI_Irecv 而不是 MPI_Bcast 向所有处理器定时广播消息
【发布时间】:2016-01-06 19:17:05
【问题描述】:

我有一个项目,我需要使用 MPI_Isend 和 MPI_Irecv 对 MPI_Bcast 的任何错误实现进行计时,并将其与 MPI_Bcast 进行比较。因为这些程序的时间是 0.000000 秒,所以我需要使用一个大数组(就像我所做的那样)。我下面的代码中还没有的是 for 循环和 MPI_Irecv/Isend 函数应该在一个循环中,以使程序花费大量的时间来完成。

这是我的代码,我将在下面讨论我遇到的问题:

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

int main(int argc, char **argv) {

  int a = 1000000000;
  int i, N;
  int Start_time, End_time, Elapse_Time;
  int proc_rank, partner, world_size;
  MPI_Status stat;
  float mydata[a];
  MPI_Request request;

  MPI_Init(&argc,&argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &proc_rank);
  MPI_Comm_size(MPI_COMM_WORLD, &world_size);

  Start_time = MPI_Wtime();

  for (i = 0; i < a; i++) {
    mydata[i] = 0.2567*i;
  }

  MPI_Irecv(mydata, a, MPI_BYTE, 0, 1, MPI_COMM_WORLD, &request);

  MPI_Isend(mydata, a, MPI_BYTE, 0, 1, MPI_COMM_WORLD, &request);

  End_time = MPI_Wtime();
  Elapse_Time = End_time - Start_time;
  printf("Time on process %d is %f Seconds.\n", proc_rank, Elapse_Time);

  MPI_Finalize;

return 0;
}

当我使用命令mpirun -np 4 ./a.out 运行它时,我只获得了一个处理器的时间,但我不确定为什么。我想我只是不明白这些功能是如何工作的,或者我应该如何使用它们。

感谢您的帮助!

【问题讨论】:

  • 你为你的程序设置了多大的堆栈?大多数操作系统仅提供少量 MiB,因此 mydata[]非常可能会出现 oveflow => 未定义的行为。
  • 我实际上不确定您所说的“设置堆栈”是什么意思。将数组大小更改为更小的值似乎可以降低问题发生的可能性,但它仍然偶尔会发生。
  • 谷歌关闭了吗?简单地说:对于auto 变量,数组太大了。在文件级别使用静态变量 - 无论是static,还是使用外部链接或本地static。我没有说这是唯一的错误。并思考“undefined”的含义。
  • world_size 值是多少?尝试查看它是否与您运行的进程数相同。
  • @CaptainWise 是一样的值

标签: c parallel-processing mpi


【解决方案1】:

您的代码中有几个不同的问题,都可能导致它崩溃或行为异常:

  • 正如@Olaf 已经提到的那样,在堆栈上分配数组mydata 是一个非常糟糕的主意。对于这么大的数组,您绝对应该通过显式调用malloc() 在堆上进行分配。即便如此,您还是在这里使用了一些严重的内存块,因此请注意不要耗尽您机器上的可用内存。此外,一些 MPI 库难以处理大小大于 2GB 的消息,您的情况就是这样。再说一遍,请小心。
  • 您将mydata 用于发送和接收目的。但是,一旦您发布了非阻塞通信,您在通信完成之前不能重复使用相应的消息。因此,在您的情况下,您需要两个数组,一个用于发送,一个用于接收。
  • 您传递给 MPI 调用的数据类型(即MPI_BYTE)与您传输的数据的实际类型(即float)不一致。您应该改用MPI_FLOAT
  • 您调用 MPI_Irecv()MPI_Isend() 而不调用任何有效的 MPI_Wait()MPI_Test() 函数。这是错误的,因为这意味着通信可能永远不会发生。
  • MPI_Wtime() 返回 double,而不是 int。这不是一个错误本身,但它可能会导致意想不到的结果。此外,您在调用printf() 时请求的格式对应于浮点数据,而不是整数,因此您必须使其保持一致。
  • (轻微 - 错字)您错过了 ()MPI_Finalize()
  • (轻微 - 我猜)您只与进程 #0 通信...

所以这里有一些可能的工作代码版本:

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

int main(int argc, char **argv) {

    int a = 1000000000;
    int i, from, to;
    double Start_time, End_time, Elapse_Time;
    int proc_rank, world_size;
    float *mysenddata, *myrecvdata;
    MPI_Request requests[2];

    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &proc_rank );
    MPI_Comm_size( MPI_COMM_WORLD, &world_size );

    Start_time = MPI_Wtime();

    mysenddata = (float*) malloc( a * sizeof( float ) );
    myrecvdata = (float*) malloc( a * sizeof( float ) );

    assert( mysenddata != NULL ); /*very crude sanity check */
    assert( myrecvdata != NULL ); /*very crude sanity check */

    for ( i = 0; i < a; i++ ) {
        mysenddata[i] = 0.2567 * i;
    }

    from = ( proc_rank + world_size - 1 ) % world_size;
    to = ( proc_rank + 1 ) % world_size;

    MPI_Irecv( myrecvdata, a, MPI_FLOAT, from, 1, MPI_COMM_WORLD, &requests[0] );
    MPI_Isend( mysenddata, a, MPI_FLOAT, to, 1, MPI_COMM_WORLD, &requests[1] );
    MPI_Waitall( 2, requests, MPI_STATUSES_IGNORE );

    End_time = MPI_Wtime();
    Elapse_Time = End_time - Start_time;
    printf( "Time on process %d is %f Seconds.\n", proc_rank, Elapse_Time );

    free( mysenddata );
    free( myrecvdata );

    MPI_Finalize();

    return 0;
}

注意:为了让代码在所有情况下都能正常工作,我在这里实现了一个通信环,进程 0 发送到进程 1 并从进程 size-1 接收......但是,在您重新的上下文中广播的实现,你可以忽略它(即fromto 参数)。

【讨论】:

  • 非常感谢!这真的很有帮助。我只是想知道-“从”和“到”如何工作? 'to' 使它发送到高于它自己的 proc_rank 一个,对于'from',说 proc_rank=0,那么它将是 0+3 % 4 = 3。所以我对'to'如何发送感到困惑从一个处理器到所有其他处理器,以及为什么我们这样定义“来自”。我猜它与函数 MPI_Isend/MPI_Irecv 有关,但我不确定是什么。
  • 不,我只是在这里实现了一个环:0->1, 1->2, ... N-1->0。这是一种确保每个流程都发送给某人并从某人那里接收的方法。在您的广播式实现的上下文中,您可以忽略它。
  • 我编辑了我的答案以反映这一点。我希望这足够清楚
【解决方案2】:

我看到的唯一解释是您的其他进程在打印之前崩溃了。尝试将部分代码放在注释中并重新执行代码。

试试这个方法,看看有没有区别

  MPI_Init(&argc,&argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &proc_rank);
  MPI_Comm_size(MPI_COMM_WORLD, &world_size);

  /*Start_time = MPI_Wtime();

  for (i = 0; i < a; i++) {
    mydata[i] = 0.2567*i;
  }

  MPI_Irecv(mydata, a, MPI_BYTE, 0, 1, MPI_COMM_WORLD, &request);

  MPI_Isend(mydata, a, MPI_BYTE, 0, 1, MPI_COMM_WORLD, &request);

  End_time = MPI_Wtime();
  Elapse_Time = End_time - Start_time;*/
  printf("I'm process %d.\n", proc_rank);

  MPI_Finalize;

【讨论】:

  • 哦,看起来不错。当我这样做时,其中一些不打印。并且变量a 很大会使它们更频繁地崩溃。我只是不确定如何解决这个问题。将其设置为静态变量会有所帮助,但如上所述,这不是唯一的错误...
  • 我的意思是它崩溃的原因可能有多种,尽管我不确定它为什么会崩溃
  • 尝试使用SIZE_MAX 作为数组大小和a 变量。
  • 我认为其他部分不会导致您的代码崩溃。只需尝试一个小数组,看看您的所有进程是否正常工作。
  • 我不确定使用 SIZE_MAX 作为数组大小是什么意思。我包括了`printf("size max %zu\n", SIZE_MAX);`,输出是size max 18446744073709551615。如果我使用一个小数组,它们大部分时间都可以工作,但偶尔会崩溃。他们也完成得太快,我来不及计时。
猜你喜欢
  • 2020-03-09
  • 2015-05-27
  • 2018-09-13
  • 1970-01-01
  • 1970-01-01
  • 2018-08-08
  • 2010-11-08
  • 1970-01-01
  • 2013-04-04
相关资源
最近更新 更多