자바 파일복사 코드와 성능 2 :: Java File Copy Code & Perfomance Issue. part 2

2008/07/13 21:21

서비 JAVA , , , , , ,

지난 포스트를 통해 자바로 파일을 복사하는 몇가지 방법을 알아보았다.
이번시간에는 각 코드의 성능을 간단히 확인해 보고자 한다.

[code]
/*
 * author 신윤섭
 */
package filecopy;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Stream을 이용한 파일복사 코드 스니핏
 * @author 신윤섭
 */
public class StreamCopy {

 /**
  * source에서 target으로의 파일 복사
  * @param source
  * @param target
  */
 public void copy(String source, String target) {
  //복사 대상이 되는 파일 생성
  File sourceFile = new File( source );
 
  //스트림 선언
  FileInputStream inputStream = null;
  FileOutputStream outputStream = null;
 
  try {
   //스트림 생성
   inputStream = new FileInputStream(sourceFile);
   outputStream = new FileOutputStream(target);
   
   int bytesRead = 0;
   //인풋스트림을 아웃픗스트림에 쓰기
   byte[] buffer = new byte[1024];  
   while ((bytesRead = inputStream.read(buffer, 0, 1024)) != -1) {
    outputStream.write(buffer, 0, bytesRead);
   }
   
  } catch (Exception e) {
   e.printStackTrace();
  }finally{
   //자원 해제
   try{
    outputStream.close();
   }catch(IOException ioe){}
   try{
    inputStream.close();
   }catch(IOException ioe){}
  }

 }
}
[/code]
[code]
/*
 * author 신윤섭
 */
package filecopy;

import java.io.File;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Buffer를 이용한 파일복사 코드 스니핏
 * @author 신윤섭
 */
public class BufferCopy {

 /**
  * source에서 target으로의 파일 복사
  * @param source
  * @param target
  */
 public void copy(String source, String target) {
  //복사 대상이 되는 파일 생성
  File sourceFile = new File( source );
 
  //스트림, 버퍼 선언
  FileInputStream inputStream = null;
  FileOutputStream outputStream = null;
  BufferedInputStream bin = null;
  BufferedOutputStream bout = null;
 
  try {
   //스트림 생성
   inputStream = new FileInputStream(sourceFile);
   outputStream = new FileOutputStream(target);
   //버퍼 생성
   bin = new BufferedInputStream(inputStream);
   bout = new BufferedOutputStream(outputStream);
   
   //버퍼를 통한 스트림 쓰기
   int bytesRead = 0;
   byte[] buffer = new byte[1024];
   while ((bytesRead = bin.read(buffer, 0, 1024)) != -1) {
    bout.write(buffer, 0, bytesRead);
   }

  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   //자원 해제
   try{
    outputStream.close();
   }catch(IOException ioe){}
   try{
    inputStream.close();
   }catch(IOException ioe){}
   try{
    bin.close();
   }catch(IOException ioe){}
   try{
    bout.close();
   }catch(IOException ioe){}
  }
 }
}
[/code]
[code]
/*
 * author 신윤섭
 */
package filecopy;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.io.IOException;

/**
 * NIO Channel을 이용한 파일복사 코드 스니핏
 * @author 신윤섭
 */
public class ChannelCopy {

 /**
  * source에서 target으로의 파일 복사
  * @param source 복사할 파일명을 포함한 절대 경로
  * @param target 복사될 파일명을 포함한 절대경로
  */
 public void copy(String source, String target) {
  //복사 대상이 되는 파일 생성
  File sourceFile = new File( source );

  //스트림, 채널 선언
  FileInputStream inputStream = null;
  FileOutputStream outputStream = null;
  FileChannel fcin = null;
  FileChannel fcout = null;

  try {
   //스트림 생성
   inputStream = new FileInputStream(sourceFile);
   outputStream = new FileOutputStream(target);
   //채널 생성
   fcin = inputStream.getChannel();
   fcout = outputStream.getChannel();
   
   //채널을 통한 스트림 전송
   long size = fcin.size();
   fcin.transferTo(0, size, fcout);

  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   //자원 해제
   try{
    fcout.close();
   }catch(IOException ioe){}
   try{
    fcin.close();
   }catch(IOException ioe){}
   try{
    outputStream.close();
   }catch(IOException ioe){}
   try{
    inputStream.close();
   }catch(IOException ioe){}
  }
 }
}
[/code]

이상의 샘플코드를 이용하여 700Mbytes 짜리 파일을 5번 복사하여 그 시간을 측정해 본 결과는 아래와 같다.
테스트는 Win XP Pro sp3, Intel Core2 Duo 2GHz, 2Gbytes, 5400rpm의 노트북용 HDD 에서 JDK 1.6.0_06
을 이용하여 이루어졌다.

결과는 아래와 같다.

스트림을 이용한 파일 카피

Stream을 이용한 파일 복사


버퍼를 이용한 파일 카피

Buffer를 이용한 파일 복사


채널을 이용한 파일 카피

Channel을 이용한 파일 복사



프로파일러를 이용하여 측정한 값이기 때문에 프로파일러의 처리량 만큼의 차이는 있겠지만 상대적 성능 비교
에는 문제가 없으리라 생각 된다.
실측에서도 예상대로 Stream , Buffer, Channel 순으로 파일 복사 시간이 줄어들고 있음을 볼 수있다.

