【C言語】処理の経過時間をミリ秒単位で計測する方法

この記事では、C言語を使ってプログラムの処理時間をミリ秒単位で計測する方法について解説します。

時間を計測するために使うライブラリや関数、具体的な実装例を紹介し、どのようにしてプログラムの性能を評価できるかを学びます。

目次から探す

C言語における時間計測の基本

C言語では、プログラムの処理時間を計測するためにいくつかのライブラリが用意されています。

これらのライブラリを使用することで、処理の経過時間をミリ秒単位で計測することが可能です。

ここでは、主に使用されるライブラリと時間の単位について解説します。

時間計測に使用するライブラリ

C言語で時間を計測するために、主に以下の2つのライブラリが使用されます。

<time.h>ライブラリ

<time.h>は、C言語の標準ライブラリの一部で、時間に関する様々な機能を提供します。

このライブラリには、時間を計測するための関数が含まれており、特にclock()time()関数がよく使用されます。

  • clock()関数は、プログラムの実行時間をクロック単位で返します。

これを使って、処理の経過時間を計測することができます。

  • time()関数は、1970年1月1日からの経過秒数を返します。

この関数を使って、時間の差を計算することができます。

<sys/time.h>ライブラリ

<sys/time.h>は、Unix系のシステムで使用されるライブラリで、より高精度な時間計測が可能です。

このライブラリには、gettimeofday()関数が含まれており、ミリ秒単位での時間計測ができます。

  • gettimeofday()関数は、現在の時刻を取得し、秒とマイクロ秒の形式で返します。

このため、ミリ秒単位での計測が容易になります。

時間の単位とその変換

時間の計測において、さまざまな単位が使用されます。

主な単位は以下の通りです。

  • 秒 (s): 基本的な時間の単位で、1秒は1000ミリ秒に相当します。
  • ミリ秒 (ms): 1秒の1/1000の時間で、プログラムの処理時間を細かく計測するのに適しています。
  • マイクロ秒 (μs): 1ミリ秒の1/1000の時間で、さらに高精度な計測が必要な場合に使用されます。

これらの単位の変換は以下のように行います。

  • 1秒 = 1000ミリ秒
  • 1ミリ秒 = 1000マイクロ秒

このように、時間の単位を理解し、適切なライブラリを使用することで、C言語における処理の経過時間を正確に計測することができます。

次のセクションでは、具体的な時間計測の方法について詳しく見ていきましょう。

<time.h>を使用したミリ秒単位の計測

C言語では、処理の経過時間を計測するために、主に<time.h>ライブラリを使用します。

このライブラリには、時間を計測するための便利な関数がいくつか用意されています。

ここでは、clock()関数time()関数を使ったミリ秒単位の計測方法について解説します。

clock()関数の使い方

clock()関数は、プログラムの実行時間をCPUクロックの単位で返します。

この関数を使用することで、処理にかかった時間を計測することができます。

CLOCKS_PER_SECの理解

clock()関数が返す値は、CPUが経過したクロックの数です。

この値を秒に変換するためには、CLOCKS_PER_SECという定数を使用します。

CLOCKS_PER_SECは、1秒あたりのクロック数を表しており、通常は1000000(1秒あたり100万クロック)です。

以下の式を使って、clock()関数の戻り値をミリ秒に変換できます。

経過時間(ミリ秒) = (clock()の戻り値 / CLOCKS_PER_SEC) * 1000

time()関数の使い方

time()関数は、1970年1月1日からの経過時間を秒単位で返します。

この関数を使って、処理の開始時刻と終了時刻を取得し、その差を計算することで経過時間を求めることができます。

time()関数は、以下のように使用します。

#include <time.h>
time_t start_time, end_time;
start_time = time(NULL); // 処理開始時刻を取得
// ここに計測したい処理を書く
end_time = time(NULL); // 処理終了時刻を取得

実装例

以下に、clock()関数time()関数を使った実装例を示します。

この例では、簡単なループ処理の経過時間を計測します。

