概要
IO によって大幅に制限されたプログラムがあり、それを高速化しようとしています。mmap を使用するのは良いアイデアのように思えましたが、実際には一連の fgets 呼び出しを使用する場合に比べてパフォーマンスが低下します。
いくつかのデモコード
約 350 万行を含む 800 MB のファイルに対してテストして、デモを本質的なものだけに絞り込みました。
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 でプロセスが実行されるのを見ると、memmap されたバージョンは途中で数千のページ フォールトを生成しました。
- fgets バージョンでは、CPU とメモリの使用量はどちらも非常に低くなっています。
質問
- これはなぜですか?fopen/fgets によって実装されたバッファリングされたファイル アクセスが、madvise POSIX_MADV_SEQUENTIAL を使用した mmap の積極的なプリフェッチよりも優れているという理由だけですか?
- おそらくこれを高速化する代替方法はありますか(IO負荷をプロセッサにシフトするオンザフライ圧縮/解凍以外)? 同じファイルで 'wc -l' のランタイムを見ると、そうではないかもしれないと推測しています。