The Way
stdio 기본 출력 함수 속도 비교 본문
C에는 온갖 입출력 함수들이 많다.
당장 출력만 해도 putchar, putc, fputc, puts, fputs, fwrite, printf, fprintf의 8개 정도 함수를 손에 꼽을 수 있을 것 같다.
함수들의 출력속도에는 상당한 차이가 있다. 그것도 꽤 많은 차이가 난다.
https://www.acmicpc.net/blog/view/57
백준 블로그인데, 여기에 함수들의 출력 속도 차이가 적혀있다. fwrite가 가장 빠르다고 나온다.
저 함수들 중 몇 개가 비어있기 때문에, 마저 채우는 느낌에서 속도를 측정해보았다.
백준에서는 숫자를 1부터 1000만까지 출력하였지만, 약간 특수한 상황이므로 이번 테스트에서는 더 본질적인 메모리에 있는 문자열을 그대로 출력하기를 해 보았다.
구체적인 테스트 방법은 출력 가능한 문자 (ASCII 32~126번까지) 95개를 순서대로 총 1000만 개 메모리에 저장하고, 이후 출력하였다.
컴파일은 gcc -O2 -o a a.c로 수행.
모두 stdout을 사용하였으며, ./a > out으로 실행.
12가지 케이스를 총 6회 반복하고, 그 중 가장 큰 값 하나를 제외한 뒤(가끔 한 값이 이상하게 오래 걸리는 경우가 있었다. 뭔가 Interrupt가 일어나는 것 같다) 그 평균 시간을 비교해보자.
먼저 Initialize 과정.
for (int i = 0; i < N; ++i) buf[i] = i % 95 + 32; buf[N] = '\0';
메모리의 각 부분에 캐릭터를 넣은 뒤, 맨 마지막에는 null 문자를 넣어주었다. 상기하였듯 N은 1000만이다.
가장 기본적인 형태의 for문이고, 나누기 하나 더하기 하나가 들어간다.
총 소요시간은 21985μs.
for문이 사실 이것보다 간단하기도 힘드니, 이 속도가 1000만 개를 iteration하는 표준 속도 정도로 생각하면 적당할 것 같다.
Case 1~3: putchar
똑같이 putchar을 사용. 데이터에 접근하는 방식을 다양하게 해보았다.
Case 1은 흔히 쓰는 []를 사용해서, Case 2는 *와 +를 사용해서, Case 3은 ptr를 ++로 더해가며 접근했다.
for (int i = 0; i < N; ++i) putchar(buf[i]);
↑소요 시간 33922μs.
for (int i = 0; i < N; ++i) putchar(*(buf + i));
↑소요 시간 32174μs.
char* ptr = buf; for (int i = 0; i < N; ++i) putchar(*ptr++);
↑소요 시간 31136μs.
결과를 놓고 보면 Case 3 > Case 2 > Case 1 순으로 성능이 좋았다.
[] 연산자가 생각보다 느린 걸지도. 그런데 의미있는 차이는 아닌 것 같다.
Case 4: putc
for (int i = 0; i < N; ++i) putc(buf[i], stdout);
↑소요 시간 29220μs.
putchar보다 조금 더 나은 성능을 보인다.
Case 5: fputc
for (int i = 0; i <= N; ++i) fputc(buf[i], stdout);
↑소요 시간 25941μs.
앞에 f가 붙었을 뿐인데, 성능이 훨씬 좋아진다. 꽤 놀라운 부분.
Case 6: puts
puts(buf);
↑소요 시간 6723μs.
여기서부터는 string이다. 내부적으로 무슨 처리를 하는지 모르겠지만, 개별 문자를 출력하는 것보다 훨씬 빨라진 것을 확인할 수 있다.
참고로 puts는 끝에 '\n'을 하나 자동으로 출력해준다. 마치 파이썬의 기본 print() 같다.
Case 7: fputs
fputs(buf, stdout);
↑소요 시간 5825μs.
마찬가지로 앞에 f가 붙었을 뿐인데 훨씬 더 빠른 성능을 보인다.
Case 8: fwrite
fwrite(buf, 1, N, stdout);
↑소요 시간 5240μs.
대망의 fwrite. 확실히 제일 빠른 것을 확인할 수 있다.
다만 byte 수를 직접 지정해주어야하는 단점이 있어서, 약간 쓰기 번거로울 수는 있을 것 같다.
Case 9~10: printf
다음은 대망의 printf. %c를 1000만 번 반복하는 것과 %s를 사용하는 것 두 경우를 해보았다.
for (int i = 0; i < N; ++i) printf("%c", buf[i]);
↑소요 시간 61529μs.
printf("%s", buf);
↑소요 시간 6563μs.
예상과는 달리 %s를 사용할 경우 printf가 그렇게 느리지는 않았다.
Case 11: fprintf
fprintf(stdout, "%s", buf);
↑소요 시간 6181μs.
마찬가지로 놀랍게도 f만 붙여도 훨씬 더 속도가 빨라진다.
이상의 결과를 정리하면 다음과 같다.
우리 모두 string 출력을 애용하자.
'공부 > C++ 메모사항' 카테고리의 다른 글
C++ 음수의 나눗셈 / 나머지 (0) | 2019.05.30 |
---|---|
windows gdb c++ stl이 예쁘게 안 나올 때 (6) | 2018.10.13 |
lower_bound와 upper_bound (0) | 2018.05.12 |
Visual Studio 2015 C4996 에러 (scanf 사용 시) (0) | 2017.08.22 |