168

ファイルから MessageDigest (ハッシュ) を作成するメソッドがあり、これを多数のファイル (>= 100,000) に対して行う必要があります。パフォーマンスを最大化するには、ファイルからの読み取りに使用するバッファーをどのくらい大きくする必要がありますか?

ほとんどの人は基本的なコードに精通しています (念のためここで繰り返します):

MessageDigest md = MessageDigest.getInstance( "SHA" );
FileInputStream ios = new FileInputStream( "myfile.bmp" );
byte[] buffer = new byte[4 * 1024]; // what should this value be?
int read = 0;
while( ( read = ios.read( buffer ) ) > 0 )
    md.update( buffer, 0, read );
ios.close();
md.digest();

スループットを最大化するためのバッファの理想的なサイズは? これはシステムに依存していることはわかっています。OS、ファイルシステム、およびHDD に依存していると確信しており、他のハードウェア/ソフトウェアが混在している可能性があります。

(私は Java に少し慣れていないので、これは私が知らない Java API 呼び出しである可能性があることを指摘しておく必要があります。)

編集:これが使用されるシステムの種類を事前に知らないため、多くを想定することはできません。(そのためJavaを使用しています。)

編集:上記のコードには、投稿を小さくするためのtry..catchなどのものがありません

4

9 に答える 9

226

最適なバッファ サイズは、ファイル システムのブロック サイズ、CPU キャッシュ サイズ、キャッシュ レイテンシなど、さまざまな要素に関連しています。

ほとんどのファイル システムは 4096 または 8192 のブロック サイズを使用するように構成されています。一度に 4100 バイトを読み取るようにバッファを構成した場合、各読み取りにはファイル システムによる 2 つのブロック読み取りが必要になります)。ブロックがすでにキャッシュにある場合は、RAM の代償を払うことになります -> L3/L2 キャッシュのレイテンシー。運が悪く、ブロックがまだキャッシュにない場合は、ディスクから RAM へのレイテンシも犠牲になります。

これが、ほとんどのバッファーのサイズが 2 の累乗であり、通常はディスク ブロック サイズよりも大きい (または等しい) ことを示している理由です。これは、ストリーム読み取りの 1 つが複数のディスク ブロック読み取りになる可能性があることを意味しますが、それらの読み取りは常に完全なブロックを使用し、無駄な読み取りはありません。

さて、これは典型的なストリーミング シナリオではかなりオフセットされます。これは、ディスクから読み取られたブロックが、次の読み取りにヒットしたときにメモリ内に残っているためです (結局、ここでは順次読み取りを行っています)。次の読み取りで RAM -> L3/L2 キャッシュ レイテンシーの料金を支払いますが、ディスク ->RAM レイテンシーは支払いません。桁違いに言えば、ディスクから RAM へのレイテンシは非常に遅いため、対処している可能性のある他のレイテンシをほとんど圧倒します。

したがって、さまざまなキャッシュ サイズでテストを実行した場合 (自分でこれを行ったことはありません)、ファイル システム ブロックのサイズまでキャッシュ サイズの大きな影響が見られると思います。それを超えると、物事はすぐに平準化すると思います。

ここにはたくさん条件と例外があります - システムの複雑さは実際には非常に驚異的です (L3 -> L2 キャッシュ転送のハンドルを取得するだけでも気が遠くなるほど複雑で、CPU の種類ごとに異なります)。

これは「現実世界」の答えにつながります。アプリが 99% の状態である場合は、キャッシュ サイズを 8192 に設定して先に進みます (さらに良いのは、パフォーマンスよりもカプセル化を選択し、BufferedInputStream を使用して詳細を非表示にすることです)。ディスク スループットに大きく依存している 1% のアプリに属している場合は、さまざまなディスク インタラクション戦略を交換できるように実装を作成し、ノブとダイヤルを提供して、ユーザーがテストして最適化できるようにします (またはいくつかを考え出すことができます)。自己最適化システム)。