이번에는 조금 더 들여다 보기로 하자. 700m짜리 파일을 한번 복사하는데 어떤 클래스와 메소들이 참여하고
있는지, 그리고 메소드가 몇번이나 호출되고 있는지 확인 해 보는것도 재미있을 것이다.

스트림이용

FileInputStream.read()실행에 대부분의 시간을 소비하고 있다.


스트림을 이용한 파일복사이다. FileInputStream.read()메소드가 71만여번 호출되고있으며 실행시간의 대부분을
이 메소드를 실행 하는데 소비하고 있음을 알 수 있다.

버퍼이용

Stream보다는 나아졌다고는 하나 역시 read()메소드가 대부분의 실행시간을 소비하고 있다.


Buffer를 이용한 방법. 위의 Stream을 이용한 방법에 비해 수행시간이 약간은 줄어들었지만 이는 Buffer를 활용
함으로써 FileOutputStream.write() 수행시간을 줄인데 따른 성능 향상이며, FileInputStream.read()메소드는
약 9만번 호출되고 있다.

채널이용

위 두 방식과는 확연히 다른 동작 성향을 보여주고 있다.


마지막으로 채널을 이용한 파일복사의 경우 위 두 경우와 비교하여 호출되는 메소드나 호출횟수등 전혀 다른
동작 성향을 보이고 있다. read 도 하지 않은채 FileDispatcher.write() 메소드를 단 한번 호출 하는것으로 파일
복사를 끝내고 있다. 이 FileDispatcher.write() 하부구조에서는 OS의 네이티브IO를 호출하고 있으리라 미루어
짐작할 수 있다.

이상으로 파일복사(스트림전송)의 세가지 방식과 그 성능에 대해 간략하게 알아보았다.
위 실험 결과는 크기가 비교적 큰 파일의 복사에서 나타나는 성향며, 다수의 작은 크기의 파일을 복사한다면
그 결과가 달라질 수도 있음을 밝혀둔다.

io작업이 필요한데 JDK 1.4 이상의 버전을 이용할 수 있다면 나은 성능을 보장하는 nio를 사용하지
않을 이유가 없어보인다.

2008/07/13 21:21 2008/07/13 21:21
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. 안녕하세요 ^^
    글이 내용이 유용해서 좋아서 제가 퍼갔습니다. ^^
    문제가 있으시다면, 언제든지 알려 주세요 ^^

    링크는 다음과 같습니다. ^^
    http://cafe.naver.com/javacircle/31059

  2. Blog Icon
    서비

    도움이 되셨다니 기쁩니다.
    담아가셔도 크게 개의치 않습니다. ^^

  3. 감사합니다 ^^

  4. Blog Icon
    lahuman

    늘 사용하는 Stream 관련 좋은 정보 감사합니다.

  5. Blog Icon
    야기

    자바 성능검사를 어떻게 하셨는지 궁금합니다 ^^;

  6. Blog Icon
    서비

    글에 관심가져 주셔서 감사합니다.
    해당 프로파일러에 대한 정보는 http://www.yunsobi.com/blog/462
    글을 참고해 주시면 감사하겠습니다.

  7. Blog Icon

    생각보다 성능차이가 많이 나네요. 유용한 자료 잘 보고 갑니다.

    아 그리고 BufferedInputStream 생성자의 인자로 넘겨준 InputStream은 BufferedInputStream의 close()가 호출 될 때
    같이 close 됩니다. Channel 의 경우도 Lazy pattern 으로 생성되었다가 InputStream이 close 될 때 같이 close 되네요.
    이 글을 보고 혹시나 해서 궁금해서 소스를 뒤져보니 그렇게 되어있네요. 덕분에 배우고 갑니다 ^^

  8. Blog Icon
    컴장이

    좋은글 퍼갈께요.
    출처는 남기겠습니다.
    감사합니다.

  9. Blog Icon
    엘리냥

    파일 복사로 고민하던 차에 좋은 정보 얻어갑니다!!
    저런 성능 테스트 부분은 정말 유용하네요.
    성능 테스트하는 것도 더 알아봐야겠어요~

  10. Blog Icon
    노란사자

    좋은글 감사합니다.
    출처를 표기하고 퍼가겟습니다.

  11. Blog Icon
    이홍재

    좋은글 감사합니다.

    해당사항 담아갈게요.

    출처표기하겠습니다.

    담아가는 곳은 http://blog.naver.com/hongjae83

    문제가 될시 삭제하겠습니다.

    감사합니다.

  12. 좋은 정보 공유해주셔서 감사합니다

  13. Blog Icon
    jini

    좋은 정보 감사합니다.

  14. Blog Icon
    sbkim

    감사합니다^^~~~

  15. 감사합니다.
    http://blog.daum.net/andro_java/210
    채널카피 올렸습니다.

  16. 감사합니다. 많은 도움이 되었습니다.
    퍼갈께요. http://cafe.naver.com/stweb

  17. Blog Icon
    지나가던개발자

    이걸 안드로이드에 적용시켰더니 파일 복사 속도가 대략 3분의 1로 줄었습니다. 엄청난 성능입니다. 감사합니다.