12.6. 使用Memchecker

Open MPI中的Memchecker功能借助内存检查工具(如Valgrind套件memcheck组件),为您的应用程序(以及Open MPI内部)提供MPI语义检查。


12.6.1. Memchecker检测的错误类型

Open MPI的内存检查器基于Valgrind中包含的memcheck工具,因此继承了它的所有优势。首先,它会检查所有内存读写操作,并拦截对malloc(3)/free(3)以及C++的new/delete操作符的调用。最重要的是,内存检查器能够检测非阻塞和单边通信中的用户缓冲区错误,例如对活跃的非阻塞接收操作的缓冲区进行读写,或对活跃的非阻塞发送操作的缓冲区进行写入。

以下是Memchecker可以检测到的一些示例问题:

访问非阻塞通信控制下的缓冲区:

int buf;
MPI_Irecv(&buf, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &req);
// The following line will produce a memchecker warning
buf = 4711;
MPI_Wait (&req, &status);

输入参数错误,例如发送缓冲区大小不正确:

char *send_buffer;
send_buffer = malloc(5);
memset(send_buffer, 0, 5);
// The following line will produce a memchecker warning
MPI_Send(send_buffer, 10, MPI_CHAR, 1, 0, MPI_COMM_WORLD);

在单边通信中访问窗口:

MPI_Get(A, 10, MPI_INT, 1, 0, 1, MPI_INT, win);
A[0] = 4711;
MPI_Win_fence(0, win);

未初始化的输入缓冲区:

char *buffer;
buffer = malloc(10);
// The following line will produce a memchecker warning
MPI_Send(buffer, 10, MPI_INT, 1, 0, MPI_COMM_WORLD);

MPI_ERROR结构中使用未初始化的MPI_Status字段的情况:(MPI-1标准将MPI ERROR字段定义为未定义,适用于单次完成调用如MPI_Wait(3)MPI_Test(3),参见MPI-1第22页):

MPI_Wait(&request, &status);
// The following line will produce a memchecker warning
if (status.MPI_ERROR != MPI_SUCCESS)
    return ERROR;

12.6.2. 构建支持Memchecker的Open MPI

要使用内存检查器,您需要Valgrind 3.2.0或更高版本,并且配置Open MPI时启用了--enable-memchecker--enable-debug标志。

注意

Memchecker功能默认处于关闭状态,因为它会导致性能下降。

当指定--enable-memchecker时,configure将检查是否存在足够新版本的valgrind发行版。如果找到,Open MPI将构建Memchecker支持功能。

例如:

shell$ ./configure --prefix=/path/to/openmpi --enable-debug \
    --enable-memchecker --with-valgrind=/path/to/valgrind

您可以通过使用ompi_info(1)命令来检查Open MPI是否已构建支持Memchecker功能。

# The exact version numbers shown may be different for your Open
# MPI installation
shell$ ompi_info | grep memchecker
MCA memchecker: valgrind (MCA v1.0, API v1.0, Component v1.3)

如果没有看到“MCA memchecker: valgrind”这一行,您可能没有正确配置和安装Open MPI。


12.6.3. 使用Memchecker运行Open MPI应用

在Open MPI构建并安装支持Memchecker后,只需使用Valgrind运行您的应用程序,例如:

shell$ mpirun -n 2 valgrind ./my_app

如果您启用了Memchecker,但目前不想检查应用程序,只需像往常一样运行您的应用程序即可。例如:

shell$ mpirun -n 2 ./my_app

12.6.4. 使用Memchecker对应用程序性能的影响

配置选项 --enable-memchecker(与 --enable-debug 一起使用)确实会导致性能下降,即使不在 Valgrind 下运行。以下说明其机制,可能有助于决定是否要在集群范围内安装时启用 --enable-memchecker

有两种情况:

  1. 如果不使用Valgrind运行,Valgrind的ClientRequests(为检查而添加到正常执行路径中的汇编指令)不会影响整体MPI性能。Valgrind ClientRequests的详细说明请参阅Valgrind文档。在x86-64架构下,ClientRequests可简化为来自valgrind.h的以下四条循环左移(ROL)和一条交换(XCHG)汇编指令:

    #define __SPECIAL_INSTRUCTION_PREAMBLE                      \
                   "rolq \$3,  %%rdi; rolq \$13, %%rdi\\n\\t"   \
                   "rolq \$61, %%rdi; rolq \$51, %%rdi\\n\\t"
    

    以及

    __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE               \
                   /* %RDX = 客户端请求( %RAX ) */           \
                   "xchgq %%rbx,%%rbx"                            \
                   : "=d" (_zzq_result)                           \
                   : "a" (& _zzq_args``0``), "0" (_zzq_default)   \
                   : "cc", "memory"                               \
                  );
    

    每个ClientRequest都会执行这些指令。当未运行Valgrind时,这些ClientRequest指令不会改变算术结果(将64位寄存器左移128位,寄存器与自身交换),除了进位标志。

    第一个请求用于检查是否在Valgrind下运行。如果未在Valgrind下运行,则不会执行后续检查(即ClientRequests)。

  2. 如果应用程序在Valgrind下运行,由于Valgrind JIT和所采用的检查工具,性能自然会降低。 关于Valgrind的Memcheck工具在SPEC 2000基准测试上的成本和开销, 请参阅优秀论文 Valgrind: A Framework for Heavyweight Dynamic Binary Instrumentation。 如需了解影子内存各种内部实现方案的评估,请参阅 Building Workload Characterization Tools with Valgrind