보통 자바에서는 자료를 읽거나 쓰기 위해 Stream을 사용하고, 자바는 두 가지 형태의 Stream을 지원합니다.


Reader/Writer, Input/OutputStream이 그것인데, Reader/Writer는 High-Level의 I/O(String이라던가..)을 지원하고, Input/OutputStream은 Low-Level의 I/O(byte)를 지원합니다. 속도 향상을 위해서는 BufferedStream을 사용하지만 BufferedStream의 경우 버퍼의 기본값은 2k입니다. 이 값은 조정할 수 있으나 자료의 용량이 클 경우 메모리가 많이 필요하기 때문에 BufferedStream을 사용할 경우 여러가지 사항을 고려해야 합니다.



아래는 BufferedStream을 사용하지 않고 I/O를 했을 경우의 예제입니다. 370k의 JPEG 파일을 복사하는데 10800ms.


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(); } }






BufferedStream을 사용해서 퍼포먼스를 개선한 예제입니다. 같은 파일을 복사하는 데에 130ms밖에 걸리지 않습니다.


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(); } }






while 루프를 사용하지 않고 배열을 사용함으로써 퍼포먼스를 개선한 예제. 같은 파일을 복사하는 데에 33ms.


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(); } }





하지만 바로 위 코드는 byte 배열로 선언되는 메모리 버퍼의 크기가 실제 파일의 크기와 동일해야 합니다. 이에 따라 두 가지 문제점이 발생할 수 있는데요, 첫 번째는 파일의 용량이 클 경우 상당한 메모리 낭비를 초래한다는 점. 두 번째는 Copy() 메소드가 수행될 대마다 new byte[]에 의해 버퍼가 새로 만들어진다는 점. 마일 파일의 용량이 클 경우 버퍼가 만들어지고 Garbage Collector에 의해 수집될 때 상당한 오버헤드를 초래할 수 있습니다.


예제4) Improved Custom Buffered Copy
static final int BUFF_SIZE=100000;
static final 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으로 선언함으로써 퍼포먼스를 개선했습니다. 이 코드로 같은 파일을 복사하는데 22ms밖에 걸리지 않아요. static buffer의 사용으로 I/O작업을 수행할 경우 발생하는 문제점을 해결하기 위해 synchronized block을 사용했습니다. 비록 sync를 사용함에 따라 성능 저하를 일으킬 수도 있지만, 실제 while 루프에 머무는 시간이 극히 짧기 때문에 퍼포먼스에 문제는 없습니다. 테스트에 의하면 synchronized 버전과 unsynchronized 버전 모두 같은 시간에 수행을 완료했었습니다.




+ Recent posts