문제

파일에서 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와 같은 항목이 누락되어 있습니다.

도움이 되었습니까?

해결책

최적 버퍼 크기는 파일 시스템 블록 크기, CPU 캐시 크기 및 캐시 대기 시간의 여러 가지와 관련이 있습니다.

대부분의 파일 시스템은 4096 또는 8192의 블록 크기를 사용하도록 구성됩니다. 이론적으로 버퍼 크기를 구성하여 디스크 블록보다 몇 바이트를 더 많이 읽는 경우 파일 시스템을 사용한 작업이 매우 비효율적 일 수 있습니다 (예 : 경우 버퍼가 한 번에 4100 바이트를 읽도록 구성했으며, 각각의 읽기는 파일 시스템에 의해 2 개의 블록 읽기가 필요합니다). 블록이 이미 캐시에 있으면 RAM-> L3/L2 캐시 대기 시간의 가격을 지불합니다. 당신이 운이 좋지 않고 블록이 아직 캐시에 있지 않은 경우, 당신은 디스크> RAM 대기 시간의 가격도 지불합니다.

그렇기 때문에 대부분의 버퍼는 크기가 2의 전력으로, 일반적으로 디스크 블록 크기보다 (또는 동일) 크기로 간주됩니다. 즉, 스트림 읽기 중 하나가 여러 디스크 블록 읽기를 초래할 수 있지만 읽기는 항상 전체 블록을 사용합니다. 낭비되지 않습니다.

다음 읽기를 누르면 디스크에서 읽은 블록이 여전히 메모리에있을 것이기 때문에 (결국 여기에서 순차적 읽기를하고 있습니다), 이제는 정면으로 설정되어 있기 때문에 이것은 일반적인 스트리밍 시나리오에서 상당히 오프셋됩니다. 다음 읽기에서 RAM-> L3/L2 캐시 대기 시간 가격을 지불하지만 디스크 -> RAM 대기 시간은 아닙니다. 크기의 측면에서, 디스크-> RAM 대기 시간은 너무 느리기 때문에 다루고있는 다른 대기 시간을 거의 늪에 빠뜨립니다.

따라서 캐시 크기가 다른 테스트를 실행 한 경우 (직접 수행하지 않았 음) 캐시 크기가 파일 시스템 블록 크기까지 큰 영향을 줄 수 있다고 생각합니다. 그 위에서, 나는 상황이 꽤 빨리 수평이 될 것이라고 생각합니다.

A가 있습니다 여기서 조건과 예외의 - 시스템의 복잡성은 실제로 매우 비틀 거리며 (L3-> L2 캐시 전송을 처리하는 것은 끔찍한 복잡하고, 모든 CPU 유형에 따라 변경).

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

다른 팁

그렇습니다. 아마도 다양한 것들에 의존 할 것입니다. 그러나 나는 그것이 매우 큰 차이를 만들 것이라고 의심합니다. 메모리 사용량과 성능 사이의 균형으로 16K 또는 32K를 선택하는 경향이 있습니다.

예외가 발생하더라도 스트림이 닫히도록 코드에 시도/마지막으로 차단해야합니다.

대부분의 경우 실제로 그렇게 중요하지 않습니다. 4K 또는 16K와 같은 좋은 크기를 선택하고 고수하십시오. 당신이 긍정적인 이것은 응용 프로그램의 병목 현상이므로 최적의 버퍼 크기를 찾기 위해 프로파일 링을 시작해야합니다. 너무 작은 크기를 선택하면 추가 I/O 작업 및 추가 기능 호출을 수행하는 데 시간을 낭비합니다. 너무 큰 크기를 선택하면 많은 캐시 누락이 보이기 시작하여 실제로 속도를 늦출 것입니다. L2 캐시 크기보다 더 큰 버퍼를 사용하지 마십시오.

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

버퍼링 스트림/리더를 사용한 다음 버퍼 크기를 사용할 수 있습니다.

BufferedxStreams는 8192를 버퍼 크기로 사용하고 있다고 생각하지만 Ovidiu가 말했듯이 모든 옵션에 대한 테스트를 실행해야합니다. 그것은 실제로 가장 좋은 크기가 무엇인지에 대한 파일 시스템 및 디스크 구성에 달려 있습니다.

Java Nio의 FileChannel 및 MappedByTeBuffer를 사용하는 파일을 읽으면 FileInputStream과 관련된 솔루션보다 훨씬 빠른 솔루션이 발생할 수 있습니다. 기본적으로 메모리 매핑 큰 파일은 작은 파일에 직접 버퍼를 사용합니다.

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

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

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

또는 프로그램은 MessageDigest.update () 안에 CPU가 묶여 있으며 대부분의 시간은 응용 프로그램 코드에 소비되지 않으므로 조정하는 데 도움이되지 않습니다.

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

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

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

대부분의 기본 하드웨어는 2의 전력 인 FLO BLOCK 및 캐시 크기로 구성되기 때문에 버퍼 크기에 대해 2의 전력을 선택하는 것이 일반적입니다. 버퍼 클래스를 사용하면 생성자의 버퍼 크기를 지정할 수 있습니다. 아무도 제공되지 않으면 대부분의 JVM에서 2의 전력 인 기본값을 사용합니다.

선택한 버퍼 크기에 관계없이 가장 큰 성능이 증가하면 버퍼가없는 파일 액세스로 이동하는 것입니다. 버퍼 크기를 조정하면 성능이 약간 향상 될 수 있지만 매우 작거나 매우 큰 버퍼 크기를 사용하지 않는 한 중요한 영향을 미치지 않을 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top