于 2008-10-26T03:44:20.997 に答える
21

はい、おそらくさまざまなことに依存していますが、それが大きな違いを生むとは思えません。私は、メモリ使用量とパフォーマンスのバランスを考慮して、16K または 32K を選択する傾向があります。

例外がスローされた場合でもストリームが確実に閉じられるように、コードに try/finally ブロックが必要であることに注意してください。

于 2008-10-25T19:21:21.090 に答える
9

ほとんどの場合、実際にはそれほど重要ではありません。4K や 16K などの適切なサイズを選択して、それをそのまま使用してください。これがアプリケーションのボトルネックであると確信している場合は、プロファイリングを開始して最適バッファー サイズを見つける必要があります。小さすぎるサイズを選択すると、余分な I/O 操作と余分な関数呼び出しを行うために時間を浪費することになります。大きすぎるサイズを選択すると、多くのキャッシュ ミスが発生し、速度が大幅に低下します。L2 キャッシュ サイズより大きいバッファを使用しないでください。

于 2008-10-25T20:49:46.607 に答える
5

理想的なケースでは、1 回の読み取り操作でファイルを読み取るのに十分なメモリが必要です。システムに File System 、アロケーション ユニット、および HDD を自由に管理させるため、これが最高のパフォーマーになります。実際には、ファイル サイズを事前に知っていると幸いです。平均ファイル サイズを 4K に切り上げて使用してください (NTFS の既定の割り当て単位)。そして何よりも、複数のオプションをテストするためのベンチマークを作成してください。

于 2008-10-25T20:00:23.147 に答える
4

Java NIO の FileChannel と MappedByteBuffer を使用してファイルを読み取ると、FileInputStream を含むどのソリューションよりもはるかに高速なソリューションが得られる可能性が高くなります。基本的に、大きなファイルをメモリマップし、小さなファイルには直接バッファを使用します。

于 2008-10-25T21:27:18.870 に答える
4

BufferedStreams/readers を使用してから、それらのバッファー サイズを使用できます。

BufferedXStreams はバッファ サイズとして 8192 を使用していると思いますが、Ovidiu が言ったように、おそらく多数のオプションでテストを実行する必要があります。最適なサイズは、ファイルシステムとディスクの構成に大きく依存します。

于 2008-10-25T20:29:51.217 に答える
1

他の回答で既に述べたように、BufferedInputStreams を使用します。

その後、バッファサイズはあまり重要ではないと思います。プログラムが I/O バウンドであり、BIS のデフォルトを超えてバッファ サイズを大きくしても、パフォーマンスに大きな影響はありません。

または、プログラムが MessageDigest.update() 内で CPU バウンドになっていて、ほとんどの時間がアプリケーション コードに費やされていないため、微調整しても役に立ちません。

(うーん...複数のコアでは、スレッドが役立つかもしれません。)

于 2008-10-25T21:20:13.487 に答える
0

1024 はさまざまな状況に適していますが、実際にはバッファ サイズを大きくしたり小さくしたりした方がパフォーマンスが向上する場合があります。

これは、ファイル システムのブロック サイズや CPU ハードウェアなど、さまざまな要因によって異なります。

基盤となるほとんどのハードウェアは 2 のべき乗であるファイル ブロックとキャッシュ サイズで構成されているため、バッファー サイズに 2 のべき乗を選択することも一般的です。 Buffered クラスを使用すると、コンストラクターでバッファー サイズを指定できます。何も指定されていない場合、ほとんどの JVM で 2 の累乗であるデフォルト値が使用されます。

選択したバッファ サイズに関係なく、最大のパフォーマンスの向上は、バッファなしからバッファありのファイル アクセスに移行することです。バッファー サイズを調整すると、パフォーマンスがわずかに向上する可能性がありますが、極端に小さいまたは極端に大きいバッファー サイズを使用していない限り、重大な影響はほとんどありません。

于 2017-01-05T08:06:48.337 に答える