web-dev-qa-db-ja.com

ページキャッシュへのデータの格納を回避しながら、先読みによる読み取りI / Oの最適化

ファイルの内容が再び読み込まれることが予想されないため、またボックスにメモリの負荷があるため、ページキャッシュに読み込まれているデータを保存せずに、ファイルからデータをシーケンシャルに読み込むことができる必要があります。有用なディスクI/Oキャッシュに貴重なメモリを使用します)。

私の質問は、これらの読み取りを最適化する方法についてです。読み取られているデータが順次ディスクに配置されていることを知っているので(断片化を除いて)、(/ sys/block/sda/queue/read_ahead_kbを増やすことで)先読みできるようにしたいのですが、これがposix_fadviseを使用して(POSIX_FADV_DONTNEEDフラグを指定して)、読み取られているデータがページキャッシュに格納されないようにする必要があるため、すべての利点につながります。

ページキャッシュからデータを削除するヒントのために、先読みデータは単に破棄されますか?

4
Stormshadow

direct IO を使用します。

ダイレクトI/Oは、ファイルシステムの機能であり、ファイルの読み取りと書き込みは、オペレーティングシステムの読み取りと書き込みのキャッシュをバイパスして、アプリケーションからストレージデバイスに直接送信されます。直接I/Oは、独自のキャッシュを管理するアプリケーション(データベースなど)でのみ使用されます。

アプリケーションは、_O_DIRECT_フラグを使用してファイルを開くことにより、ダイレクトI/Oを呼び出します。

例えば:

_int fd = open( filename, O_RDONLY | O_DIRECT );
_

直接IOは一風変わっており、いくつかの制限があります。アプリケーションIOバッファはページ揃えにする必要があり、一部のファイルシステムでは各IOリクエストは、ページサイズの正確な倍数にする必要があります。この最後の制限により、ファイルの最後の部分の読み取り/書き込みが困難になる場合があります。

アプリケーションで先読みを処理する簡単なコーディング方法は、fdopenを使用し、_posix_memalign_とsetvbufを使用して大きなページ揃えのバッファーを設定することで実現できます。

_// should really get page size using sysconf()
// but beware of systems with multiple page sizes
#define ALIGNMENT ( 4UL * 1024UL )
#define BUFSIZE ( 1024UL * 1024UL )
char *buffer;
...

int fd = open( filename, O_RDONLY | O_DIRECT );
FILE *file = fdopen( fd, "rb" );

int rc = posix_memalign( &buffer, ALIGNMENT, BUFSIZE );
rc = setvbuf( file, buffer, _IOFBF, BUFSIZE );
_

mmap()を使用して、バッファに使用する匿名メモリを取得することもできます。これには、自然にページが揃うという利点があります。

_...
char *buffer = mmap( NULL, BUFSIZE, PROT_READ | PROT_WRITE,
    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0 );
rc = setvbuf( file, buffer, _IOFBF, BUFSIZE );
_

次に、fileストリームから読み取るfread()/fgets()または任意の_FILE *_タイプの読み取り関数を使用します。

straceなどのツールを使用して、実際のreadシステムコールがページ揃えおよびページサイズのバッファーで実行されていることを確認する必要があります-_FILE *_のCライブラリ実装ベースのストリーム処理はsetvbufで指定されたバッファをIOバッファリングにのみ使用しないため、配置とサイズがオフになる可能性があります。Linux/ glibcはそうではないと思いますただし、チェックしないでサイズや配置がオフの場合、IOの呼び出しは失敗します。

繰り返しになりますが、LinuxダイレクトIOは奇抜な場合があります。ダイレクトIOをサポートしているファイルシステムは一部だけで、一部のファイルシステムは他のものよりも特殊です。[〜#〜 ] test [〜#〜]これを使用する場合は、徹底的にテストします。

ポストされたコードは、ストリームのバッファを埋める必要がある場合は常に1 MBの先読みを行います。スレッドを使用して、より高度な先読みを実装することもできます。1つのスレッドが1つのバッファを満たし、他のスレッドがフルバッファから読み取られます。これにより、先読みが行われるときに「スタッター」の処理が回避されますが、かなり複雑なマルチスレッドコードが大量に使用されます。

4
Andrew Henle

これはよく答えられます このStackOverflow質問で

  • read()の前にファイルオフセットを追跡する

  • read()の後に、データの範囲でfadvise(POSIX_FADV_DONTNEED)を呼び出します[...]

  • 最良の結果を得るには、ページ揃えされたブロックでデータを読み取る必要があります。 I/Oキャッシュはページベースであり、fadvise()は指定されたデータ範囲をページのリストにマップします。ミスアライメントは余分なread() sを引き起こし(そしてパフォーマンスに悪影響を及ぼします)、それ以外は無害です。

メモリを最適化する場合、aheadを読みたくありません。読みたいだけです。ディスクは、データをストリーミングするように指示する必要なく、読み取りを行うと順次動作します。

1
kubanczyk