태그 보관물: BufferedWriter

BufferedWriter를 이용한 가장 좋은 성능 방안에 대해서..

파일에 로그를 남긴다거나 데이타를 쓰기위해서 java.io.File 객체를 써도 되지만, 성능을 위해서 BufferedWriter를 많이 이용하게 됩니다. 특히 로그 라이브러리나 로깅 유틸리티 클래스에서 거의 대부분 사용할 것으로 생각이 드네요..
그래서, 생각한 방법이 BufferedWriter를 캐싱하는 것입니다.

아래 코드의 shutdown() 메쏘드는 ShudownHooking으로 등록된 쓰레드가 프로세스가 종료되면 호출을 해서 버퍼에 남아있는 내용을 기록하도록 되어 있습니다.

BufferedWriterCacheUtility.java

package net.sjava.io.test;

import java.util.Map;
import java.util.LinkedHashMap;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.util.Iterator;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BufferedWriterCacheUtility {
   
    /** max cache size */
    private static final int size = 10;
    /** lock */
    private static Lock lock = new ReentrantLock();
   
    /** initialized lru cache */
    private static Map<String, BufferedWriter> cache = new LinkedHashMap<String, BufferedWriter> (size, .75F, true) {
        private static final long serialVersionUID = 1L;
        protected boolean removeEldestEntry(Map.Entry<String, BufferedWriter> eldest) {
            if(size() > size) {
                try {
                    // flush and close before deleted
                    eldest.getValue().close();
                } catch(java.io.IOException e) {
                    // ignore
                }
            }
           
            return size() > size;  
        }
    };

   
    /**
     *
     * @param fileName
     * @return
     */
    public static BufferedWriter createBufferedWriter(String fileName) {
        lock.lock();
        try{
            if(cache.containsKey(fileName))
                return cache.get(fileName);
       
            return new BufferedWriter(new FileWriter(fileName, true), 1024);
        } catch(java.io.IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            lock.unlock();
        }
    }
   
    /**
     *
     * @param fileName
     * @param writer
     */
    public static void close(String fileName, BufferedWriter writer) {
        lock.lock();
        try {
            cache.put(fileName, writer);
        } finally {
            lock.unlock();
        }
    }
   
    /** shutdown hooking thread call */
    public static void shutdown() {
        // 아래의 코드를 통해서 종료가 되더라도 다 flush 하고 종료한다.     
        Iterator<BufferedWriter> iter = null;
        try {
            iter = cache.values().iterator();
            while (iter.hasNext()){
                iter.next().close();
                //iter.next().close();
            }
           
        }catch(java.io.IOException e) {
            e.printStackTrace();
        }
    }
}


아래 코드는 BufferedWriter를 사용하는 방법에 대한 3가지의 예입니다.
물론 2번째(bwriter02)를 사용하는 예가 가장 빠르겠지만, 3번째 예는 여러개의 fileName을 가지고 file write를 하기에 가장 좋은 방법으로 생각이 듭니다.

BufferedWriterTest.java

package net.sjava.io.test;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

public class BufferedWriterTest {

    private static SimpleDateFormat format = new SimpleDateFormat(“yyyy.MM.dd HH:mm:ss SSS”);

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //load();
               
        System.out.println(“s – ” + format.format(new Date()));
       
        BufferedWriter bwriter01 = null;
        try {
            for(int i=0; i < 100000; i++) {
                bwriter01 = new BufferedWriter(new FileWriter(“c:\\test01.txt”, true), 1024);
                bwriter01.write(“aaaaaaaaaaaaccccccccccccccccccaaa”);
                bwriter01.newLine();
                bwriter01.flush();
                bwriter01.close();
            }
        } catch(java.io.IOException e) {
            e.printStackTrace();
        }
        System.out.println(“e – ” + format.format(new Date()));
        
        System.out.println(“s – ” + format.format(new Date()));
        BufferedWriter bwriter02 = null;
        try {
            bwriter02 = new BufferedWriter(new FileWriter(“c:\\test02.txt”, true), 1024);
            for(int i=0; i < 100000; i++) {
                bwriter02.write(“aaaaaaaaaaaaccccccccccccccccccaaa”);
                bwriter02.newLine();
            }
            bwriter02.flush();
            bwriter02.close();
        } catch(java.io.IOException e) {
            e.printStackTrace();
        }
        System.out.println(“e – ” + format.format(new Date()));        
        
        // 날짜별 혹은 서비스별로 로깅을 해야 될경우
        System.out.println(“s – ” + format.format(new Date()));
        BufferedWriter bwriter03 = null;
        try {
            for(int i=0; i < 100000; i++) {
                bwriter03 = BufferedWriterCacheUtility.createBufferedWriter(“c:\\test03.txt”);   
                bwriter03.write(“aaaaaaaaaaaaccccccccccccccccccaaa”);
                bwriter03.newLine();
                BufferedWriterCacheUtility.close(“c:\\test03.txt”, bwriter03);
            }
        } catch(java.io.IOException e) {
            e.printStackTrace();
        }
       
