发生异常时不会调用MPI错误处理程序

时间:2013-11-19 01:01:48

标签: c mpi fault-tolerance

我在过去几天一直在尝试使用MPI在C中编写容错应用程序。我正在尝试学习如何将错误处理程序附加到MPI_COMM_WORLD通信器,以防万一节点出现故障(可能是由于崩溃)并退出而不调用MPI_Finalize()程序仍然可以从这种情况中恢复并继续计算。

到目前为止我遇到的问题是,在将错误处理函数附加到通信然后导致节点崩溃之后,MPI不会调用错误处理程序,而是强制所有线程退出。

我认为这可能是我的应用程序的一个问题,所以我在线查找示例代码并尝试运行它但情况是一样的...我目前正在尝试运行的示例代码如下。 (我从这里得到https://www.google.co.uk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CC4QFjAA&url=http%3A%2F%2Fwww.shodor.org%2Fmedia%2Fcontent%2F%2Fpetascale%2Fmaterials%2FdistributedMemory%2Fpresentations%2FMPI_Error_Example.pdf&ei=jq6KUv-BBcO30QW1oYGABg&usg=AFQjCNFa5L_Q6Irg3VrJ3fsQBIyqjBlSgA&sig2=8An4SqBvhCACx5YLwBmROA道歉,因为我在pdf但是我没有写它,所以我现在粘贴下面相同的代码):

/* Template for creating a custom error handler for MPI and a simple program 
to demonstrate its' use. How much additional information you can obtain 
is determined by the MPI binding in use at build/run time. 

To illustrate that the program works correctly use -np 2 through -np 4.

To illustrate an MPI error set victim_mpi = 5 and use -np 6.

To illustrate a system error set victim_os = 5 and use -np 6.

2004-10-10 charliep created
2006-07-15 joshh  updated for the MPI2 standard
2007-02-20 mccoyjo  adapted for folding@clusters
2010-05-26 charliep cleaned-up/annotated for the petascale workshop 
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "mpi.h"

void ccg_mpi_error_handler(MPI_Comm *, int *, ...);

int main(int argc, char *argv[]) {
    MPI_Status status;
    MPI_Errhandler errhandler;
    int number, rank, size, next, from;
    const int tag = 201;
    const int server = 0;
    const int victim_mpi = 5;
    const int victim_os = 6;

    MPI_Comm bogus_communicator;
    MPI_Init(&argc, &argv);!
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    MPI_Comm_create_errhandler(&ccg_mpi_error_handler, &errhandler);
    MPI_Comm_set_errhandler(MPI_COMM_WORLD, errhandler);

    next = (rank + 1) % size;
    from = (rank + size - 1) % size;

    if (rank == server) {
        printf("Enter the number of times to go around the ring: ");
        fflush(stdout);
        scanf("%d", &number);                                              
        --number;
        printf("Process %d sending %d to %d\n", rank, number, next);
        MPI_Send(&number, 1, MPI_INT, next, tag, MPI_COMM_WORLD);
    }

    while (true) {
        MPI_Recv(&number, 1, MPI_INT, from, tag, MPI_COMM_WORLD, &status);
        printf("Process %d received %d\n", rank, number);
        if (rank == server) {
            number--;
            printf("Process 0 decremented number\n");
        }

        if (rank == victim_os) {
            int a[10];
            printf("Process %d about to segfault\n", rank);
            a[15565656] = 56;
        }

        if (rank == victim_mpi) {
            printf("Process %d about to go south\n", rank);
            printf("Process %d sending %d to %d\n", rank, number, next);
           MPI_Send(&number, 1, MPI_INT, next, tag, bogus_communicator);
        } else {
            printf("Process %d sending %d to %d\n", rank, number, next);
            MPI_Send(&number, 1, MPI_INT, next, tag, MPI_COMM_WORLD);
        }

        if (number == 0) {
            printf("Process %d exiting\n", rank);
            break;
        }
    }

    if (rank == server)
        MPI_Recv(&number, 1, MPI_INT, from, tag, MPI_COMM_WORLD, &status);

    MPI_Finalize();
    return 0;
}

void ccg_mpi_error_handler(MPI_Comm *communicator, int *error_code, ...) {
    char error_string[MPI_MAX_ERROR_STRING];
    int error_string_length;
    printf("ccg_mpi_error_handler: entry\n");
    printf("ccg_mpi_error_handler: error_code = %d\n", *error_code);
    MPI_Error_string(*error_code, error_string, &error_string_length);
    error_string[error_string_length] = '\0';
    printf("ccg_mpi_error_handler: error_string = %s\n", error_string);
    printf("ccg_mpi_error_handler: exit\n");
    exit(1);
}

程序实现了一个简单的令牌环,如果你给它注释中描述的参数,那么我会得到这样的结果:

    >>>>>>mpirun -np 6 example.exe
    Enter the number of times to go around the ring: 6
    Process 1 received 5
    Process 1 sending 5 to 2
    Process 2 received 5
    Process 2 sending 5 to 3
    Process 3 received 5
    Process 3 sending 5 to 4
    Process 4 received 5
    Process 4 sending 5 to 5
    Process 5 received 5
    Process 5 about to go south
    Process 5 sending 5 to 0
    Process 0 sending 5 to 1
    [HP-ENVY-dv6-Notebook-PC:09480] *** Process received signal *** 
    [HP-ENVY-dv6-Notebook-PC:09480] Signal: Segmentation fault (11)
    [HP-ENVY-dv6-Notebook-PC:09480] Signal code: Address not mapped (1) 
    [HP-ENVY-dv6-Notebook-PC:09480] Failing at address: 0xf0b397
    [HP-ENVY-dv6-Notebook-PC:09480] [ 0] /lib/x86_64-linux-gnu/libpthread.so.0(+0xfcb0) [0x7fc0ec688cb0]
    [HP-ENVY-dv6-Notebook-PC:09480] [ 1] /usr/lib/libmpi.so.0(PMPI_Send+0x74) [0x7fc0ec8f3704]
    [HP-ENVY-dv6-Notebook-PC:09480] [ 2] example.exe(main+0x23f) [0x400e63]
    [HP-ENVY-dv6-Notebook-PC:09480] [ 3] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7fc0ec2da76d]
    [HP-ENVY-dv6-Notebook-PC:09480] [ 4] example.exe() [0x400b69]
    [HP-ENVY-dv6-Notebook-PC:09480] *** End of error message *** 
    --------------------------------------------------------------------------
    mpirun noticed that process rank 5 with PID 9480 on node andres-HP-ENVY-dv6-Notebook-PC exited on signal 11 (Segmentation fault).
    --------------------------------------------------------------------------

显然,在我看到的输出中,printf()中的ccg_mpi_error_handler()都没有被执行,因此我假设处理程序根本没有被调用。我不确定它是否有任何帮助,但我正在运行ubuntu linux 12.04并且我使用apt-get安装了MPI。我用来编译程序的命令如下:

mpicc err_example.c -o example.exe

此外,当我mpicc -v时,我得到以下内容:

  Using built-in specs.
  COLLECT_GCC=/usr/bin/gcc
  COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.6/lto-wrapper
  Target: x86_64-linux-gnu
  Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.6.3-1ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
  Thread model: posix
  gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
非常感谢帮助!感谢...

2 个答案:

答案 0 :(得分:4)

MPI标准不要求MPI实现甚至能够正常处理错误。以下摘自MPI-3.0的§8.3说明了一切:

  

MPI实现不能或可能选择不处理在此期间发生的某些错误   MPI电话。这些可能包括生成异常或陷阱的错误,例如浮点错误或访问冲突。 MPI处理的错误集与实现有关。每个此类错误都会产生 MPI异常

     

上述文本优先于本文档中有关错误处理的任何文本。   具体而言,应该读取 处理错误 的文本。

(保留原始格式,包括使用粗体和斜体字体)

这有很多原因,但大多数都与性能和可靠性之间的某种权衡有关。在各个级别进行错误检查并优雅地处理错误条件会产生一些不那么微小的开销,并使库代码库非常复杂。

也就是说,并非所有MPI库都是平等的。其中一些实现了比其他更好的容错。例如,与英特尔MPI 4.1相同的代码:

...
Process 5 about to go south
Process 5 sending 5 to 0
ccg_mpi_error_handler: entry
ccg_mpi_error_handler: error_code = 403287557
ccg_mpi_error_handler: error_string = Invalid communicator, error stack:
MPI_Send(186): MPI_Send(buf=0x7fffa32a7308, count=1, MPI_INT, dest=0, tag=201, comm=0x0) failed
MPI_Send(87).: Invalid communicator
ccg_mpi_error_handler: exit

您的案例中的错误消息格式表明您使用的是Open MPI。 Open MPI中的容错是一种实验性的(OMPI开发人员之一,即Jeff Squyres,不时访问Stack Overflow--他可以提供更明确的答案)并且必须在库构建时明确启用并提供选项比如--enable-ft=LAM

默认情况下,MPICH也无法处理这种情况:

Process 5 about to go south
Process 5 sending 5 to 0

===================================================================================
=   BAD TERMINATION OF ONE OF YOUR APPLICATION PROCESSES
=   EXIT CODE: 139
=   CLEANING UP REMAINING PROCESSES
=   YOU CAN IGNORE THE BELOW CLEANUP MESSAGES
===================================================================================
YOUR APPLICATION TERMINATED WITH THE EXIT STRING: Segmentation fault (signal 11)
This typically refers to a problem with your application.
Please see the FAQ page for debugging suggestions

请注意,当检测到错误时,当前MPI不保证程序状态保持一致:

  

检测到错误后,MPI的状态未定义。也就是说,使用用户定义的错误处理程序或MPI_ERRORS_RETURN,不一定允许用户在检测到错误后继续使用MPI。这些错误处理程序的目的是允许用户在程序退出之前发出用户定义的错误消息并采取与MPI无关的操作(例如刷新I / O缓冲区)。 MPI实现可以在发生错误后允许MPI继续,但不需要这样做。

其中一个原因是,对这种“破碎”的传播者进行集体操作变得不可能,许多内部MPI机制要求所有级别之间的集体信息共享。提出了一种更好的容错机制,称为直通稳定(RTS),以包含在MPI-3.0中,但它没有超过最终投票。使用RTS添加一个新的MPI调用,通过集体删除所有失败的进程,从而创建一个健康的通信器,然后剩余的进程可以继续在新的通信器中运行。

免责声明:我不为英特尔工作,也不认可他们的产品。只有这样,IMPI才能提供比Open MPI和MPICH的默认构建配置更好的开箱即用的用户错误处理实现。通过更改构建选项或未来可能会出现适当的FT,可能在开源实现中实现可比较的容错级别(例如,在Open MPI中有RTS的原型实现)

答案 1 :(得分:1)

虽然Hristo在他提到的所有内容中都是正确的,但情况并不那么黯淡。确实,通过 default ,几乎任何MPI实现都没有容错能力。但是,有一些选项可以在Open MPI和MPICH中启用实验性容错。 Hristo提到了Open MPI的构建标志。对于MPICH,该选项是mpiexec的运行时标志。使用以下命令:

mpiexec -n <num_procs> --disable-auto-cleanup <executable> <program args>

--disable-auto-cleanup标志将告诉MPICH在一个进程失败时不会自动终止所有进程。这样您就可以触发自定义MPI_Errhandler。当然,为了使用它,您需要一个足够新版本的MPICH。我认为MPICH 3.0之后的任何东西都可以工作,但我不记得何时添加了这个功能。目前,MPICH正处于3.1的预览版本中,因此如果您大胆的话,可以试试。

虽然FT本身没有进入MPI 3.0(称为用户级故障缓解,而不是 Run Through Stabilization ,这是一个较旧的FT提案), FT应用程序的希望,即使使用集体。您可以尝试新的点对点通信器创建功能MPI_COMM_CREATE_GROUP,以在失败后创建新的通信器。显然这将有点棘手,你需要确保你仔细处理所有正在进行的操作,但它可以做一些事情。或者,你可以避免集体,一切都更容易。