11.9.1. Open MPI IO("OMPIO")

OMPIO是MPI规范中定义的MPI I/O函数的Open MPI原生实现。

OMPIO的主要目标是:

  1. 通过将MPI I/O功能分离到子框架中,提高并行I/O库的模块化程度。

  2. 允许框架利用不同的运行时决策算法来确定在特定场景下使用哪个模块,从而实现非特定于文件系统的决策。

  3. 改进并行I/O功能与Open MPI其他组件的集成,特别是派生数据类型引擎和进度引擎。与派生数据类型引擎的集成可以更快地解码派生数据类型,并使用优化的数据类型到数据类型的复制操作。

OMPIO本质上是OpenMPI中io框架的一个组件。在打开文件时,OMPIO组件会初始化多个子框架及其组件,具体包括:

  • fs: 负责所有文件管理操作

  • fbtl: 支持阻塞和非阻塞的独立I/O操作

  • fcoll: 支持阻塞和非阻塞的集体I/O操作

  • sharedfp: 支持所有共享文件指针操作。

11.9.1.1. OMPIO及其相关框架的MCA参数

ompi_info(1) 命令可以显示 OMPIO iofcollfsfbtlsharedfp 组件所有可用的参数:

shell$ ompi_info --param io       ompio --level 9
shell$ ompi_info --param fcoll    all --level 9
shell$ ompi_info --param fs       all --level 9
shell$ ompi_info --param fbtl     all --level 9
shell$ ompi_info --param sharedfp all --level 9

11.9.1.2. OMPIO子框架组件

OMPIO架构围绕子框架设计,允许开发者针对特定环境、应用或基础设施开发相对少量的优化代码。尽管已投入大量努力为默认值和组件间切换点做出合理决策,但用户和/或系统管理员偶尔可能希望调整组件的选择逻辑,强制使用特定组件。

强制使用某个组件的最简单方法是直接限制该框架下可用的组件列表。例如,一个想要使用dynamic fcoll组件的应用程序,只需在mpirun期间将该组件名称作为值传递给相应的MCA参数,或者通过Open MPI中任何其他可影响参数值的机制来实现,例如:

shell$ mpirun --mca fcoll dynamic -n 64 ./a.out

fsfbtl组件通常根据所使用的文件系统类型来选择(例如,当文件位于PVFS2/OrangeFS文件系统上时选择pvfs2组件,对于Lustre文件系统则选择lustre组件等)。如果没有可用的特定文件系统组件(例如本地文件系统、NFS、BeefFS等),则使用ufs fs组件,而posix fbtl组件则作为读写操作的默认组件。

fcoll框架提供了多个不同的组件。当前OMPIO中的决策逻辑会利用应用程序提供的文件视图以及文件系统级别的特征(例如文件系统、条带宽度)来决定使用哪个组件。最重要的fcoll组件包括:

  • dynamic_gen2: 用于lustre文件系统的默认组件。该组件基于两阶段I/O算法,采用静态文件分区策略,即聚合器进程默认只会将数据写入单个存储服务器。

  • vulcan:默认在所有其他文件系统上使用的组件。该组件基于两阶段I/O算法,采用均匀文件分区策略,即每个n个聚合器将写入整个文件的1/n部分。

  • individual: 该组件将所有集体I/O操作以个体I/O操作的方式执行。

sharedfp框架根据文件系统特性提供了共享文件指针操作的不同实现方案。

  • lockedfile: 该组件将用于支持文件锁定的文件系统。

  • sm: 用于通信器所有进程位于同一物理节点场景的组件。

  • individual:当其他两个组件都不可用时可以使用的组件。不过该组件仅提供有限功能(即仅支持写入操作)。

    注意

    详见individual共享文件指针组件章节了解其功能与限制。

11.9.1.3. 优化OMPIO性能

