I/O 퍼포먼스 개선

자바에서는 자료를 읽거나 쓰기 위해 stream을 사용한다. 자바는 두가지 형태의 stream을 지원한다. Readers and Writers와 Input and Output stream이 그것이다. Reader and Writers는 high-level의 I/O(예. String)를 지원하고 Input and Output stream은 low-level의 I/O(byte)를 지원한다. 속도 향상을 위해서는 Buffered stream을 사용한다. Buffered stream을 사용할 경우 버퍼의 기본값은 2K이다. 이 값은 조정될 수 있으나, 자료의 용량이 클 경우 메모리가 많이 필요하기 때문에 Buffered stream을 사용할 경우 여러 가지 사항을 고려해야 한다.

예제1) Simple file copy

public static void copy(String from, String to) throws IOException{
  InputStream in=null;
  OutputStream out=null;
  try{
    in=new FileInputStream(from);
    out=new FileOutputStream(to);
    while(true){
        int data=in.read();
        if(data==-1)
          break;
        out.write(data);
    }
    in.close();
    out.close();
  }finally{
    if(in!=null){in.close();}
    if(out!=null){out.close();}
  }
}

Buffered stream을 사용하지 않고 I/O를 했을 경우의 예제이다. 370K의 JPEG 파일을 복사하는데 10800ms.

예제2) Faster file copy

public static void copy(String from, String to) throws IOException{
  InputStream in=null;
  OutputStream out=null;
  try{
    in=new BufferedInputStream(new FileInputStream(from));
    out=new BufferedOutputStream(new FileOutputStream(to));
    while(true){
      int data=in.read();
      if(data==-1)
        break;
      out.write(data);
    }
  }finally{
    if(in!=null){in.close();}
    if(out!=null){out.close();}
  }
}

Bufferd stream을 사용해서 퍼포먼스를 개선한 예제이다. 예제1)과 같은 파일을 복사하는데 130ms.

예제3) Custom buffered copy

public static void copy(String from, String to) throws IOException{
  InputStream in=null;
  OutputStream out=null;
  try{
    in=new FileInputStream(from);
    out=new FileOutputStream(to);
    int length=in.available();
    byte[] bytes=new byte[length];
    in.read(bytes);
    out.write(bytes);
  }finally{
    if(in!=null){in.close();}
    if(out!=null){out.close();}
  }
}

while 루프를 사용하지 않고 배열을 사용함으로서 퍼포먼스를 개선한 예제이다. 예제1)과 같은 파일을 복사하는데 33ms. 하지만 예제3)은 byte배열로 선언되는 메모리 버퍼의 크기가 실제 파일의 크기와 동일해야 한다. 이에 따라 두 가지의 문제점이 발생할 수 있다. 첫 번째는 파일의 용량이 클 경우 상당한 메모리 낭비를 초래한다는 점이다. 두 번째 문제점은 copy()메소드가 수행될 때마다 new byte[]에 의해 버퍼가 새로 만들어진다는 점이다. 만일 파일의 용량이 클 경우 버퍼가 만들어지고 Garbage Collector에 의해 수집될 때 상당한 OverHead를 초래할 수 있다.

예제4) Improved custom buffered copy

static final int BUFF_SIZE=100000;
static fianl byte[] buffer=new byte[BUFF_SIZE];
public static void copy(String from, String to) throws IOException{
  InputStream in=null;
  OutputStream out=null;
  try{
    in=new FileInputStream(from);
    out=new FileOutputStream(to);
    while(true){
      synchronized(buffer){
        int amountRead=in.read(buffer);
        if(amountRead==-1)
          break;
        out.write(buffer,0,amountRead);
      }
  }finally{
    if(in!=null){in.close();}
    if(out!=null){out.close();}
  }
}

크 기가 100K인 byte 배열을 임시버퍼로 지정하고, 이를 static으로 선언함으로서 퍼포먼스를 개선했다. 예제1)과 같은 파일을 복사하는데 22ms. static buffer의 사용으로 I/O작업을 수행할 경우 발생할 수 있는 문제점을 해결하기 위해 synchronized block을 사용했다. 비록 synchronization을 사용함에 따라 성능 저하를 초래하지만, 실제 while 루프에 머무는 시간이 극히 짧기 때문에 퍼포먼스에 문제는 없다. 테스트에 의하면 synchronized 버전과 unsynchronized 버전 모두 같은 시간에 수행을 완료했다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.