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의 제곱이며 일반적으로 디스크 블록 크기보다 크거나 같습니다. 즉, 스트림 읽기 중 하나에서 여러 디스크 블록 읽기가 발생할 수 있지만 이러한 읽기는 항상 전체 블록을 사용하므로 낭비되는 읽기가 없습니다.

디스크에서 읽은 블록이 다음 읽기에 도달했을 때 여전히 메모리에 남아 있기 때문에 이것은 일반적인 스트리밍 시나리오에서 상당히 상쇄됩니다. 결국 순차 읽기를 수행합니다. 다음 읽기에서 RAM-> L3 / L2 캐시 대기 시간 요금을 지불하지만 디스크-> RAM 대기 시간은 지불하지 않습니다. 크기면에서 디스크-> RAM 대기 시간은 너무 느려서 처리 할 수있는 다른 대기 시간과 거의 비슷합니다.

따라서 다른 캐시 크기로 테스트를 실행 한 경우 (이 작업을 직접 수행하지 않은 경우) 캐시 크기가 파일 시스템 블록 크기에 크게 영향을 줄 수 있습니다. 그 이상으로, 나는 상황이 꽤 빨리 사라질 것이라고 생각합니다.

있다 사실은 꽤 비틀하는 시스템의 복잡성 - 조건과 예외의 여기 (단지 L3에 대한 핸들을 얻기가 -> L2 캐시 전송은 마음 bogglingly 복잡한이며, 모든 CPU 종류로 변경).

이로 인해 '실제'답변이 발생합니다. 앱이 99 % 밖에 없다면 캐시 크기를 8192로 설정하고 계속 진행하십시오 (더 나은 성능은 캡슐화를 선택하고 BufferedInputStream을 사용하여 세부 정보를 숨기십시오). 디스크 처리량에 크게 의존하는 1 %의 앱을 사용하는 경우 다양한 디스크 상호 작용 전략을 교체하고 사용자가 테스트 및 최적화 할 수 있도록 노브와 다이얼을 제공 할 수 있도록 구현을 구성하십시오. 자체 최적화 시스템).


3
작은 파일 (3,5Mb)과 큰 파일 (175 Mb) 모두를 위해 Android 앱의 휴대 전화 (Nexus 5X)에서 약간의 벤치 마크를했습니다. 그리고 황금 크기는 524288 길이의 byte []라는 것을 알았습니다. 파일 크기에 따라 작은 버퍼 4Kb와 큰 버퍼 524Kb 사이를 전환하면 10-20ms를 이길 수 있지만 그만한 가치는 없습니다. 그래서 제 경우에는 524 Kb가 최선의 선택이었습니다.
Kirill Karmazin

19

예, 아마도 여러 가지에 달려있을 것입니다.하지만 그것이 큰 차이를 만들지 않을 것입니다. 16K 또는 32K를 메모리 사용과 성능 사이의 균형으로 선택하는 경향이 있습니다.

예외가 발생하더라도 스트림이 닫히도록하기 위해 코드에 try / finally 블록이 있어야합니다.


try..catch에 대한 게시물을 편집했습니다. 내 실제 코드에는 하나가 있지만 게시물을 더 짧게 만들기 위해 생략했습니다.
ARKBAN

1
고정 크기를 정의하려면 어떤 크기가 더 낫습니까? 4k, 16k 또는 32k?
BattleTested

2
@MohammadrezaPanahi : 오소리 사용자에게 의견을 사용하지 마십시오. 두 번째 코멘트 전에 1 시간 미만을 기다렸습니다 . 사용자는 쉽게 잠들거나 회의를하거나 기본적으로 다른 것들로 바쁠 수 있으며 댓글에 답할 의무가 없습니다. 그러나 귀하의 질문에 대답하십시오 : 그것은 전적으로 상황에 달려 있습니다. 메모리가 매우 제한된 시스템에서 실행중인 경우 작은 버퍼가 필요할 수 있습니다. 큰 시스템에서 실행중인 경우 더 큰 버퍼를 사용하면 읽기 호출 수가 줄어 듭니다. Kevin Day의 답변은 매우 좋습니다.
Jon Skeet

7

