The Way

stdio 기본 출력 함수 속도 비교 본문

공부/C++ 메모사항

stdio 기본 출력 함수 속도 비교

Jeonggyun 2019. 5. 14. 21:26

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 출력을 애용하자.

Comments