作曲・指導・C言語・Linux

金沢音楽制作

金沢音楽制作では、楽曲・楽譜の制作と、作曲や写譜などレッスンを行っています。

出力関数による速度の差について

出力関数の速度について調べてみました。調査方法は、timeコマンドを使って1000万行のテキストファイルを読み込んで、標準出力とファイル出力します。何度か実行してみて、早かった順のタイムを3つ提示しました。

環境:Macbook 2020(i7 1.2GHz, RAM 16GB)、gcc 11.2.0

入力関数は、putc()fputc()の時はfgetc()を、それ以外はfgets()を使います。出力関数は、fputc()printf()puts()fputs()を対象としています。なお、puts()を使う時は、文字列の改行コードを削るのにstrlen()strtok()も使うものとします。

結論

fgetc()fputc()は非常に遅く、また速度にばらつきがあります。他の関数は全て同じくらいの速さです。とはいえ、1000万行もあるファイルでの話ですから、使い方によっては誤差の範囲かも知れません。

関数 出力先 速度1 速度2 速度3
getc() 標準出力 1m7.786s 1m10.816s 1m21.650s
printf() 標準出力 0m33.246s 0m33.769s 0m33.991s
puts() + strtok() 標準出力 0m32.217s 0m32.603s 0m32.825s
puts() + strlen() 標準出力 0m33.398s 0m33.869s 0m33.912s
fputs() 標準出力 0m32.044s 0m32.628s 0m32.730s
fputc() ファイル出力 0m47.236s 0m48.154s 0m49.020s
fprintf() ファイル出力 0m1.909s 0m1.928s 0m1.967s
fputs() ファイル出力 0m1.903s 0m1.932s 0m1.954s

基本となるコード

標準出力の場合、以下のコードのoutput()を変更します。引数にファイルを指定すると標準出力されます。catコマンドのような振る舞いをします。なお、速度を調査することが目的なので、エラー処理は一切行っていません。

#include <stdio.h>
#include <string.h>  // when using strtok() || strlen()

static void output(FILE *fp);

int main(int argc, char *argv[])
{
  int i;

  for (i = 1; i < argc; i++) {
    FILE *fp;

    fp = fopen(argv[i], "r");

    output(fp);

    fclose(fp);
  }

  return 0;
}

ファイル出力は、以下のコードのoutputFile()を変更します。実行ファイルの第1引数に出力ファイルを、第2引数以降に入力ファイルを指定します。

#include <stdio.h>

static void outputFile(FILE *fpo, FILE *fpi);

int main(int argc, char *argv[])
{
  FILE *fpo;
  int i;

  fpo = fopen(argv[1], "a");

  for (i = 2; i < argc; i++) {
    FILE *fpi;

    fpi = fopen(argv[i], "r");

    outputFile(fpo, fpi);

    fclose(fpi);
  }

  fclose(fpo);

  return 0;
}

標準出力

putc()

static void output(FILE *fp)
{
  int c;

  while ((c = fgetc(fp)) != EOF) {
    putc(c, stdout);
  }
}
1st 2nd 3rd
real 1m7.786s 1m10.816s 1m21.650s
user 0m58.860s 1m1.486s 1m10.747s
sys 0m8.392s 0m8.794s 0m10.197s

printf()

static void output(FILE *fp)
{
  const int MaxLength = 256;
  char buf[MaxLength];

  while (fgets(buf,MaxLength,fp) != NULL) {
    printf("%s", buf);
  }
}
1st 2nd 3rd
real 0m33.246s 0m33.769s 0m33.991s
user 0m7.399s 0m7.416s 0m7.530s
sys 0m9.374s 0m9.364s 0m9.490s

puts() + strtok()

static void output(FILE *fp)
{
  const int MaxLength = 256;
  char buf[MaxLength];

  while (fgets(buf,MaxLength,fp) != NULL) {
    strtok(buf, "\n");
    puts(buf);
  }
}
1st 2nd 3rd
real 0m32.217s 0m32.603s 0m32.825s
user 0m7.471s 0m7.656s 0m7.537s
sys 0m8.727s 0m8.989s 0m8.685s

puts() + strlen()

static void output(FILE *fp)
{
  const int MaxLength = 256;
  char buf[MaxLength];

  while (fgets(buf,MaxLength,fp) != NULL) {
    buf[strlen(buf)-1] = '\0';
    puts(buf);
  }
}
1st 2nd 3rd
real 0m33.398s 0m33.869s 0m33.912s
user 0m6.163s 0m6.228s 0m6.216s
sys 0m9.053s 0m9.116s 0m9.180s

fputs()

static void output(FILE *fp)
{
  const int MaxLength = 256;
  char buf[MaxLength];

  while (fgets(buf,MaxLength,fp) != NULL) {
    fputs(buf, stdout);
  }
}
1st 2nd 3rd
real 0m32.044s 0m32.628s 0m32.730s
user 0m5.738s 0m5.883s 0m5.858s
sys 0m8.729s 0m8.922s 0m8.901s

ファイル出力

fputc()

static void outputFile(FILE *fpo, FILE *fpi)
{
  char c;

  while ((c = fgetc(fpi)) != EOF) {
    fputc(c, fpo);
  }
}
1st 2nd 3rd
real 0m47.236s 0m48.154s 0m49.020s
user 0m45.435s 0m46.581s 0m47.170s
sys 0m1.035s 0m1.073s 0m1.084s

fprintf()

static void outputFile(FILE *fpo, FILE *fpi)
{
  const int MaxLength = 256;
  char buf[MaxLength];

  while (fgets(buf,MaxLength,fpi) != NULL) {
    printf(fpo, "%s", buf);
  }
}
1st 2nd 3rd
real 0m1.909s 0m1.928s 0m1.967s
user 0m0.932s 0m0.948s 0m0.954s
sys 0m0.839s 0m0.845s 0m0.841s

fputs()

static void outputFile(FILE *fpo, FILE *fpi)
{
  const int MaxLength = 256;
  char buf[MaxLength];

  while (fgets(buf,MaxLength,fpi) {
    fputs(buf, fpo);
  }
}
1st 2nd 3rd
real 0m1.903s 0m1.932s 0m1.954s
user 0m0.949s 0m0.975s 0m0.956s
sys 0m0.835s 0m0.849s 0m0.853s

更新情報