#include <stdio.h>
#include <time.h>
int main() {
    // clock()を使用した計測
    clock_t start_clock, end_clock;
    start_clock = clock(); // 処理開始時刻を取得
    // 計測したい処理(例:ループ処理)
    for (long i = 0; i < 100000000; i++); // 何もしないループ
    end_clock = clock(); // 処理終了時刻を取得
    double elapsed_time_clock = (double)(end_clock - start_clock) / CLOCKS_PER_SEC * 1000; // ミリ秒に変換
    printf("clock()を使用した経過時間: %f ミリ秒\n", elapsed_time_clock);
    // time()を使用した計測
    time_t start_time, end_time;
    start_time = time(NULL); // 処理開始時刻を取得
    // 計測したい処理(例:ループ処理)
    for (long i = 0; i < 100000000; i++); // 何もしないループ
    end_time = time(NULL); // 処理終了時刻を取得
    double elapsed_time_time = difftime(end_time, start_time) * 1000; // ミリ秒に変換
    printf("time()を使用した経過時間: %f ミリ秒\n", elapsed_time_time);
    return 0;
}

このプログラムを実行すると、clock()関数time()関数を使用したそれぞれの経過時間がミリ秒単位で表示されます。

clock()関数はCPUの使用時間を計測するため、CPU負荷の高い処理に対してより正確な結果を得ることができます。

一方、time()関数はシステム全体の経過時間を計測するため、他のプロセスの影響を受けることがあります。

このように、C言語では<time.h>ライブラリを利用することで、簡単に処理の経過時間をミリ秒単位で計測することができます。

<sys/time.h>を使用した高精度な計測

C言語では、<sys/time.h>ライブラリを使用することで、より高精度な時間計測が可能になります。

このライブラリには、gettimeofday()関数が含まれており、マイクロ秒単位での時間計測ができます。

<sys/time.h>はWindows系では使えません。注意してください。

これにより、処理の経過時間をミリ秒単位で計測することが容易になります。

gettimeofday()関数の使い方

gettimeofday()関数は、現在の時刻を取得するための関数です。

この関数は、2つの引数を取ります。

1つ目は、struct timeval型のポインタで、ここに現在の時刻が格納されます。

2つ目は、struct timezone型のポインタですが、通常はNULLを指定します。

以下は、gettimeofday()関数の基本的なシグネチャです。

int gettimeofday(struct timeval *tv, struct timezone *tz);

struct timevalは、次のように定義されています。

struct timeval {
    time_t      tv_sec;     // 秒
    suseconds_t tv_usec;    // マイクロ秒
};

この構造体を使用することで、秒とマイクロ秒の両方を取得できます。

実装例

以下に、gettimeofday()を使用して処理の経過時間をミリ秒単位で計測するサンプルプログラムを示します。

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h> // sleep()関数を使用するため
int main() {
    struct timeval start, end;
    // 計測開始
    gettimeofday(&start, NULL);
    // ここで計測したい処理を実行
    sleep(1); // 1秒間スリープ
    // 計測終了
    gettimeofday(&end, NULL);
    // 経過時間を計算
    long seconds = end.tv_sec - start.tv_sec;
    long microseconds = end.tv_usec - start.tv_usec;
    long elapsed = seconds * 1000 + microseconds / 1000; // ミリ秒に変換
    printf("経過時間: %ldミリ秒\n", elapsed);
    return 0;
}

このプログラムでは、gettimeofday()を使って処理の開始時刻と終了時刻を取得し、その差を計算してミリ秒単位で表示しています。

sleep(1)関数を使って1秒間の処理をシミュレートしています。

精度の比較

gettimeofday()関数は、マイクロ秒単位での精度を持っているため、非常に高精度な時間計測が可能です。

これに対して、clock()関数は、プロセッサのクロックサイクルに基づいて時間を計測するため、システムの負荷や他のプロセスの影響を受けることがあります。

以下に、clock()gettimeofday()の精度の違いをまとめた表を示します。

関数名精度使用例
clock()クロックサイクル単位CPUの処理時間を計測する場合
gettimeofday()マイクロ秒単位高精度な時間計測が必要な場合

このように、gettimeofday()は高精度な時間計測が求められる場面で特に有用です。

処理の経過時間を正確に把握したい場合は、<sys/time.h>ライブラリを活用することをお勧めします。

クロックの精度と注意点

C言語で処理の経過時間を計測する際には、使用するクロックの精度や特性に注意が必要です。

ここでは、クロックの解像度、システム依存性、マルチスレッド環境での注意点について詳しく解説します。

クロックの解像度

クロックの解像度とは、時間を計測する際の最小単位のことを指します。

C言語で使用するclock()関数gettimeofday()関数は、それぞれ異なる解像度を持っています。

  • clock()関数は、プロセッサのクロックサイクルを基にした時間を計測しますが、解像度はシステムによって異なります。

