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