본문 바로가기

else if (개발)

자바 System.out.println vs C printf

최근에 C라이브러리와 C라이브러리를 JNI로 래핑한 라이브러리의 성능을 비교하다가 C와 자바의 표준출력 차이에 대해서 알게되었습니다.

아래의 두 코드는 Hello World를 여러번 출력하는 초간단 자바와 C 코드입니다.

//test.c
#include <stdio.h>
int main() {
    int i;
    for (i = 0; i < 1000000; i++){
        printf("Hello World!\n");
    }
    return 0;
}

//Test.java
public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 1000000; i++) {
            System.out.println("Hello World!");
        }
    }
}

이렇게 간단한 두개의 코드를 컴파일하고 각각 실행시간을 time으로 재는데 단, 출력을 파일로 리다이렉션 합니다. 그 결과 두 프로그램의 실행시간 차이는 무려 다음과 같습니다.
$ time ./test > output1.txt
real    0m0.103s
user    0m0.050s
sys     0m0.050s

$ time java Test > output2.txt
real    0m9.885s
user    0m2.660s
sys     0m7.340s

이렇게 차이가 나는 이유는 출력 버퍼링의 차이 때문입니다.
C에서 표준출력은 터미널과 같은 대화형 장치에는 \n을 기준으로 flush를 하는 line buffering을 하지만, 파일로 리다이렉션 할 때는 full buffering으로 전환됩니다. 하지만, 자바의 System.out.println은 항상 line buffering을 하게 됩니다.
언어의 성능의 문제가 아니라 buffering 방식이 다르기 때문에 애초에 공정한 게임이 아닌 것입니다. 공정한 게임을 위해서는 자바 코드를 다음과 같이 수정해야 합니다.
import java.io.*;
public class Test {
    public static void main(String[] args) throws IOException {
        //표준출력 full buffering
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        for (int i = 0; i < 1000000; i++) {
            bw.write("Hello World!\n");
        }
        bw.close();
    }
}
$ javac Test.java
$ time java Test > output2.txt
real    0m0.294s
user    0m0.260s
sys     0m0.020s

자바와 C 프로그램의 성능을 비교하는데 표준출력을 파일로 리다이렉션 하면서 이 차이를 무시하면  아주 엉뚱한 판단을 하게 되겠죠? 제가 그랬습니다. T.T