在上一篇中我们介绍了 mpi4py 中的文件互操作性,下面我们将介绍 mpi4py 中获得高性能 I/O 的方法和建议。
MPI 在底层实现中可充分利用集合操作和非连续数据读/写进行面向文件系统/设备的特殊优化。因此使用 MPI I/O 操作,最重要的就是要活用其提供的几个特征:单次非连续数据访问,集合操作,非阻塞操作,理解并灵活设置适合文件系统特征的各 hint,等。
由于底层可能提供的优化措施,应尽量使更多的进程参与 I/O 操作而不是将其集中在一个进程上。对非连续 I/O,应定义相应的派生数据类型描述“非连续性”,进而再利用集合操作让多个进程一起提交数据 I/O 请求。通常情况下应用程序都有自己的计算模式进而也可找出其 I/O 访问特征,要针对不同的 I/O 特征结合底层硬件设计相应的执行模型。对相同的 I/O 访问特征,也能以不同的发起方式提交给 I/O 系统,比如说使用哪一个具体的读/写方法及怎样调用该读/写方法等。
4 个级别的访问模式
一般可以将 I/O 访问模式分为 4 个级别。
我们以一个具体的例子来讲解这四个不同的访问级别。考虑一个以块状形式分布在16 个进程上的二维数组,具体的分布情况如下图所示:
这个数组作为一个整体以行优先的顺序存储在一个文件中,如上图所示,文件中首先是 0 号进程本地子数组的第一行,紧接着是 1 号进程本地子数组的第一行,然后是 2 号进程本地子数组的第一行,然后是 3 号进程本地子数组的第一行,接下来是 0 号进程本地子数组的第二行,1 号进程本地子数组的第二行,等等。现在每个进程要从这个文件中读取该进程本地的数据。可以看出,每个进程的本地数据都是以不连续的小块方式存储于文件的不同位置。
对这个例子,4 个级别的访问模式分别为:
级别 0:各个进程按照类似 Unix/Linux 中 I/O 操作方式独立地一次读取本地子数组的一行。具体来说,像下面这样:
fh = MPI.File.Open(..., filename, ...)
for i in range(num_local_rows):
fh.Seek(...)
fh.Read(row[i], ...)
fh.Close()
级别 1:基本同级别 0,不过采用集合 I/O 操作。具体来说,像下面这样:
fh = MPI.File.Open(MPI.COMM_WORLD, filename, ...)
for i in range(num_local_rows):
fh.Seek(...)
fh.Read_all(row[i], ...)
fh.Close()
级别 2:各个进程定