影响I/O操作性能的最重要参数如下:

  1. io_ompio_cycle_buffer_size: 每次调用时单个读/写操作处理的数据大小。默认情况下,单个读/写操作会作为一个整块执行。在某些场景下,将操作拆分为多个较小的块可以提高性能。

  2. io_ompio_bytes_per_agg: 聚合进程上用于集体I/O操作的临时缓冲区大小。默认值为32MB。调整此参数对集体操作的性能有极大影响。

  3. io_ompio_num_aggregators: 用于集合I/O操作的聚合器数量。将此参数设置为大于零的值会禁用OMPIO内部的自动聚合器选择逻辑。调整此参数对集合操作的性能有极大影响。

  4. io_ompio_grouping_option: 用于自动决定聚合器数量的算法。处理常规二维或三维数据分解的应用程序可以尝试将此参数更改为4(混合)算法。

  5. fs_ufs_lock_algorithm: 用于确定文件操作中需要锁定文件哪部分的参数。由于ufs fs组件在多种文件系统上使用,openmpi会自动选择适用于所有文件系统的正确值,例如在NFS文件系统上强制锁定,而在本地文件系统上禁用锁定。用户可以根据实际使用场景调整所需的锁定行为,因为默认值可能对其应用程序来说往往过于严格。

11.9.1.4. 在并行文件系统上设置条带大小和条带宽度

许多fs组件允许您操作并行文件系统上新文件的布局。请注意,许多文件系统仅允许在文件创建时更改这些设置,即对于已存在的文件可能无法修改这些值。

  1. fs_pvfs2_stripe_size: 设置PVFS2/OrangeFS文件系统上新文件的存储服务器数量。如果未设置,将使用系统默认值。请注意,此参数也可以通过stripe_size MPI信息值进行设置。

  2. fs_pvfs2_stripe_width: 设置PVFS2文件系统上新文件的单个块大小。如果未设置,将使用系统默认值。请注意,此参数也可以通过stripe_width MPI信息值进行设置。

  3. fs_lustre_stripe_size: 设置Lustre文件系统上新文件的存储服务器数量。如果未设置,将使用系统默认值。请注意,此参数也可以通过stripe_size MPI Info值进行设置。

  4. fs_lustre_stripe_width: 设置Lustre文件系统上新文件的单个块大小。如果未设置,将使用系统默认值。请注意,此参数也可以通过stripe_width MPI Info值来设置。

11.9.1.5. 在MPI文件I/O操作中使用GPU设备缓冲区

OMPIO支持通过MPI I/O接口直接读写GPU缓冲区。使用此功能可简化专用于GPU设备的缓冲区管理,因此无需通过主机内存实现文件I/O操作的中转。

在内部,openmpi将用户缓冲区划分为多个块以执行读/写操作。openmpi使用的块大小对设备缓冲区的文件I/O操作性能有显著影响,可以通过io_ompio_pipeline_buffer_size MCA参数进行控制。

11.9.1.6. 使用 individual sharedfp 组件及其局限性

individual共享文件指针组件提供了一种近似共享文件指针操作的实现,仅适用于写入操作。该组件仅在既无法使用sm也无法使用lockedfile组件的情况下推荐使用,例如当使用多个节点且文件系统不支持锁定时。

从概念上讲,每个进程将write_shared操作的数据连同时间戳写入单独的文件中。在每次集体操作(或在文件关闭操作期间),会使用时间戳作为主要标准,将所有独立文件中的数据合并到实际的输出文件中。

该组件存在一定的限制和约束条件,例如它依赖各个集群节点上的同步时钟来确定最终文件中条目之间的顺序,这可能导致与实际调用序列相比出现一些偏差。

此外,该组件仅支持write操作,不支持读取操作。

11.9.1.7. OMPIO的其他特性

OMPIO 提供了许多额外功能,主要面向开发者,但有时也可能对感兴趣的高级用户有所帮助。这些功能通常可以通过 MCA 参数进行控制。

  • io_ompio_coll_timing_info: 设置此参数将在关闭文件时生成一个简短报告,仅显示在集体I/O操作的通信和I/O操作中所花费的时间。

  • io_ompio_record_file_offset_info: 设置此参数将基于所使用的文件视图报告进程之间的邻接关系。这对于理解I/O操作的性能特征有时很重要。注意,使用此功能需要在编译ompio时额外设置编译时标志。

    该标志生成的输出文件以矩阵形式记录了进程对文件的访问模式,表现为进程间的邻接关系。例如,如果进程0访问文件的前四个字节,而进程1访问接下来的四个字节,则称进程0和1具有邻接关系,因为它们访问了文件中相邻的元素。对于文件中检测到的每个邻接关系,相应进程对的值会增加一。

    数据以压缩行存储格式提供。为最小化使用此功能写入的数据量,仅输出非零值。输出文件的第一行表示矩阵中非零元素的数量;第二个数字是行索引中的元素数量。输出文件的第三行给出所有列索引。第四行列出了所有值,第五行给出了行索引。行索引表示值数组中新行开始的位置。