参考書を読みながらclock関数を用いたプログラムの実行時間を計測するプログラムを何個か作成していたのですが、はじめの方のサンプルプログラムは問題なく動いていたのに、数個目のプログラムから実行時間が正しく表示されなくなってしまいました。
問題のコード
参考書で作成していたサンプルプログラムの要所を抜粋したサンプルプログラムです。
/* clock関数を用いて実行時間を計測するプログラム */ #include <time.h> #include <stdio.h> int main(void) { clock_t start, end; int n; start = clock(); // 経過時間を取得 puts("数秒待ってから適当な値を入力してください。"); printf("n : "); scanf("%d", &n); end = clock(); // 経過時間を取得 printf("このプログラムの実行時間はは%.1f秒です。\n", (double)(end - start) / CLOCKS_PER_SEC); printf("debug : end = %f\n", end); printf("debug : start = %f\n", start); return 0; } /*--- RESULT ----- $ ./a 数秒待ってから適当な値を入力してください。 n : 3 プログラム実行から終了までの時間は0.0秒です。 debug : end = 0.000000 debug : start = 0.000000 ----- RESULT ---*/
ユーザーの入力の前にclock関数で経過時間を取得し、ユーザーの入力が終わった時点でもう一度clock関数で経過時間を取得し、経過時間の差からプログラムの実行時間を計算、表示するプログラムになっています。
実際に実行してみると、入力可能の画面になってから数秒待って値を入力しても、clock_t型のendの数値が0のままになっています。
clock関数の挙動
どのコードが問題なのか、参考書と実際のコードを良く確かめてみても大きな違いは見当たらず。
試しに公式で配布されているサンプルコードをコンパイラに通して実行してみると、自分が書き込んだコードと同じでプログラム実行時間は0秒と表示されてしまいました。
これはコードミスとは別の問題かなと思いつつ、処理系をCygwin & gccからとcommand prompt & clに変更して翻訳・実行してみると、自分のプログラムも公式のサンプルも思った通りの動作をしてくれます。
これが参考書の記述でよく見かける処理系による挙動の違いかな?とようやく勘付き、ネット上のリファレンスを確認してみると案の定、”処理系に依存します”の文字を確認しました。
プログラム実行開始からの経過時間(プロセッサ時間)を返却します。経過時間の精度は処理系に依存します。
この時間は、厳密には ISO C99 (正味の CPU 時間を戻り値にすることが規定されている) に準拠していないことに注意してください。
どうやら参考書の処理系と違ったことが原因だったようです。
参考書を見直すと確かに掲載されている関数仕様の欄には”処理系定義の時点から~”と記載されています。
具体的な挙動の違い
でははじめの方のサンプルプログラムはなぜ問題なくプログラムの実行時間が取得できたのか。
部分的にコメントアウトしてデバッグをしてみると、次のようなサンプルならCygwin & gccな処理系でも実行時間が取得できました。
/* clock関数とsleep関数を用いて実行時間を取得・表示するプログラム */ #include <time.h> #include <stdio.h> /*--- xミリ秒経過するのを待つ ---*/ int sleep(unsigned long x) { clock_t c1 = clock(), c2; do { if ((c2 = clock()) == (clock_t)-1) return 1; } while (1000.0 * (c2 - c1) / CLOCKS_PER_SEC < x); return 1; } int main(void) { int t; clock_t start, end; puts("何秒プログラムをスリープさせますか?"); printf("t : "); scanf("%d", &t); start = clock(); // 経過時間取得 sleep(1000 * t); // tミリ秒スリープ end = clock(); // 経過時間取得 printf("このプログラムの実行時間は%.1f秒でした。\n", (double)(end - start) / CLOCKS_PER_SEC); return 0; } /*--- RESULT ----- $ ./a 何秒プログラムをスリープさせますか? t : 3 このプログラムの実行時間は3.0秒でした。 ----- RESULT --- */
このプログラムのsleep関数はxミリ秒経過するまでループする関数です。
gccではscanf関数でユーザーの処理を待つという形では経過時間が加算されず、sleep関数のような処理の場合にのみ経過時間が加算されるようです。
参考書のサンプルプログラムなので処理系依存といえど、結果は似たようなものになるものだと高をくくっていましたが、処理依存の処理はここまで変わってくるものなんですね。
よく覚えておこうと思います。