一般的には、ミリ秒単位での計測が可能ですが、実際の精度は環境によって変わることがあります。

  • 一方、gettimeofday()関数は、マイクロ秒単位での高精度な時間計測が可能です。

このため、より短い処理時間を計測したい場合には、gettimeofday()を使用することが推奨されます。

システム依存性

C言語の時間計測は、使用するオペレーティングシステムやハードウェアに依存します。

例えば、WindowsとLinuxでは、時間計測の実装が異なるため、同じコードを実行しても結果が異なる場合があります。

  • Windowsでは、QueryPerformanceCounter()QueryPerformanceFrequency()を使用することで、高精度な時間計測が可能です。
  • Linuxでは、clock_gettime()関数を使用することで、ナノ秒単位の精度で時間を計測できます。

このように、プラットフォームによって使用する関数やその精度が異なるため、移植性を考慮する必要があります。

マルチスレッド環境での注意点

マルチスレッド環境では、時間計測において特別な注意が必要です。

複数のスレッドが同時に実行されるため、計測結果が予期しないものになることがあります。

  • スレッド間での競合状態が発生する可能性があるため、特定の処理の開始と終了を正確に計測することが難しくなることがあります。

これを避けるためには、計測対象の処理を排他制御することが重要です。

  • また、スレッドのスケジューリングによって、実行時間が変動することも考慮しなければなりません。

特に、短い処理時間を計測する場合、他のスレッドの影響を受けやすくなります。

このような理由から、マルチスレッド環境での時間計測は、単一スレッドの環境に比べて難易度が高くなるため、注意が必要です。

実際のプログラムでの応用

処理時間を計測するサンプルプログラム

ここでは、C言語を使用して処理時間を計測するサンプルプログラムを示します。

このプログラムでは、配列の要素をソートする処理の時間を計測します。

具体的には、クイックソートアルゴリズムを用いて、処理にかかる時間をミリ秒単位で計測します。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// クイックソートの実装
void quicksort(int arr[], int low, int high) {
    if (low < high) {
        int pivot = arr[high]; // ピボットを選択
        int i = (low - 1); // 小さい要素のインデックス
        for (int j = low; j < high; j++) {
            if (arr[j] < pivot) {
                i++;
                // 要素の交換
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        // ピボットを正しい位置に移動
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;
        // 再帰的にソート
        quicksort(arr, low, i);
        quicksort(arr, i + 2, high);
    }
}
int main() {
    // 配列の初期化
    int arr[] = {34, 7, 23, 32, 5, 62};
    int n = sizeof(arr) / sizeof(arr[0]);
    // 処理開始前の時間を取得
    clock_t start = clock();
    // クイックソートを実行
    quicksort(arr, 0, n - 1);
    // 処理終了後の時間を取得
    clock_t end = clock();
    // 経過時間を計算
    double time_taken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000; // ミリ秒に変換
    // ソート結果の表示
    printf("ソート後の配列: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n処理時間: %.2f ミリ秒\n", time_taken);
    return 0;
}

このプログラムでは、まず配列を初期化し、clock()関数を使用して処理の開始時間を取得します。

その後、クイックソートを実行し、処理が終了した後に再度clock()関数を使用して終了時間を取得します。

最後に、経過時間を計算し、ミリ秒単位で表示します。

計測結果の解析と活用方法

上記のプログラムを実行すると、ソート後の配列と処理にかかった時間が表示されます。

例えば、次のような出力が得られるかもしれません。

ソート後の配列: 5 7 23 32 34 62 
処理時間: 0.12 ミリ秒

この結果から、クイックソートアルゴリズムが配列を効率的にソートできたことがわかります。

また、処理時間が非常に短いことから、アルゴリズムの性能が良好であることも示されています。

このように、処理時間を計測することで、プログラムの性能を評価し、最適化の必要性を判断することができます。

特に、大規模なデータセットや複雑なアルゴリズムを扱う場合、処理時間の計測は非常に重要です。

計測結果をもとに、アルゴリズムの選択やデータ構造の改善を行うことで、プログラムの効率を向上させることができます。

さらに、異なるアルゴリズムや実装を比較する際にも、処理時間の計測は有用です。

例えば、クイックソートとマージソートの処理時間を比較することで、どちらのアルゴリズムが特定のデータセットに対してより効率的であるかを判断できます。

このように、時間計測はプログラミングにおいて非常に重要な要素となります。

目次から探す