대부분의 경우 실제로 그렇게 중요하지 않습니다. 4K 또는 16K와 같은 좋은 크기를 골라 내십시오. 있는 거 당신이 경우 이 응용 프로그램에서 병목 현상입니다, 당신은 최적의 버퍼 크기를 찾기 위해 프로 파일링 시작합니다. 너무 작은 크기를 선택하면 추가 I / O 작업 및 추가 함수 호출을 수행하는 데 시간이 낭비됩니다. 너무 큰 크기를 선택하면 캐시 속도가 크게 저하되어 실제로 속도가 느려집니다. L2 캐시 크기보다 큰 버퍼를 사용하지 마십시오.


4

이상적인 경우에는 한 번의 읽기 작업으로 파일을 읽을 수있는 충분한 메모리가 있어야합니다. 시스템이 File System, 할당 단위 및 HDD를 마음대로 관리 할 수 ​​있기 때문에 이는 최고의 성능입니다. 실제로 파일 크기를 미리 알고 있으면 4K로 반올림 된 평균 파일 크기 (NTFS의 기본 할당 단위) 만 사용하면됩니다. 그리고 무엇보다도 : 여러 옵션을 테스트하기위한 벤치 마크를 만듭니다.


파일에서 읽고 쓰는 데 가장 적합한 버퍼 크기는 4k입니까?
BattleTested

4

BufferedStreams / readers를 사용한 다음 버퍼 크기를 사용할 수 있습니다.

BufferedXStreams가 버퍼 크기로 8192를 사용하고 있다고 생각하지만 Ovidiu가 말했듯이 옵션을 모두 테스트해야 할 것입니다. 실제로 가장 좋은 크기는 파일 시스템과 디스크 구성에 달려 있습니다.


4

Java NIO의 FileChannel 및 MappedByteBuffer를 사용하여 파일을 읽으면 FileInputStream과 관련된 솔루션보다 훨씬 빠른 솔루션이 될 것입니다. 기본적으로 큰 파일을 메모리 매핑하고 작은 파일에는 직접 버퍼를 사용합니다.


4

BufferedInputStream의 소스에서 다음을 찾을 수 있습니다 : private static int DEFAULT_BUFFER_SIZE = 8192;
따라서 기본값을 사용하는 것이 좋습니다.
그러나 더 많은 정보를 알아낼 수 있다면 더 가치있는 답변을 얻을 수 있습니다.
예를 들어, adl은 1454 바이트의 버퍼를 선호 할 수 있습니다. TCP / IP의 페이로드 때문입니다. 디스크의 경우 디스크의 블록 크기와 일치하는 값을 사용할 수 있습니다.


1

다른 답변에서 이미 언급했듯이 BufferedInputStreams를 사용하십시오.

그 후 버퍼 크기는 실제로 중요하지 않다고 생각합니다. 프로그램이 I / O 바운드이고 BIS 기본값보다 버퍼 크기가 커지면 성능에 큰 영향을 미치지 않습니다.

또는 프로그램이 MessageDigest.update () 내에서 CPU에 바인딩되어 있고 대부분의 시간이 응용 프로그램 코드에 사용되지 않으므로 조정해도 도움이되지 않습니다.

(흠 ... 여러 코어가 있으면 스레드가 도움이 될 수 있습니다.)


0

1024는 다양한 환경에 적합하지만 실제로는 더 크거나 작은 버퍼 크기로 더 나은 성능을 볼 수 있습니다.

이는 파일 시스템 블록 크기 및 CPU 하드웨어를 포함한 여러 가지 요인에 따라 다릅니다.

대부분의 기본 하드웨어는 2의 거듭 제곱 블록과 캐시 크기로 구성되므로 버퍼 크기로 2의 거듭 제곱을 선택하는 것이 일반적입니다. 버퍼 된 클래스를 사용하면 생성자에서 버퍼 크기를 지정할 수 있습니다. 제공되지 않은 경우 대부분의 JVM에서 2의 거듭 제곱 인 기본값을 사용합니다.

어떤 버퍼 크기를 선택하든 가장 큰 성능 향상은 버퍼되지 않은 파일 액세스에서 버퍼 된 파일 액세스로 이동하는 것입니다. 버퍼 크기를 조정하면 성능이 약간 향상 될 수 있지만 매우 작거나 큰 버퍼 크기를 사용하지 않으면 큰 영향을 미치지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.