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


156

ファイルから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、および HDDに依存し。他のハードウェア/ソフトウェアも混在している可能性があります。

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

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

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

回答:


213

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

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

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

さて、これは一般的なストリーミングシナリオではかなりオフセットされています。ディスクから読み取られたブロックは、次の読み取りにヒットしたときもメモリ内にあるためです(結局、ここでは順次読み取りを行っています)。 RAMへの支払い->次の読み取り時のL3 / L2キャッシュレイテンシ価格。ただし、ディスク-> RAMレイテンシは支払いません。大きさの点で言うと、ディスクからRAMへのレイテンシは非常に遅いため、処理している可能性のある他のレイテンシはほとんど失われます。

したがって、異なるキャッシュサイズでテストを実行した場合(自分でこれを行っていない場合)、おそらくファイルシステムブロックのサイズまでのキャッシュサイズの大きな影響が見つかると思います。その上で、物事はかなりすぐに横ばいになると思います。

トンあります条件と例外のは、ここで-システムの複雑さは、実際には非常に驚異的(ちょうどL3のハンドルを取得- > L2キャッシュ転送はbogglingly複雑な心であり、それはすべてのCPUの種類によって変化します)。

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


3
携帯電話(Nexus 5X)でAndroidアプリの小さなファイル(3,5Mb)と大きなファイル(175Mb)の両方でいくつかのベンチマークを行いました。ゴールデンサイズは524288バイトの長さのバイト[]になることがわかりました。さて、ファイルサイズに応じて小さなバッファー4Kbと大きなバッファー524Kbを切り替えると、10〜20ミリ秒かかる場合がありますが、その価値はありません。したがって、私の場合、524 Kbが最良のオプションでした。
Kirill Karmazin

19

はい、それはおそらくさまざまな事柄に依存しています-しかし、それが大きな違いをもたらすとは思えません。私は、メモリ使用量とパフォーマンスの間の適切なバランスとして、16Kまたは32Kを選ぶ傾向があります。

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


try..catchに関する投稿を編集しました。実際のコードには1つありますが、投稿を短くするために省略しました。
ARKBAN、2008年

1
固定サイズを定義したい場合、どちらのサイズが良いですか?4k、16k、32k?
BattleTested

2
@MohammadrezaPanahi:アナグマユーザーへのコメントは使用しないでください。2番目のコメントの前に1時間未満待った。ユーザーは簡単に眠ったり、会議に参加したり、基本的に他のことで忙しく、コメントに答える義務がないことを覚えておいてください。しかし、あなたの質問に答えるには、それは完全に文脈に依存します。メモリに非常に制約のあるシステムで実行している場合は、おそらく小さなバッファが必要です。大規模なシステムで実行している場合、より大きなバッファーを使用すると、読み取り呼び出しの数が減ります。ケビン・デイの答えはとても良いです。
Jon Skeet

7

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


4

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


ファイルの読み取りと書き込みに最適なバッファーサイズは4kですか?
2018

4

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

私は、BufferedXStreamsがバッファーサイズとして8192を使用していると思いますが、Ovidiuが言ったように、オプション全体でテストを実行する必要があります。実際のところ、最適なサイズはファイルシステムとディスクの構成によって異なります。


4

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


4

BufferedInputStreamのソースには、次のものが見つかります。private static int DEFAULT_BUFFER_SIZE = 8192;
したがって、そのデフォルト値を使用することは問題ありません。
しかし、さらに多くの情報を把握できれば、より価値のある答えが得られます。
たとえば、TCP / IPのペイロードが原因で、adslが1454バイトのバッファを優先する場合があります。ディスクの場合は、ディスクのブロックサイズと一致する値を使用できます。


1

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

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

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

(うーん...複数のコアを使用すると、スレッドが役立つ場合があります。)


0

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

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.