6

概要

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' のランタイムを見ると、そうではないかもしれないと推測しています。
4

3 に答える 3

8

POSIX_MADV_SEQUENTIALシステムへのヒントにすぎず、特定の POSIX 実装では完全に無視される場合があります。

2つのソリューションの違いmmapは、ファイルを完全に仮想アドレス空間にマップする必要があるのに対しfgets、IOは完全にカーネル空間で行われ、変更されないバッファーにページをコピーするだけです。

IO は一部のカーネル スレッドによって行われるため、オーバーラップの可能性も高くなります。

mmap1 つ (または複数) の独立したスレッドが各ページの最初のバイトを読み取ることで、実装の体感パフォーマンスを向上させることができます。この (またはこれらの) スレッドには、すべてのページ フォールトがあり、アプリケーション スレッドが特定のページに到達したときには、既に読み込まれています。

于 2011-05-19T09:09:22.367 に答える
4

のマニュアル ページを読むと、のフラグを追加するmmapことでページ フォールトを防止できることがわかります。MAP_POPULATEmmap

MAP_POPULATE (since Linux 2.5.46): マッピング用のページ表を移入(事前フォルト)します。ファイル マッピングの場合、これによりファイルの先読みが発生します。マッピングへのその後のアクセスは、ページ フォールトによってブロックされません。

このようにして、ページ フォールトのプリロード スレッド (Jens の提案による) は廃止されます。

編集: まず、意味のある結果を得るために、作成するベンチマークはページ キャッシュをフラッシュして実行する必要があります。

    echo 3 | sudo tee /proc/sys/vm/drop_caches

さらに: MADV_WILLNEEDwith のアドバイスmadviseは必要なページを pre-fault します ( POSIX_FADV_WILLNEEDwith fadvise と同じ)。残念ながら、現在のところ、これらの呼び出しは要求されたページが障害になるまでブロックされます。しかし、フォールト前のリクエストをカーネルのワークキューにキューイングして、これらの呼び出しを予想どおり非同期にするカーネルパッチが進行中です。別の先読みユーザースペーススレッドを廃止します。

于 2013-01-19T21:27:26.300 に答える