[C++] 백준 알고리즘 : 입출력 실행 속도 높이기

wansuper·2023년 2월 19일
0

CodingTest

목록 보기
4/34

개요

C++로 알고리즘을 풀 때 실행 속도를 높이기 위해 흔히 아래와 같은 구문을 작성한다.

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

c++의 cin, cout은 c언어의 scanf, printf보다 속도가 느리다.
출력에서 큰 차이는 아니지만 입력의 경우, 2배 이상의 속도 차이가 난다.
그렇기에, 백준의 일부 문제에서는 입출력 속도 차이로 인해 오답인 경우가 존재한다.

실제 시간이 어느정도 걸리는지 알아보면
입력의 경우
cin을 쓰는 경우는 평균 2.1742초,
scanf를 쓰는 경우 평균 0.9206초 가 걸린다.

출력의 경우
cout을 쓰는 경우는 평균 0.9229초,
printf를 쓰는 경우 평균 0.8614초 가 걸린다.

그럼 동기화를 끊는다면 속도가 어느정도 빨라질까?
입력의 경우
cin은 평균 0.5938초로 scanf와 비교해보면 거의 2배 가까이 빠른 속도를 보여준다.
출력의 경우
cout은 평균 0.8272초로 거의 차이가 나지 않는다.

cout.tie(NULL); 은 시간 차이가 크지 않아 잘 쓰이지 않는듯하다.

C++ 입출력 속도 높이기

#include<iostream>
using namespace std;

int main() {
ios::sync_with_stdio(false); 
cin.tie(NULL); 

return 0;
}

ios::sync_with_stdio(false); cin.tie(NULL);

설명 및 규칙

  1. 위의 코드를 사용하면, C와 C++사이의 stream 동기화를 끊는다. 그러면, C++ stream들은 독립적인 buffer를 갖게 되고, buffer의 수가 줄어들며 속도가 빨라지게 된다.
  2. 대신, 위의 코드를 쓸 땐 C의 표준 입출력을 혼용하면 안된다. 같이 사용하게 될 경우 엉뚱한 답을 낼 확률이 매우 높다!
    예를 들면, scanf / printf / getchar / putchar / puts 등이 있다.
  3. endl 대신 개행문자인 \n 을 쓰도록 하자.
    endl의 경우 개행 문자 출력과 함께 출력 버퍼를 비우는 역할까지 같이 하기 때문에 딜레이가 발생하기 때문이다.
  4. 마지막으로, 위의 코드는 싱글 스레드 환경에서만 써야 한다. 싱글스레드 vs 멀티스레드 환경에 대한 내용은 다음 블로그를 참조하자. (https://velog.io/@gil0127/%EC%8B%B1%EA%B8%80%EC%8A%A4%EB%A0%88%EB%93%9CSingle-thread-vs-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C-Multi-thread-t5gv4udj)

ios_base::sync_with_stdio(false);

ios_base::sync_with_stdio(false);의 장점

ios_base::sync_with_stdio 구문은 c의 stdio와 cpp의 iostream을 동기화시켜주는 역할을 하는데, 이 때 iostream과 stdio의 버퍼를 모두 사용하기 때문에 딜레이가 발생한다.

따라서, ios_base::sync_with_stdio(false); 코드를 작성해줌으로써 동기화를 비활성화시켜준다.

이로 인해, c++만의 독립적인 버퍼가 생성되어 c의 버퍼와 병행하여 사용할 수 없게 되지만, 사용하는 버퍼의 수가 줄어들었기 때문에 실행 속도는 빨라지게 된다.

알고리즘 문제를 풀 때는 대부분 싱글 쓰레드 환경이기 때문에 해당 코드를 추가해줘도 문제가 발생하지 않을 확률이 높다.

ios_base::sync_with_stdio(false);의 단점

동기화된 C++ 버퍼의 경우 thread-safe하기 때문에 모든 I/O의 순서가 예상한 것과 정확히 일치함을 보장할 수 있다.

-> (output from different threads may interleave, but you get no data races).

하지만, ios_base::sync_with_stdio(false); 코드를 추가함으로 인해 동기화가 비활성화됐기 때문에 멀티 쓰레드 환경에서는 출력 순서를 보장할 수 없다.


cin.tie(null);

결론부터 말하자면 cin.tie(null); 코드는 cin과 cout의 묶음을 풀어준다.

기본적으로 cin과 cout은 묶여있고 묶여있는 스트림들은 한 스트림이 다른 스트림에서 각 I/O 작업을 진행하기 전에 자동으로 버퍼를 비워준다.

아래의 코드로 예시를 들어보면,

cout << "이름을 입력하세요: ";
cin >> name;

위 경우 cin과 cout이 묶여있기 때문에, 이름을 입력하기 전에 "이름을 입력하세요: " 구문이 먼저 출력될 것이다.

하지만, cin.tie(null); 코드를 추가한다면 cin과 cout의 묶음이 풀리면서 "이름을 입력하세요: "; 구문이 먼저 출력되지도 않았는데 먼저 이름을 입력을 받는 경우가 발생할 수 있다.

이는 cout이 기본적으로 버퍼에 추가되고 바로 비워지지 않기 때문이다. (출력 명령을 내리거나 버퍼가 가득 찼을 경우에만 버퍼를 비우고 출력한다.)

따라서, cin.tie(null); 코드를 추가했고 name을 입력받기 전에 "이름을 입력하세요: " 구문을 먼저 보고 싶다면 cout으로 "이름을 입력하세요: "를 출력할 때 버퍼를 비워줘야 한다.


예시

#include <iostream>
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	std::cout << "Enter name:";
	std::cin >> name;
  	
  	return 0;
}

