概述
我有一个受IO显著限制的程序,正在尝试加速它的运行。使用mmap似乎是个好主意,但实际上与仅使用一系列fgets调用相比,它会降低性能。
一些演示代码
我已经将演示压缩到了基本要素,并针对大约3.5百万行的800mb文件进行了测试:
使用fgets:
char buf[4096];
FILE * fp = fopen(argv[1], "r");
while(fgets(buf, 4096, fp) != 0) {
// do stuff
}
fclose(fp);
return 0;
800mb文件的运行时间:
[juhani@xtest tests]$ time ./readfile /r/40/13479/14960
real 0m25.614s
user 0m0.192s
sys 0m0.124s
mmap版本:
struct stat finfo;
int fh, len;
char * mem;
char * row, *end;
if(stat(argv[1], &finfo) == -1) return 0;
if((fh = open(argv[1], O_RDONLY)) == -1) return 0;
mem = (char*)mmap(NULL, finfo.st_size, PROT_READ, MAP_SHARED, fh, 0);
if(mem == (char*)-1) return 0;
madvise(mem, finfo.st_size, POSIX_MADV_SEQUENTIAL);
row = mem;
while((end = strchr(row, '\n')) != 0) {
// do stuff
row = end + 1;
}
munmap(mem, finfo.st_size);
close(fh);
运行时间有很大的差异,但从未比fgets更快:
[juhani@xtest tests]$ time ./readfile_map /r/40/13479/14960
real 0m28.891s
user 0m0.252s
sys 0m0.732s
[juhani@xtest tests]$ time ./readfile_map /r/40/13479/14960
real 0m42.605s
user 0m0.144s
sys 0m0.472s
其他注意事项
- 观察在top中运行的过程,memmapped版本在此过程中产生了几千个页面错误。
- fgets版本的CPU和内存使用率都非常低。
问题
- 为什么会出现这种情况?难道是由于fopen/fgets实现的缓冲文件访问比mmap与madvise POSIX_MADV_SEQUENTIAL实现的积极预取更好吗?
- 除了即时压缩/解压以将IO负载转移给处理器之外,是否有可能采用其他方法使其更快?看着相同文件上'wc -l'的运行时间,我猜想也许不是这种情况。
fread
(或read
)调用将所有内容读入一个大缓冲区,然后进行解析。 - user2100815