C++ 입출력 방식인 cin, cout은 느립니다..
하지만, 일반적인 경우 cin 과 cout으로 입출력을 할때 속도 때문에 크게 문제가 된다고 생각하신적은 없을 겁니다.
그런데 최근에 알고리즘 문제를 풀다가 cin, cout을 최적화 하지 않았을때의 속도 차이 때문에 시간초과가 나는 것을 봐서 본 포스팅에서는 cin, cout 입출력 속도를 높이는 방법에 대해 다루어볼까 합니다.
방법이라 하기도 민망하게 딱 아래 세 줄의 코드만 추가해주시면 됩니다.
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
이제 저 세줄의 코드가 무엇을 의미하고 어떠한 원리로 cin과 cout의 속도를 개선해주는지 알아봅시다.
ios::sync_with_stdio(false);
cin과 cout은 C++의 iostream 헤더를 통해 사용할 수 있고, printf와 scanf는 C의 stdio.h 헤더를 통해 사용할 수 있습니다. 문제는 c++의 iostream과 C의 stdio.h가 동기화 되어 있다는 점입니다.
동기화가 되어 있다는 것은 stdio.h의 버퍼와 iostream의 버퍼(buffer)를 같이 사용한다는 뜻이고, 이는 곧 속도 저하를 의미합니다.
버퍼(buffer)
데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 메모리의 영역
따라서, ios::sync_with_stdio(false);는 두 헤더의 동기화를 비활성화하고, C++ 독립 버퍼를 사용할 수 있게끔 해서 수행 속도가 빨라지는 효과를 발생합니다.
cin.tie(NULL); cout.tie(NULL);
cin과 cout은 하나로 묶여있습니다. 한 스트림이 다른 스트림에서 각 IO(입출력) 작업을 진행하기 전 자동으로 버퍼를 비워준다는 것입니다.
예로, cin을 통해 입력을 받으면 cout 출력 버퍼(buffer)를 비워준 후 입력을 받습니다.
이렇게 버퍼를 비우는 과정도 시간이 소모되기 때문에 cin.tie(NULL); cout.tie(NULL);을 통해 이 둘의 상호 연결을 끊고, 비워주는 과정을 생략할 수 있어 속도가 빨라지는 효과를 볼 수 있습니다.
주의해야할 점
먼저 위의 코드들을 통해 cin, cout 속도를 빠르게 해준다면,
- (printf, scanf, getchar, puts, gets) 등 C의 stdio.h 입출력 방식을 같이 사용할 수 없습니다.
- 멀티스레드 환경에서 사용할 수 없습니다.
이유는 버퍼(buffer)의 동기화를 비활성화 한 이후로는 서로 독립 버퍼를 사용하기 때문에 출력되는 순서가 함수의 호출 순서와 다를 수 있기 때문입니다.
따라서, 실제 프로젝트나 시스템 구축 시에 잘 사용되지는 않지만 코딩 테스트 환경은 싱글스레드 환경이 대부분이며 화면 출력을 고려하지 않기 때문에 속도 향상을 위해 사용하면 좋다고 생각하시면 됩니다.
사용 예시
#include <iostream>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int N, Q;
cin >> N;
cin >> Q;
int S[100001] = {};
int elem;
for (int i = 1; i <= N; i++) {
cin >> elem;
S[i] = S[i - 1] + elem;
}
int start, end;
for (int i = 0; i < Q; i++) {
cin >> start >> end;
cout << S[end] - S[start - 1] << "\n";
}
}
실제로 위의 코드는 라인 5 ~ 7까지가 없다면 시간초과가 됩니다. 하지만 본 포스팅에서 다룬 구문을 사용함으로서 해결할 수 있었습니다.
추가로, 라인 21에서 endl을 사용하는 것보다 “\n” 을 직접 넣어주는 것이 속도가 더 빠릅니다.