과거에 Java NIO관련 포스팅을 몇 번 했습니다. 그리고 꼭 allocateDirect()를 이용하여 커널버퍼를 직접 사용하는 것을 권장 했습니다. 하지만 allocateDirect() 메서드는 allocate()메서드에 비해 굉장히 오버헤드가 심하므로 한번만 allocateDirect()를 쓰고 해당 ByteBuffer는 재활용 해야합니다. 필요할 때마다 만들어 쓰지 않고 한 번 만들어서 필요할 때마다 다시 써야 좋은 퍼포먼스를 낼 수 있습니다. 하지만 allocate()로 할당할때와 allocateDirect()로 할당 할 때의 속도 차이는 대체 얼마나 나는 걸까요. 직접 비교해 보았습니다.

1. 테스트 코드 작성

테스트 코드를 작성하여 속도 비교를 하려고 했습니다. ByteBufferallocate() 메서드로 할당, allocateDirect() 메서드로 할 당 한 것, 그리고 byte 배열을 할당한 것 3가지 케이스를 테스트 하려고 합니다. 모두 1024바이트의 버퍼를 생성해 보겠습니다. 테스트 코드는 다음과 같습니다.

long startTime = System.currentTimeMillis();
for(int i = 0; i<1000000 ; i++){
    ByteBuffer buf = ByteBuffer.allocate(1024);
    // ByteBuffer buf = ByteBuffer.allocateDirect(1024);
    // byte[] buf = new byte[1024];
}
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
System.out.println("elapsedTime = " + elapsedTime)

위에서 주석 처리한 부분을 하나씩 풀어 실행 시킬 것 입니다.

2. 퍼포먼스 비교

각 모두 for문을 통해 백만번 할당을 하면서 결과를 비교하겠습니다. 단위는 밀리새컨드입니다.

byte[] non-direct ByteBuffer direct ByteBuffer
1차 328 375 10063
2차 360 391 8719
3차 312 375 8656
4차 344 375 8625
5차 313 375 8875
6차 296 359 8516
7차 313 375 8625
8차 312 375 8594
9차 297 375 8641
10차 313 406 8734

결과를 보면 byte 배열 할당보다 non-direct ByteBuffer 할당이 살짝 느린 것으로 나타납니다만 그렇게 큰 차이는 아닙니다. channel을 이용할 수 있다는 점에서 때론 byte 배열 할당보다 non-direct ByteBuffer를 이용하는 것이 효과적일 때도 있으니 그리 큰 차이는 아닙니다. 하지만 direct ByteBuffer는 위 두가지 경우와 큰 차이를 보입니다. 속다가 25배가넘네요.

참고로 1차의 기록은 JVM초기화에 필요한 시간도 포함이므로 큰 의미는 없습니다.

3. Direct ByteBuffer는 재활용 하자!

보시는바와 같이 byte[]와 non-direct ByteBuffer에 비해 direct ByteBuffer의 할당 속도가 매우 느린 것으로 나타납니다. 따라서 ByteBuffer가 필요할때마다 그때그떄 할당해서 사용한다면 프로그램 퍼포먼스에 큰 영향을 줄 수 있습니다. 이 오버헤드는 무시 할 수 있을 것 같지 않습니다. 따라서 ByteBuffer를 재활용하여 사용합시다. 재활용 방법으로는 ByteBufferPool이 가능 할 것입니다. 자세한건 Pool패턴을 참고합시다.