如何进行并行mmap以加速文件读取?

4
我正在处理这段代码,现在已经使mmap起作用了,但我想知道是否可以并行使用mmap,如果可以,如何实现。假设我的数据存储在并行文件系统(GPFS, RAID0, 等等)中,并且我想使用n个进程来读取它。
例如,如何让每个处理器将数据的1/nth连续块读入内存?或者,将每个nth内存块(1 B, 1 MB, 100 MB, 1 GB,任意优化选择)读入内存?
我假设这里是一个posix文件系统。

1
使用mmap()会让你受制于内核的虚拟内存管理器。而且,由于创建物理到虚拟映射需要是线程安全的,所以在负载下它往往会变成单线程。请查看lio_listio()来执行多个异步IO操作。http://man7.org/linux/man-pages/man3/lio_listio.3.html如果您正在流式传输大量数据(只读一次,不寻址),请使用直接IO http://www-01.ibm.com/support/knowledgecenter/SSFKCN_3.5.0/com.ibm.cluster.gpfs.v3r5.gpfs100.doc/bl1adm_direct.htm。 - Andrew Henle
如果我想在重量级进程范式中实现这个功能怎么办?比如在分布式内存环境中使用mpi,每个rank获取1/n的数据并对其进行处理?是否会出现相同的问题?如果不是,那么我该如何将第i个1/n的数据映射到内存中? - drjrm3
多个集群物理服务器进行读取?那么每个读取只需要单线程。mmap()可能有效,但我见过一些非常快的文件系统可以比虚拟到物理映射更快地传递数据。如果您的磁盘速度很快,mmap()将无法很好地工作。如果您没有任何局部性并且最终不得不在集群中传递文件数据,则这可能比快速文件系统慢得多。这完全取决于您的处理需求-要实现真正的快速,您必须调整所有内容以协同工作,并且不能抽象出物理设计。 - Andrew Henle
mmap() 可以将任意数量的字节从任意偏移量映射到文件中。void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); length 是要映射的字节数,offset 是开始映射的文件偏移量。http://linux.die.net/man/2/mmap - Andrew Henle
1个回答

0

这是我用于并行读取的mpi函数。它根据pagesize将文件分成n个连续的部分,并通过mmap使每个进程读取一个单独的部分。由于进程i最后一行(很可能)只有半行,而进程i+1的第一行是同一行的后半部分,因此需要进行一些额外的技巧来处理。

ikind nchars_orig; // how many characters were in the original file
int pagesize = getpagesize();
off_t offset;
struct stat file_stat;
int finp = open(inpfile, O_RDONLY);
int status = fstat(finp, &file_stat);
nchars_orig = file_stat.st_size;

// find out hwich pieces of the file each process should read
ikind nchars_per_proc[nprocs];
for(int ii = 0; ii < nprocs; ii++) {
    nchars_per_proc[ii] = 0;
}   
// start at the second to last proc, so the last proc will get hit first
// we will decrement him at the end, so this will distribute the work more evenly
int jproc = nprocs-2;
ikind nchars_tot = 0;
ikind nchardiff = 0;
for(ikind ic = 0; ic < nchars_orig; ic+= pagesize) {
    jproc += 1;
    nchars_tot += pagesize;
    if(jproc == nprocs) jproc = 0;
    if(nchars_tot > nchars_orig) nchardiff = nchars_tot - nchars_orig;
    nchars_per_proc[jproc] += pagesize;
}   
nchars = nchars_per_proc[iproc];
if( iproc == nprocs-1 ) nchars = nchars - nchardiff;
offset = 0;
for(int ii = 0; ii < nprocs; ii++) {
    if( ii < iproc ) offset += nchars_per_proc[ii];
} 
cs = (char*)mmap(0, nchars, PROT_READ, MAP_PRIVATE, finp, offset);

请重新阅读@Andrew Henie对原问题的评论。 mmap()并不比显式磁盘IO更快(它使用相同的磁盘/内存总线/通道)。您只是将“I/O阻塞”换成了“页面错误阻塞”。 - wildplasser
我更关心的是高效的io吞吐量。这些文件将在网络附加存储上占用TB级别的空间,每次发送<1 KB的数据比一次读取大块(10+ GB)的数据效率低...我想...无论如何,这正是我想要实现的(使用mmap并行读取文件与mpi),所以现在我可以测试我的理论了。 - drjrm3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接