728x90
반응형

백준 문제를 풀다가 알게 된 사실입니다.

https://www.acmicpc.net/problem/1753

해당 1753 문제를 풀던 도중이었죠.

해당 문제는 edge 가 최대 300000 개로 다른 문제들에 비해 입출력이 상당히 많은 문제입니다.

 

대충 코드를 작성하고 제출을 했는데 시간 초과가 발생했습니다.

그래서 내가 뭔가 알고리즘을 잘못 짠 건가 해서 구글링을 하던 중 코드가 구조상 거의 동일한데

저 코드는 되고 내 코드는 안 되는 이유를 찾았습니다.

 

결론부터 말하자면 바로 3줄의 차이였습니다.

ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);

 

우선 C/C++ 의 여러 가지 입출력의 수행 시간 차이를 알아보겠습니다.

출처 : https://algospot.com/forum/read/2496/

위의 사진을 보면 getchar(*) 가 0.39초로 가장 빠르고

그다음으로 흔히 사용하는 scanf 가 0.798 ,  cin이 2.051로 2배가 넘는 속도 차이를  보여주고 있습니다.

이 느린 cin에 sync_with_stdio 를 사용하면 scanf 와 비슷한 속도를 보여줍니다.

 

그러면 왜 이런 현상이 일어나는 걸까요?

 

우선 C++는 기본적으로 입출력 시 자신만의 표준 스트림을 사용하지 않습니다.

그럼 어떻게 하느냐? 바로 C의 표준스트림을 사용합니다.

C++ 의 표준 스트림 cin, cout 등과 C의 표준스트림 stdin, stdout 이 다 연결돼있다는 소립니다.

그래서 cin 과 printf 를 혼합해서 사용해도 입출력의 순서가 보장되는 것입니다.

 

하지만 그 동기화를 끊어버리면? 입출력의 순서가 보장되지 않는 대신 속도가 빨라집니다.

또한 C++ 의 입출력은 쓰레드 안전성이 보장되지만 동기화를 끊어버리면 보장되지 않습니다.

 

아래의 두 코드는 동기화를 한 경우와 하지 않은 경우의 차이입니다.

ubuntu 환경에서 테스트하였고 (비주얼스튜디오에서는 다르게 나올 수 있습니다!!)

두 코드의 차이점은 동기화의 차이밖에 없습니다.

 

1. 동기화를 풀지 않을 경우

#include <iostream>
using namespace std;

int main()
{	
	for(int i = 0; i < 10; ++i) {
		cout << "cout\n";
		printf("printf\n");
	}
}

 

2. 동기화를 풀 경우

#include <iostream>
using namespace std;

int main()
{
	ios_base::sync_with_stdio(false);

	for (int i = 0; i < 10; ++i) {
		cout << "cout\n";
		printf("printf\n");
	}
}

위와 같이 같은 코드인데도 출력이 다릅니다.

 


다음으로

cin.tie(NULL);
cout.tie(NULL);

이 두줄은 cin 과 cout 의 묶음을 풀어버리는 코드입니다.

기본적으로 cin 과 cout은 묶여있고 묶여있으면 스트림이 각 입출력을 하기 전에 자동으로 버퍼를 비워줍니다.

코드를 예로 들어보겠습니다.

#include <iostream>
using namespace std;

int main()
{
	cin.tie(NULL);
	cout.tie(NULL);
	int num;
	cout << "enter num : ";
	cin >> num;
}

위의 코드가 묶음을 풀었을 경우에 "enter num : " 이 출력되기도 전에 cin이 먼저 실행될 수도 있다는 것이다.

 

추가적으로 개행 시 보통 endl 을 많이 사용하게 되는데 이 endl 의 경우 버퍼를 비워주는 역할도 수행하므로 "\n"을 사용하는 것보다 속도가 느립니다. 따라서 알고리즘을 할 때는 극한의 속도(?)를 위해 "\n" 사용을 생활화합시다.

 

결론

보통 알고리즘 문제를 풀 경우 싱글 쓰레드 환경이므로 sync_with_stdio(false) 를 해도 cin, scanf  cout, printf 를 섞어 사용하지 않는 이상 결과에 영향이 없고 속도가 매우 빨라진다.

cin.tie(NULL) cout.tie(NULL) 또한 알고리즘 문제는 화면에 보이는 순서가 크게 중요하지 않으므로 크게 지장이 없다.

 

 

요약

1. sync_with_stdio 사용할 거면 cout 과 printf 혼용 금지와 싱글쓰레드에서만 사용

2. cin.tie(NULL) cin.tie(NULL) 시에 cin cout 을 빠르게 혼용 시 순서가 뒤죽박죽

728x90
반응형

+ Recent posts