알고리즘 문제를 C/C++로 풀 때, 입출력 함수를 과연 C stdio vs C++ iostream 둘 중에 뭘로 쓸 것인가?
실험의 발단은 이렇다.
"평소에 stdio를 즐겨 쓰던 필자는 과연 속도의 이점을 위해 iostream으로 갈아타야 하는가??"
라는 궁금증이 생겨 직접 테스트를 해보았다.
결론부터 말하면 속도 상에 유의미한 차이는 없는 것 같았다.
실험 방법은 이렇다.
어떤 알고리즘을 짜고, 입출력 함수만 다르게 하여 수행 시간을 비교하는 방식으로 진행하였다.
실험 조건
- Windows 10 x64
- Visual Studio 2019 (16.11.18)
- Code
- 백준 1992번 쿼드 트리 문제
- 조건부 컴파일을 통해 stdio vs iostream 취사 선택 가능
- Result
- time.h의 clock() 함수를 이용하여 수행 시간 측정
- n회 반복 측정하여 평균값 도출
본 실험에서는 100회 수행 후 평균값을 내는 방식으로 진행하였다.
그 이유는 코드를 실행할 때마다 측정되는 수행 시간이 달랐기 때문이다.
필자가 구현한 코드는 아래와 같다.
C99/C++20 정답 코드를 기반으로 반복 수행 및 시간 측정 코드를 추가하였다.
그리고 조건부 컴파일을 이용해 stdio와 iostream을 취사 선택할 수 있도록 구성하였다.
#define _CRT_SECURE_NO_WARNINGS
#define __USE_IOSTREAM__ 0 // 1: iostream, 0: stdio
#define TEST_COUNT 100
#if __USE_IOSTREAM__ == 1
#include <iostream>
using namespace std;
#else
#include <cstdio>
#endif
int arr[65][65];
int N;
#include <ctime>
time_t time_start, time_end;
bool check(int y, int x, int len, int value)
{
for (int i = y; i < y + len; i++)
{
for (int j = x; j < x + len; j++)
{
if (arr[i][j] != value)
goto EXIT;
}
}
return true;
EXIT:
return false;
}
void quadtree(int y, int x, int len)
{
if (len == 0) return;
bool allzero = check(y, x, len, 0);
bool allone = check(y, x, len, 1);
if (allzero)
{
#if __USE_IOSTREAM__ == 1
cout << "0";
#else
printf("0");
#endif
return;
}
if (allone)
{
#if __USE_IOSTREAM__ == 1
cout << "1";
#else
printf("1");
#endif
return;
}
#if __USE_IOSTREAM__ == 1
cout << "(";
#else
printf("(");
#endif
int half = len >> 1;
quadtree(y, x, half);
quadtree(y, x + half, half);
quadtree(y + half, x, half);
quadtree(y + half, x + half, half);
#if __USE_IOSTREAM__ == 1
cout << ")";
#else
printf(")");
#endif
}
int main(void)
{
freopen("Text.txt", "r", stdin);
int time_average = 0;
for (int t = 0; t < TEST_COUNT; t++)
{
#if __USE_IOSTREAM__ == 1
ios::sync_with_stdio(false);
cin.tie(0);
#endif
time_start = clock();
#if __USE_IOSTREAM__ == 1
cin >> N;
#else
scanf("%d", &N);
#endif
for (int i = 0; i < N; i++)
{
char temp[65];
#if __USE_IOSTREAM__ == 1
cin >> temp;
#else
scanf("%s", &temp);
#endif
for (int j = 0; j < N; j++)
{
arr[i][j] = temp[j] - '0';
}
}
quadtree(0, 0, N);
time_end = clock();
time_average += time_end - time_start;
}
time_average /= TEST_COUNT;
#if __USE_IOSTREAM__ == 1
cout << "execution time: " << time_average;
#else
printf("execution time: %d", time_average);
#endif
return 0;
}
__USE_IOSTREAM__
값을 1로 설정하면 iostream, 0으로 설정하면 stdio를 사용하도록 하였다.
그리고 iostream의 경우 아래 코드를 추가하여 버퍼 동기화를 비활성화하였다.
ios::sync_with_stdio(false);
cin.tie(0);
실험에 사용한 TC는 아래와 같다.
위 문제에서 입력으로 줄 수 있는 worst case를 작성하였다.
64
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
1010101010101010101010101010101010101010101010101010101010101010
0101010101010101010101010101010101010101010101010101010101010101
코드를 실행하고 약 30초 정도 기다리면 결과가 출력된다.
stdio를 사용했을 때 출력된 결과는 아래와 같았다.
execution time: 287
iostream를 사용했을 때 출력된 결과는 아래와 같았다.
execution time: 293
추가로 아래의 버퍼 동기화 비활성화 코드를 제거하고도 수행 시간을 측정해보았다. 결과는 아래와 같았다.
ios::sync_with_stdio(false);
cin.tie(0);
execution time: 295
속도 상에 유불리가 있을 것을 기대하였으나 측정값을 봤을 땐 그렇지 않은 것 같았다. 그리고 몇 번 더 실행해보면 서로 결과값이 역전되기도 하였다.
적어도 필자가 궁금했던 "입출력 함수를 갈아타야 하는가" 에 대한 결론은 "아니오" 로 난 것 같다.