9.7. 构建完全静态的MPI应用程序
9.7.1. 你真的需要完全静态吗?
没有人会意外进入这个文档部分。
如果您正在阅读这段文字,很可能是因为您希望解决某个问题,而完全静态的MPI应用程序似乎是正确的解决方案。人们通常认为完全静态的MPI应用程序可以解决以下两个常见问题:
MPI应用程序无法启动,因为运行时找不到依赖的共享库。
大规模启动时的文件系统性能极差。
如果这些问题中的任何一个困扰着你,Open MPI社区强烈建议你使用其他机制来解决问题:完全静态的MPI应用程序虽然可行,但由于其他原因并非最优选择。
以下部分将讨论上述问题。
9.7.1.2. 大规模提升文件系统性能
在v5.0.0版本之前,Open MPI将大量插件编译为独立的动态共享对象(DSOs)——即文件系统中的独立文件。这些DSOs中的许多会在运行时被每个MPI进程加载。
这可能导致文件系统拥塞,尤其是当Open MPI安装在网络文件系统上并启动大型作业时:许多节点将同时与文件服务器通信,并可能需要传输大量小型文件。
从v5.0.0版本开始,默认情况下,Open MPI的插件不再构建为动态共享对象(DSO)。因此,Open MPI在启动时通常只加载少量共享库。即使Open MPI安装在网络文件系统上,随着时间的推移这些库很可能会被节点缓存,因此在启动MPI作业时产生的网络文件系统流量相对较小。
简而言之:与之前版本相比,Open MPI v5.0.x对网络文件系统的影响已大幅降低。现在不再需要编译完全静态的应用程序来消除启动时打开每个DSO文件的行为。
9.7.1.3. 完全静态应用的其他弊端
以下是完全静态MPI应用程序不够理想的几个其他原因:
当应用程序静态链接所有依赖项时,操作系统无法在多个进程副本之间共享代码。
例如,如果在节点上启动N个完全静态链接的MPI应用程序副本,将消耗(N * 应用程序大小)字节的RAM。相反,启动N个动态链接的MPI应用程序副本——其中每个副本具有相同的依赖库——每个共享依赖库只会被加载到RAM中一次。
换句话说:使用动态链接可以节省内存。
完全静态的应用程序不会链接到动态链接库,例如Linux上的
libdl,该库提供了dlopen(3)、dlsym(3)等功能。这将破坏依赖此类接口的Open MPI功能。警告
Open MPI的内存管理功能(在InfiniBand等绕过操作系统的网络上提供重要性能优化)需要
dlsym(3)接口,因此无法在完全静态的应用程序中工作。
现在你被说服了吗?如果可能的话,请尽量避免构建完全静态的MPI应用程序。
9.7.2. 构建完全静态的MPI应用程序
警告
如果在阅读完上述所有内容后,您仍然坚持要构建完全静态的MPI应用程序,请注意完全静态链接并不适合胆小者,也不推荐使用。但在某些注意事项下,这是可能实现的。
您的程序链接的所有组件都必须提供静态库。这包括Open MPI;您必须使用Open MPI的
configure命令中的--enable-static选项,或者确保Open MPI库的静态版本可用。注意
某些Linux发行版默认可能不包含常用Linux库的静态版本(例如libnuma),或者需要安装额外的RPM包才能获得对应的静态库。
Open MPI必须在不包含内存管理器的情况下构建。这意味着Open MPI必须使用
--without-memory-manager标志进行配置。在某些Open MPI没有内存管理器的平台上这无关紧要,但在某些平台上这是必需的(如Linux系统使用许多操作系统旁路网络时)。在Open MPI没有内存管理器的平台上使用此标志是无害的。重要提示
不包含内存管理器支持可能会导致Open MPI与操作系统旁路网络一起使用时性能降低。
以下是在Linux上配置Open MPI以构建完全静态库的方法:
shell$ ./configure --without-memory-manager --disable-dlopen \
--enable-static --disable-shared ...
--disable-shared 标志是可选的;它将阻止 Open MPI 同时构建共享库。
或者,您也可以尽可能多地使用静态库构建Open MPI,但通过省略--disable-dlopen标志来保留dlopen功能:
shell$ ./configure --without-memory-manager \
--enable-static --disable-shared ...
这将为您提供一个基本静态构建的Open MPI,但保留了至少部分动态库的优势。
9.7.2.1. 包含完整归档文件
某些系统可能对其支持库有额外的约束条件,需要采取额外步骤才能生成可正常工作的完全静态MPI应用程序。例如,任何拥有自身运行时插件系统(即在运行时加载动态共享对象("DSOs"))的库,在生成完全静态构建时都会面临额外的复杂性。
在这种情况下,通常需要运行mpicc ... --showme来查看
Open MPI封装命令将使用的编译器/链接器命令,然后为运行时所需的
DSO插件静态版本添加链接器参数来增强这些命令。
例如,如果您有一个libfoo.a在运行时动态加载plugin.so,您将需要一个plugin.a,并且——假设使用GNU链接器——添加类似以下的参数:
-static: 告知链接器生成静态可执行文件。-Wl,--whole-archive -lfoo /path/to/plugin.a -Wl,--no-whole-archive: 告诉链接器将整个foo库和整个plugin.a归档文件包含在可执行文件中。
您可以选择手动在命令行中添加这些参数,或者修改包装器编译器的默认行为以向最终用户隐藏这种复杂性(但请注意,如果修改了包装器编译器的默认行为,所有用户都将创建静态应用程序!)。