        System.out.println(“e – ” + format.format(new Date()));
    }

}


여담이지만, java.io.BufferedWriter 소스를 보면, write시에 lock을 잡고 씁니다.
아래와 같은 코드를 많이 사용하지만요..

lock.lock();
try {
 bWriter.write(“000000000000000”);
}catch(java.io.IOException e) {
  e.printStackTrace();
} finally {
  lock.unlock();
}

위 코드보다는 아래코드를 사용하는게 맞겠습니다.

try {
 bWriter.write(“000000000000000”);
}catch(java.io.IOException e) {
  e.printStackTrace();
}

BufferedWriter vs BufferedOutputStream 성능차이..

로그 데이터를 파일에 저장하기 위해서 BufferedWriter 클래스를 사용하다가, 갑자기 BufferedOutputStream으로 처리하는 것과의 성능상 차이에 대해서 확인하고 싶어서, 동일한 데이타를 파일에 저장해 봤다. 두 클래스의 성능 차이는 별로 없어 보이지만 그래도 확인 해 보자. 아래의 BufferWriterTest, BufferOutputStream 클래스의 buffersize는 1024로 동일하다.

– BufferWriterTest.java

import java.util.*;
import java.text.*;
import java.io.*;

public class BufferWriterTest {
	//
	static DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");

	static String txt = "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라"
			+ "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ "가나다라마바사아자차타파하가나다라마바사아자차타파하" + "가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ "가나다라마바사아자차타파하마바사아자차타파하";

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Date start = new Date();
		System.out.println("s – " + df.format(start));

		BufferedWriter bufferWriter;

		try {
			bufferWriter = new BufferedWriter(new FileWriter("c:\\BufferedWriter.txt", true), 1024);
			for (int i = 0; i < 5000000; i++) {
				bufferWriter.write(BufferWriterTest.txt);
				bufferWriter.newLine();
			}

			bufferWriter.flush();
			bufferWriter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		Date end = new Date();
		System.out.println("e – " + df.format(end));
	}
}

결과(3.15G 파일 쓰기)

s – 2009-06-19 15:55:00:968
e – 2009-06-19 15:55:48:280

– BufferOutputStreamTest.java

import java.io.IOException;
import java.io.FileOutputStream;
import java.io.BufferedOutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class BufferOutputStreamTest {
	//
	static DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
	static String txt = "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ 	"가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라"
			+	"가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ 	"가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+	"가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하" 
			+ "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하"
			+ "가나다라마바사아자차타파하가나다라마바사아자차타파하가나다라마바사아자차타파하" 
			+ 	"가나다라마바사아자차타파하마바사아자차타파하";

	/**
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Date start = new Date();
		System.out.println("s – " + df.format(start));

		BufferedOutputStream bufferedOutput;
		try {
			bufferedOutput = new BufferedOutputStream(new FileOutputStream("c:\\BufferedStream.txt", true), 1024);

			for (int i = 0; i < 5000000; i++) {
				bufferedOutput.write(txt.getBytes());
				bufferedOutput.write("\n".getBytes());
			}
			bufferedOutput.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		Date end = new Date();
		System.out.println("e – " + df.format(end));
	}
}

결과(3.15G 파일 쓰기, )

s – 2009-06-19 15:56:41:889
e – 2009-06-19 15:58:19:825

위 결과를 보면, 비슷한 사이즈의 파일을 추가할 경우에 꽤 성능차이가 난다. 느낌에는 비슷한 성능을 낼 것으로 생각이 드는데, 스트링 처리는 Reader/Writer 시리즈가 더 적합하다는 것인가? 혹시 아래의 구조 때문에 그럴까요? 아시는 분은 조언을..

BufferedWriter BufferedOutputStream
java.lang.Object
– java.io.Writer
– java.io.BufferedWriter
java.lang.Object
– java.io.OutputStream
– java.io.FilterOutputStream
– java.io.BufferedOutputStream

File에 내용 추가하기..

간단하게 아래의 코드로 File에 내용을 추가한다.

		try {
			// ask user for file name to write to
			FileWriter out = new FileWriter("out.txt", true);
			BufferedWriter writer = new BufferedWriter(out, 64); // buffersize를 줘서 성능을 높이자..
			writer.write("fooooooooooooooooooooooo~~");
			writer.newLine();
			writer.flush();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

위의 FileWriter out = new FileWriter(“out.txt”, true); 에서 out.txt 파일이 없는 경우에 파일은 생성한다. FileWriter 생성자에서 true 인자를 사용해서 파일에 추가하고, BufferedWriter는 buffersize를 설정해서 성능을 높일 수 있다. ^^