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");
}
}
답변
더 큰 파일 크기에 대한 나의 경험 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 드라이브가 아닙니다 .
무엇을하고 있습니까? 그것은 중요.
답변
비교하려는 것이 파일 복사 성능 인 경우 채널 테스트의 경우 다음을 수행해야합니다.
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에 따르면 :
많은 운영 체제는 실제로 복사하지 않고 파일 시스템 캐시에서 대상 채널로 바이트를 직접 전송할 수 있습니다.
답변
내 테스트 (Win7 64bit, 6GB RAM, Java6)를 기반으로 NIO transferFrom은 작은 파일에서만 빠르며 큰 파일에서는 매우 느립니다. NIO 데이터 버퍼 플립은 항상 표준 IO보다 성능이 뛰어납니다.
-
1000x2MB 복사
- NIO (transferFrom) ~ 2300ms
- NIO (직접 데이터 버퍼 5000b 플립) ~ 3500ms
- 표준 IO (버퍼 5000b) ~ 6000ms
-
100x20mb 복사
- NIO (직접 데이터 버퍼 5000b 플립) ~ 4000ms
- NIO (transferFrom) ~ 5000ms
- 표준 IO (버퍼 5000b) ~ 6500ms
-
1x1000mb 복사
- NIO (직접 데이터 버퍼 5000b 플립) ~ 4500 초
- 표준 IO (버퍼 5000b) ~ 7000ms
- NIO (transferFrom) ~ 8000ms
transferTo () 메소드는 파일 청크에서 작동합니다. 고급 파일 복사 방법으로 사용되지 않았습니다 :
Windows XP에서 큰 파일을 복사하는 방법?
답변
질문의 “유용성”부분에 대한 답변 :
FileChannel
over 를 사용 하는 것의 미묘한 단점 중 하나 는 중단 된 상태 의 스레드에서 FileOutputStream
차단 작업 (예 : read()
또는 write()
) 을 수행 하면 채널이 갑자기 닫히는 것입니다 .java.nio.channels.ClosedByInterruptException
이제 FileChannel
사용 된 것이 스레드의 주요 기능의 일부이며 디자인이이를 고려 한다면 이것은 좋은 일이 될 수 있습니다 .
그러나 로깅 기능과 같은 일부 보조 기능에서 사용하는 경우 성 가실 수도 있습니다. 예를 들어, 중단 된 스레드에 의해 로깅 기능이 호출되면 로깅 출력이 갑자기 닫히는 것을 알 수 있습니다.
불행히도 이것을 설명하지 않으면 쓰기 무결성에 영향을 미치는 버그가 발생할 수 있기 때문에 미묘합니다. [1] [2]
답변
base64로 인코딩 된 파일을 디코딩하기 위해 FileInputStream 대 FileChannel의 성능을 테스트했습니다. 내 경험상 나는 다소 큰 파일을 테스트했으며 전통적인 io는 항상 nio보다 약간 빠릅니다.
FileChannel은 여러 IO 관련 클래스에서 동기화 오버 헤드로 인해 이전 버전의 jvm에서 이점을 보았을 수 있지만 최신 jvm은 불필요한 잠금을 제거하는 데 상당히 좋습니다.
답변
transferTo 기능 또는 비 차단 기능을 사용하지 않는 경우 기존 IO가 NIO에 매핑되므로 기존 IO와 NIO (2)의 차이를 알 수 없습니다.
그러나 transferFrom / To와 같은 NIO 기능을 사용하거나 버퍼를 사용하려는 경우 NIO를 사용하는 것이 좋습니다.
답변
내 경험에 따르면 NIO는 작은 파일로 훨씬 빠릅니다. 그러나 큰 파일의 경우 FileInputStream / FileOutputStream이 훨씬 빠릅니다.