(+23.02.24 추가: https://www.acmicpc.net/board/view/22716)

C

scanf / printf는 충분히 빠름

C++

아래 얘기는 cin, cout을 쓸 때의 얘기지, scanf/prinf로 입출력을 하고자 하신다면 그냥 쓰면 된다. scanf/printf는 충분히 빠르다.
endl은 개행문자를 출력할 뿐만 아니라 출력 버퍼를 비우는 역할까지 합니다. 그래서 출력한 뒤 화면에 바로 보이게 할 수 있는데, 그 버퍼를 비우는 작업이 매우 느립니다. 게다가 온라인 저지에서는 화면에 바로 보여지는 것은 중요하지 않고 무엇이 출력되는가가 중요하기 때문에 버퍼를 그렇게 자주 비울 필요가 없습니다. 그래서 endl을 '\n'으로 바꾸는 것만으로도 굉장한 시간 향상이 나타납니다.
cin.tie(NULL)은 cin과 cout의 묶음을 풀어 줍니다. 기본적으로 cin으로 읽을 때 먼저 출력 버퍼를 비우는데, 마찬가지로 온라인 저지에서는 화면에 바로 보여지는 것이 중요하지 않습니다. 입력과 출력을 여러 번 번갈아서 반복해야 하는 경우 필수적입니다.
ios_base::sync_with_stdio(false)는 C와 C++의 버퍼를 분리합니다. 이것을 사용하면 cin/cout이 더 이상 stdin/stdout과 맞춰 줄 필요가 없으므로 속도가 빨라집니다. 단, 버퍼가 분리되었으므로 cin과 scanf, gets, getchar 등을 같이 사용하면 안 되고, cout과 printf, puts, putchar 등을 같이 사용하면 안 됩니다.


결론

알고리즘을 풀 때는 보통 싱글 스레드 환경이기 때문에 ios_base::sync_with_stdio(false); 코드를 추가해줘도 결과에 영향이 없고 C와 C++의 버퍼를 분리하기 때문에 속도가 빨라진다. (다시 강조하지만 이 때, c 스타일의 입,출력문을 혼용하지 않도록 주의한다.)

기본적으로 cin으로 읽을 때 먼저 출력 버퍼를 비우는데, 마찬가지로 알고리즘을 풀 때는 화면에 바로 보이는 것이 중요하지 않다. 따라서, 입력과 출력을 여러 번 번갈아가며 반복해야 하는 경우 필수적으로 cin.tie(null); 코드를 추가하여 cout과 cin의 묶음을 풀어줘야 한다.

고로, 알고리즘을 사용해 풀 때는 위의 코드를 사용하는 습관을 들이도록 하자.

출처 1: https://jaimemin.tistory.com/1521
출처 2: https://ip99202.github.io/posts/%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%86%8D%EB%8F%84-%EC%A4%84%EC%9D%B4%EA%B8%B0/
출처 3: https://www.acmicpc.net/blog/view/55

profile
🚗 Autonomous Vehicle 🖥️ Study Alone

0개의 댓글