Java NIO FileChannel 및 FileOutputstream 성능 / 유용성


169

nio FileChannel와 normal FileInputStream/FileOuputStream을 사용 하여 파일을 읽고 파일 시스템에 쓸 때 성능 (또는 장점)에 차이가 있는지 알아 내려고합니다 . 나는 내 컴퓨터에서 동일한 수준에서 수행하고 여러 번 FileChannel속도가 느려지는 것을 관찰했습니다 . 이 두 가지 방법을 비교하는 자세한 내용을 알고 싶습니다. 내가 사용한 코드는 다음과 같습니다 350MB. 테스트하는 파일은 다음과 같습니다 . 임의 액세스 또는 기타 고급 기능을보고 있지 않은 경우 파일 I / O에 NIO 기반 클래스를 사용하는 것이 좋은 옵션입니까?

package trialjavaprograms;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class JavaNIOTest {
    public static void main(String[] args) throws Exception {
        useNormalIO();
        useFileChannel();
    }

    private static void useNormalIO() throws Exception {
        File file = new File("/home/developer/test.iso");
        File oFile = new File("/home/developer/test2");

        long time1 = System.currentTimeMillis();
        InputStream is = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(oFile);
        byte[] buf = new byte[64 * 1024];
        int len = 0;
        while((len = is.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        fos.flush();
        fos.close();
        is.close();
        long time2 = System.currentTimeMillis();
        System.out.println("Time taken: "+(time2-time1)+" ms");
    }

    private static void useFileChannel() throws Exception {
        File file = new File("/home/developer/test.iso");
        File oFile = new File("/home/developer/test2");

        long time1 = System.currentTimeMillis();
        FileInputStream is = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(oFile);
        FileChannel f = is.getChannel();
        FileChannel f2 = fos.getChannel();

        ByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024);
        long len = 0;
        while((len = f.read(buf)) != -1) {
            buf.flip();
            f2.write(buf);
            buf.clear();
        }

        f2.close();
        f.close();

        long time2 = System.currentTimeMillis();
        System.out.println("Time taken: "+(time2-time1)+" ms");
    }
}

5
transferTo/ transferFrom는 파일 복사에 더 일반적입니다. 한 번에 작은 덩어리를 읽고 머리가 너무 많은 시간을 소비하면 문제가 발생할 수 있지만 하드 드라이브를 더 빠르거나 느리게 만들지 않는 기술은 무엇입니까?
Tom Hawtin-tackline

1
(사용중인 OS 또는 JRE 공급 업체 및 버전은 언급하지 않았습니다.)
Tom Hawtin-tackline

죄송합니다. FC10을 Sun JDK6과 함께 사용하고 있습니다.
Keshav

답변:


202

더 큰 파일 크기에 대한 나의 경험 java.nio은보다 빠릅니다 java.io. 견고하게 빠릅니다. > 250 % 범위 에서처럼. 즉, 명백한 병목 현상을 제거하고 있습니다. 조사를위한 잠재적 영역 :

버퍼 크기 기본적으로 가지고있는 알고리즘은

  • 디스크에서 버퍼로 복사
  • 버퍼에서 디스크로 복사

내 자신의 경험이 버퍼 크기인지하고있다 익은 튜닝. 내 응용 프로그램의 한 부분은 4KB, 다른 부분은 256KB로 정했습니다. 귀하의 코드가 큰 버퍼로 고통 받고 있다고 생각합니다. 1KB, 2KB, 4KB, 8KB, 16KB, 32KB 및 64KB의 버퍼로 일부 벤치 마크를 실행하여 스스로 입증하십시오.

동일한 디스크에서 읽고 쓰는 Java 벤치 마크를 수행하지 마십시오.

그렇다면 실제로 디스크가 아닌 디스크 벤치마킹입니다. 또한 CPU가 사용 중이 아닌 경우 다른 병목 현상이 발생했을 수 있습니다.

필요하지 않은 경우 버퍼를 사용하지 마십시오.

대상이 다른 디스크 또는 NIC 인 경우 왜 메모리에 복사합니까? 파일 크기가 클수록 발생하는 대기 시간은 그리 간단하지 않습니다.

다른 사람들이 말했듯이 FileChannel.transferTo()또는을 사용하십시오 FileChannel.transferFrom(). 여기서 주요 장점은 JVM이있는 경우 DMA에 대한 OS의 액세스 ( 직접 메모리 액세스 ) 를 사용한다는 것 입니다. (이는 구현에 따라 다르지만 범용 CPU의 최신 Sun 및 IBM 버전을 사용하는 것이 좋습니다.) 데이터는 디스크를 통해 버스에서 버스로, 목적지로 곧바로 전달됩니다. RAM 또는 CPU.

밤낮으로 일한 웹 응용 프로그램은 IO가 무겁습니다. 마이크로 벤치 마크 및 실제 벤치 마크도 수행했습니다. 그리고 결과는 내 블로그에 올라 있습니다.

생산 데이터 및 환경 사용

마이크로 벤치 마크는 왜곡되기 쉽습니다. 가능하면 원하는 하드웨어를 사용하여 원하는로드를 정확히 수행하려는 작업에서 데이터를 수집하십시오.

내 벤치 마크는 프로덕션 시스템, 강력한 시스템,로드중인 시스템에서 로그로 수집 되었기 때문에 견고하고 안정적입니다. JVM이 하드 디스크를 작동하는 동안 강렬하게 관찰하는 동안 내 노트북의 7200 RPM 2.5 "SATA 드라이브가 아닙니다 .

무엇을하고 있습니까? 그것은 중요.


@Stu Thompson-게시물 주셔서 감사합니다. 나는 같은 주제에 대해 연구하고 있기 때문에 당신의 대답을 발견했습니다. nio가 Java 프로그래머에게 제공하는 OS 개선 사항을 이해하려고합니다. 그중 몇 가지는 DMA 및 메모리 매핑 파일입니다. 그러한 개선 사항을 더 많이 경험하셨습니까? PS-블로그 링크가 깨졌습니다.
Andy Dufresne

@AndyDufresne 내 블로그가 현재 다운되었으며, 이번 주 후반에 블로그를 이동할 예정입니다.
Stu Thompson


1
한 디렉토리에서 다른 디렉토리로 파일을 복사하는 것은 어떻습니까? (각각 다른 디스크 드라이브)
Deckard

1
흥미
롭다

38

비교하려는 것이 파일 복사 성능 인 경우 채널 테스트의 경우 다음을 수행해야합니다.

final FileInputStream inputStream = new FileInputStream(src);
final FileOutputStream outputStream = new FileOutputStream(dest);
final FileChannel inChannel = inputStream.getChannel();
final FileChannel outChannel = outputStream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
inputStream.close();
outputStream.close();

이것은 한 채널에서 다른 채널로 자신을 버퍼링하는 것보다 느리지 않으며 잠재적으로 훨씬 빠릅니다. Javadocs에 따르면 :

많은 운영 체제는 실제로 복사하지 않고 파일 시스템 캐시에서 대상 채널로 바이트를 직접 전송할 수 있습니다.


7

내 테스트 (Win7 64bit, 6GB RAM, Java6)를 기반으로 NIO transferFrom은 작은 파일에서만 빠르며 큰 파일에서는 매우 느립니다. NIO 데이터 버퍼 플립은 항상 표준 IO보다 성능이 뛰어납니다.

  • 1000x2MB 복사

    1. NIO (transferFrom) ~ 2300ms
    2. NIO (직접 데이터 버퍼 5000b 플립) ~ 3500ms
    3. 표준 IO (버퍼 5000b) ~ 6000ms
  • 100x20mb 복사

    1. NIO (직접 데이터 버퍼 5000b 플립) ~ 4000ms
    2. NIO (transferFrom) ~ 5000ms
    3. 표준 IO (버퍼 5000b) ~ 6500ms
  • 1x1000mb 복사

    1. NIO (직접 데이터 버퍼 5000b 플립) ~ 4500 초
    2. 표준 IO (버퍼 5000b) ~ 7000ms
    3. NIO (transferFrom) ~ 8000ms

transferTo () 메소드는 파일 청크에서 작동합니다. 고급 파일 복사 방법으로 사용되지 않았습니다 : Windows XP에서 큰 파일을 복사하는 방법?


6

질문의 "유용성"부분에 대한 답변 :

FileChannelover 를 사용 하는 것의 미묘한 단점 중 하나 는 중단 된 상태 의 스레드에서 FileOutputStream차단 작업 (예 : read()또는 write()) 을 수행 하면 채널이 갑자기 닫히는 것입니다 .java.nio.channels.ClosedByInterruptException

이제 FileChannel사용 된 것이 스레드의 주요 기능의 일부이며 디자인이이를 고려 한다면 이것은 좋은 일이 될 수 있습니다 .

그러나 로깅 기능과 같은 일부 보조 기능에서 사용하는 경우 성 가실 수도 있습니다. 예를 들어, 중단 된 스레드에 의해 로깅 기능이 호출되면 로깅 출력이 갑자기 닫히는 것을 알 수 있습니다.

불행히도 이것을 설명하지 않으면 쓰기 무결성에 영향을 미치는 버그가 발생할 수 있기 때문에 미묘합니다. [1] [2]


3

base64로 인코딩 된 파일을 디코딩하기 위해 FileInputStream 대 FileChannel의 성능을 테스트했습니다. 내 경험상 나는 다소 큰 파일을 테스트했으며 전통적인 io는 항상 nio보다 약간 빠릅니다.

FileChannel은 여러 IO 관련 클래스에서 동기화 오버 헤드로 인해 이전 버전의 jvm에서 이점을 보았을 수 있지만 최신 jvm은 불필요한 잠금을 제거하는 데 상당히 좋습니다.


2

transferTo 기능 또는 비 차단 기능을 사용하지 않는 경우 기존 IO가 NIO에 매핑되므로 기존 IO와 NIO (2)의 차이를 알 수 없습니다.

그러나 transferFrom / To와 같은 NIO 기능을 사용하거나 버퍼를 사용하려는 경우 NIO를 사용하는 것이 좋습니다.


0

내 경험에 따르면 NIO는 작은 파일로 훨씬 빠릅니다. 그러나 큰 파일의 경우 FileInputStream / FileOutputStream이 훨씬 빠릅니다.


4
당신은 그 혼합을 얻었습니까? 내 자신의 경험은 더 큰 파일보다 작을 때보 다 java.nio빠릅니다 . java.io
Stu Thompson

아니요, 내 경험은 다른 방향입니다. java.nio파일이 메모리에 매핑 될만큼 작다면 빠릅니다. 더 커지면 (200MB 이상) java.io빠릅니다.
tangens

와. 저의 반대입니다. 파일을 읽기 위해 반드시 파일을 매핑 할 필요는 없습니다 FileChannel.read(). 를 사용하여 파일을 읽는 유일한 방법은 없습니다 java.nio.
Stu Thompson

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