web-dev-qa-db-ja.com

FileInputStreamを使用する場合、理想的なバッファサイズをどのように決定しますか?

ファイルから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、FileSystem、andHDDに依存していると確信しています。

(私はJavaにいくらか慣れていないことを指摘する必要があるので、これは単なるJava AP​​I呼び出しかもしれません。)

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

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

141
ARKBAN

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

ほとんどのファイルシステムは、4096または8192のブロックサイズを使用するように設定されています。理論的には、ディスクブロックよりも数バイト多く読み込むようにバッファサイズを設定すると、ファイルシステムの操作は非常に非効率的になります(つまり、一度に4100バイトを読み取るようにバッファを設定すると、各読み取りにはファイルシステムによる2つのブロック読み取りが必要になります)。ブロックがすでにキャッシュにある場合、RAM-> L3/L2キャッシュレイテンシの代価を支払うことになります。運が悪く、ブロックがまだキャッシュにない場合は、ディスク-> RAMレイテンシの代価も支払います。

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

今、これは典型的なストリーミングシナリオでかなり相殺されます。なぜなら、ディスクから読み取られたブロックは、次の読み取りにヒットしてもメモリ内に残っているからです(結局、ここでシーケンシャル読み取りを行っています)。次の読み取りでRAM-> L3/L2キャッシュレイテンシの価格を支払いますが、ディスク-> RAMのレイテンシは支払いません。大きさの観点から見ると、ディスク-> RAMのレイテンシは非常に遅いため、処理している他のレイテンシをほとんど圧倒します。

そのため、さまざまなキャッシュサイズでテストを実行した場合(これを自分で行っていない場合)、キャッシュサイズがファイルシステムブロックのサイズまで大きな影響を与える可能性があります。その上で、私は物事がかなり急速に平準化すると思う。

ここにtonの条件と例外があります-システムの複雑さは実際には非常に驚異的です(L3-> L2キャッシュ転送のハンドルを取得するのは気が遠くなるほど複雑で、すべてのCPUタイプで変化します) )。

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

198
Kevin Day

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

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

15
Jon Skeet

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

7
Adam Rosenfield

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

4
Ovidiu Pacurar

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

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

4
John Gardner

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

4
Alexander

BufferedInputStreamのソースには以下が含まれています。private static int DEFAULT_BUFFER_SIZE = 8192;
したがって、デフォルト値を使用するのは適切です。
しかし、さらに多くの情報を把握できれば、より価値のある回答が得られます。
たとえば、adslはTCP/IPのペイロードのため、1454バイトのバッファーを優先する場合があります。ディスクの場合、ディスクのブロックサイズに一致する値を使用できます。

3
GoForce5500

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

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

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

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

1
Maglob

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

これは、ファイルシステムのブロックサイズやCPUハードウェアなどの多くの要因に依存します。

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

どのバッファサイズを選択しても、最大のパフォーマンス向上は、非バッファファイルアクセスからバッファファイルアクセスへの移行です。バッファサイズを調整するとパフォーマンスがわずかに向上する場合がありますが、極端に小さいまたは極端に大きいバッファサイズを使用している場合を除き、大きな影響はありません。

0
Adrian Krebs