TLDR
setvbuf
で標準出力にバッファを設定して処理時間を計測するだけです。
バッファは大きいほうがいいけど処理速度の向上が期待できるのはある程度の大きさまで。
printfの仕様
知っている人には当たり前のことですがprintf
は直接コンソールに文字列を出力させる関数ではありません。まずバッファへと内容を書き込み、そののちにバッファからコンソールに出力がなされます。
バッファからコンソールに出力されるタイミングですがデフォルトでは
- バッファサイズを上回る文字数を入力する
- 改行文字を入力する
-
fflush(stdout)
を使う - プロセスが終了する(このときは文字化けする。なんでだろ)
となっていることが多いです。これは行バッファリングに相当します。
C言語では他に改行文字での出力を行わない完全バッファリングと一切バッファリングを行わないバッファリングなしモードがあります。
自分でバッファを設定する
setvbuf
という関数を使います。第1引数でバッファを設定するストリームを指定し、第2引数で自分で用意したバッファを渡し、第3引数でバッファリングモードを指定します。
- _IONBFがバッファリングなし
- _IOLBFが行バッファリング
- _IOFBFが完全バッファリング
となっています。第4引数はバッファの大きさです。
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int type, size_t size);
例えば標準出力(stdout)に対して2文字分4バイトのバッファを設定するとこんな感じです。
char streamBuf[2];
if(setvbuf(stdout,streamBuf,_IOFBF,sizeof(streamBuf)) != 0){
perror("ERROR\n");
return 0;
}
バッファサイズによる処理速度の変化
バッファのサイズを変更しながらそれぞれについてprintf
で"Hello World!"を1万回表示し、その処理時間を計測します。
#include<stdio.h>
#include<time.h>
#define LOOP_MAX 10000
#define BUF_MAX 1024*4
int main(void){
clock_t cpu_time_start;
clock_t cpu_time_end;
double result_time;
FILE *fp;
fp = fopen("test.txt","w");
if(fp == NULL){
perror("ERROR\n");
return 0;
}
for(int buffer_size = 1 ;buffer_size <= BUF_MAX ;buffer_size *= 2){
char streamBuf[buffer_size];
if(buffer_size == 1){
if(setvbuf(stdout,NULL,_IONBF,0) != 0){
perror("ERROR\n");
return 0;
}
}else{
if(setvbuf(stdout,streamBuf,_IOFBF,sizeof(streamBuf)) != 0){
perror("ERROR\n");
return 0;
}
}
cpu_time_start = clock();
for(int i = 0;i < LOOP_MAX;i++){
printf("Hello World!");
}
fflush(stdout);
cpu_time_end = clock();
result_time = (double)(cpu_time_end - cpu_time_start)/CLOCKS_PER_SEC;
fprintf(fp,"%d,%f\n",buffer_size,result_time);
}
fclose(fp);
return 0;
}
実行結果
注 2バイトに対応するところはバッファなしの結果(_IONBF)
少し見にくいですがバッファが小さいうちはバッファサイズを倍にすると処理時間がおよそ半分になっているのが確認できます。
これはバッファはRAM上にあり、バッファへのアクセス速度は出力(今回は画面)へのアクセス速度に比べて非常に速いために起こっていると考えられます。
例えばバッファが4バイト(2文字分)の場合バッファなしの場合に比べて出力へのアクセス回数は半減しますからそれにより処理時間も半減しています。
横軸は指数オーダーなので縦軸もそれに揃えます。
バッファが大きくなるにつれ傾きが緩やかになっていくのが確認できます。これはバッファサイズが大きくなるにつれバッファへの書き出しにかかる時間が大きくなって無視できなくなることや、その他オーバーヘッドによるものと考えられます。
バッファとして使える領域は有限ですしただひたすらに大きく設定すればいいってもんでもなさそうです。