低水準入出力と高水準入出力の速度の違い

低水準入出力関数(システムコールのwrite, read)は、呼び出しがとても遅いのでprintfなどの高水準のブッファリングを取り入れた標準関数を使用することが一般的に推奨されています。

今回は、低水準、高水準の両者で同様の処理をしたときにどの程度、所要時間に差が出るのかを検証するプログラムを勉強しました。

下に、二つのプログラムをのせますが、どちらも二つのファイルをオープンして一方から内容をバイト単位で読み込み、他方へバイト単位で書き出すプログラムです。

入出力処理の前後で、現在時刻を取得し、その差分で所要時間を計算させています。


○システムコールを使うタイプ



// high_level_io.c
#include<stdio.h> //fprintf
#include<stdlib.h>//exit
#include<sys/time.h> //gettimeofday
#include<sys/types.h>//read
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>


double time_pass()
{
struct timeval tv;
//現在の時刻を取得
gettimeofday(&tv, NULL);
//秒数に換算して値を返却
return tv.tv_sec + (double) tv.tv_usec * 1e-6;
}

int main(int argc, char *argv[])
{
int *fdin, *fdout;
double start_time, end_time;
char c;

if(argc != 3)
{
fprintf(stderr, "Usage: copy_highio <file1> <file2>\n");
exit(EXIT_FAILURE);
}

//入力ファイルを開く
if((fdin = open(argv[1], O_RDONLY)) < 0)
{
perror(argv[1]);
exit(EXIT_FAILURE);
}
//出力ファイルを開く
if((fdout = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
{
perror(argv[2]);
exit(EXIT_FAILURE);
}

//開始時刻を取得
start_time = time_pass();

//ファイルよりバイト単位でデータを読み込んで、出力ファイルへ出力
while(read(fdin, &c, 1) == 1)
{
write(fdout, &c, 1);
}

//終了時刻を取得
end_time = time_pass();

//ファイルを閉じる。
close(fdin);
close(fdout);

printf("%.4f\n", end_time - start_time);
}





○標準入出力を使うタイプ


// high_level_io.c
#include<stdio.h> //fprintf
#include<stdlib.h>//exit
#include<sys/time.h> //gettimeofday

double time_pass()
{
struct timeval tv;
//現在の時刻を取得
gettimeofday(&tv, NULL);
//秒数に換算して値を返却
return tv.tv_sec + (double) tv.tv_usec * 1e-6;
}

int main(int argc, char *argv[])
{
FILE *fpin, *fpout;
double start_time, end_time;
char c;

if(argc != 3)
{
fprintf(stderr, "Usage: copy_highio <file1> <file2>\n");
exit(EXIT_FAILURE);
}

//入力ファイルを開く
if((fpin = fopen(argv[1], "rb")) == NULL)
{
perror(argv[1]);
exit(EXIT_FAILURE);
}
//出力ファイルを開く
if((fpout = fopen(argv[2], "wb")) == NULL)
{
perror(argv[2]);
exit(EXIT_FAILURE);
}

//開始時刻を取得
start_time = time_pass();

//ファイルよりバイト単位でデータを読み込んで、出力ファイルへ出力
while(fread(&c, 1, 1, fpin) == 1)
{
fwrite(&c, 1, 1,fpout);
}

//終了時刻を取得
end_time = time_pass();

//ファイルを閉じる。
fclose(fpin);
fclose(fpout);

printf("%.4f\n", end_time - start_time);
}

#プログラムのビルド
$ gcc -o low_level_io  low_level_io.c

$ gcc -o high_level_io  high_level_io.c



#GNUが配布しているlsコマンドのソースコードをコピーしてみます


$ ./low_level_io ls.c  out.2
0.8041

$ ./high_level_io ls.c  out.1
0.0282


0.8041秒と、0.0282秒とは。。。。

ブッファリングを導入したことで飛躍的に処理が早くなっていることが一目両全です。

システムコールはやはり特別なものなんですね。