백준에서 코딩 테스트 문제를 풀 때, 자바에서 Scanner와 System.out.print 코드를 작성하여 제출하면 시간 초과가 발생하는 경우가 있다. 이때 해결법으로 BufferedReader, BufferedWriter 함수를 사용하는데, 문득 이 함수가 왜 더 빠른지 궁금해졌다.
해당 함수는 이름 그대로 버퍼를 이용하여 입출력을 진행한다.
사용자로부터 값을 입력 받을 때, 키보드를 누르는 순간 데이터를 전달하지 않고, 버퍼에 잠시 저장해두었다가 전달하는 방법으로 동작한다.
두 장치 간의 입출력 속도 차이로 인한 처리 지연을 방지하기 위해 도입된 개념으로, 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 해당 데이터를 보관하는 임시 메모리 영역이다.
Buffer를 사용해서 더 빨라지는 이유는?
홈쇼핑 사이트에서 주문이 들어올 때마다 택배 기사가 물건을 하나씩 전달하는 방법
주문 여러 개를 받고 여러 개의 택배를 트럭에 실어서 한 번에 옮기는 방법
위와 같은 경우에서 1번처럼 주문이 들어올 때마다 일일이 물건을 배송하는 방법보다는 2번처럼 한 번에 물건을 옮기는 방법이 시간적으로 더 효율적이다. 여기서 트럭은 버퍼로 볼 수 있다. 이처럼 버퍼를 이용한 입출력은 바로
하나씩 데이터를 전달하지 않고, 버퍼에 모아두고 한 번에 전달하기에 속도가 빠르고 효율적이다.
즉, Scanner 는 사용자로부터 데이터를 입력받을 때마다 전송되어야 하기에, BufferedReader 보다 더 많은 시간이 소요된다. 이는 System.out.print와 BufferedWriter에도 동일하다.
그러면 무조건 BufferedReader, BufferedWriter를 사용하는 게 이득인 걸까?
자바를 처음 접할 때 배우는 입출력 방식인 Scanner는 Space(띄어쓰기), Enter(줄바꿈, 개행) 모두를 경계로 인식하기에 입력받은 데이터를 가공하기 편리하다는 장점이 있다.
그에 비해 BufferedReader는 Enter(줄바꿈, 개행)만 경계로 인식하고 받은 데이터가 String으로 고정되기 때문에 입력받은 데이터를 추가로 가공하는 작업이 필요하기에 Scanner에 비해 사용하기 다소 불편하다.
그러나 이는 입출력하는 데이터의 양에 따라 작업 속도에서 많은 차이가 발생한다. BufferedReader의 버퍼 메모리는 8KB로 Scanner의 버퍼 메모리 1KB보다 크기에 많은 입력이 있을 경우 더 효율적이다. 하지만, BufferedReader의 경우 Scanner보다 큰 메모리를 사용하게 된다.
따라서 효율적인 메모리 사용과 시간 단축을 위해서 Scanner와 Buffered 함수를 적절히 섞어가며 사용할 필요가 있어 보인다.
아래 블로그에 System.out.print와 System.out.println의 속도 차이에 대한 실험과 고찰이 있으니 한번 씩 읽어보면 좋을 것 같다.