時間の測定
プログラムの評価基準
プログラムは様々な基準に基づいて評価される。基準が異なれば順位も異なる。ある 基準でよいプログラムが別の基準で悪く評価されることがある。基準は価値観の反映で ある。
評価基準には、性能、機能、再利用性、安全性、維持性などがある。性能は速さ、機能 は能力の高さや多さ、再利用性は他のプログラムから利用される頻度、安全性は安心して 利用できる完成度の高さ、維持性はソースの読みやすさなどである。中でも性能は重要 な基準である。
性能の評価基準にも種類がある。例えば、計算を開始してから終了するまでに実行され る行数、命令数や時間などがあげられる。行数はソースレベルだが、命令数は機械語レ ベルである。命令数はCPUの種類だけに依存するが、たとえCPUの種類が同じでも時間 はCPUの周波数にも依存する。一概にどの評価基準がよいとはいえない。時間は絶対的 な物理量だが、機種によって異なるため比較が難しい。行数や命令数は機種に依存しない が、実際の時間とは正確に一致しない。
ここでは、性能を評価するために時間を測定する方法について学ぶ。
時間による性能評価
パソコンの性能は年々上昇している。ムーアの法則によればCPUの性能は1.5年間で 2倍になる。3年後には4倍速くなる。よって、同じプログラムが1/4の時間で実行でき るようになる。このような場合、同じプログラムの実行時間を基準にパソコンの性能を 比較できる。
プログラムの性能を比較するには同じパソコンで実行する必要がある。パソコンが異 なると、その性能も異なるため、実行時間も変わってしまう。
プログラムの実行時間を計測するには時間を測定する必要がある。時間を測定するに は以下の方法がある。
1. TIMEコマンド
現在時刻を分単位で表示する。例)TIME /T 2. time関数
秒単位の時間を求める関数timeを用いて時間を表示する。
TIMEコマンド
TIMEコマンドは現在時刻を分単位で表示するコマンドである。TIMEコマンドはコマ ンドプロンプトで使う。下図は実行例である。
TIMEコマンドの詳しい使い方を知るには/?オプションを指定する。一般に分単位では 測定精度が不十分である。精度に関しては後ほど詳しく述べる。
time関数
time関数は秒単位の時間を返す。以下は現在時刻を秒単位で表示するプログラム second.cpp(付録C参照)である1。これをコンパイルしたコマンドsecond.exeは TIMEコマンドの代わりに使用することができる。
#include <stdio.h>
#include <time.h>
void main() { time_t t;
time(&t); // 時刻型time_t変数tに現在時刻をセット printf("%s\n", ctime(&t));
}
ここで、time_tは時間を表すデータ型(時刻型)であるが、実際には1970年1月1 日の0時からの秒数を表す整数である。time 関数は変数に現在時刻をセットする 。
ctimeは時刻型を文字列に変換する関数である。
演習1
現在時刻を秒単位で表示するプログラムsecond.exeを作成せよ。
ヒント VSでプロジェクトsecondを作成し、second.cppを入力する。
有効数字
定規などの目盛りで計る場合、目盛りの1/10まで読み取る。例えば、1mm目盛りの
1短いプログラムは本文中に記入してよい。ただし、本文とは異なることがわかるように 書式を変える。しかし、長いプログラムは付録に掲載し、本文ではその一部を引用する。
定規は0.1mm単位まで読み取る。そのため、12.3mmと読み取っても3には誤差があ る。真の値は12.25以上、12.35未満の範囲にあると考えられる。このとき12.3±0.05 と、範囲の中央値12.3と誤差±0.05を明記することがある。ただし、範囲の端の値が範 囲に含まれるかどうかはあまり重要ではない。なぜなら真の値が端にあることはないか らである。
測定の精度には個人差がある。熟練者なら1/10まで正しく読み取れても、初心者は 1/5までしか読み取れないことがある。その場合、12.3±0.1と記す。
誤差を明示する表記は正確であるが、読みにくい。そのため、しばしば誤差を省略す る。今、誤差を省略した値12.3を考えよう。このとき12.3を12.3±0.05と考えてよい のは、1/10まで読み取った場合に限られる。同様に12.3±0.1と考えてよいのは、目盛 りの1/5まで読み取った場合に限られる。誤差の範囲が±0.1を超えると12.2や12.4の 可能性も出てくる。誤差の範囲が広いと12.3であるとはいえなくなる。その場合、確か なことは12であるということだけである。
以上はアナログデータの場合である。アナログデータの場合、目盛りの1/10まで読み 取るという無理をしているため、最終桁の数値はあまり信用できない。信用できる桁ま でを有効数字という。有効数字が12.3であるなら0.3まで信用してよい。実験の測定値 は有効数字でなければならない。
有効数字の12と12.0は異なる。前者は12±0.5を意味するが、後者は12.0±0.05を 意味する。右端の0は有効数字の桁を表すため省略してはいけない。
デジタルデータは目盛り単位で読み取るため、全桁が有効数字である。1mmの定規で
12.3mmと測定された値の有効数字は12mmとなり、2桁である。ただし、デジタル測
定器の測定値が必ずしもデジタルデータであるとは限らない。中には過剰な表示をする 測定器もあるので注意が必要である。
有効数字を表すとき有効桁を明示するため浮動小数点で表示する。例えば、12を 1.2×101と表す。ここでXYはXのY乗を表す。120は1.20×102と表す。ここで1.20 の0を省略してはいけない。なぜなら、最後の0は有効桁が3桁であることを示してい るからである。
有効数字の計算には以下の規則がある。
剰余の有効桁数は元の有効桁数の小さな方と等しい。
例えば、1.23×102と3.4×103をかけると4.182×105であるが、3.4が2桁しか ないので4.2×105と丸める。
加減の有効数字は、元の有効数字の小さな方と桁を合わせて加減し、大きい方に合 わせて丸める。
例えば、1.23×102と3.4×103を足すと(1.23+34)×102=35.23×102であるが、
34が2桁までしかないので3.5×103と丸める。また、1.23×101+3.4×103は (0.123+34)×102=34×102となり、一方が誤差の範囲に入って無視されることが
ある。
減算で有効桁が減ることがある。
例えば、有効桁3桁の有効数字の差1.23×102-3.45×101を計算すると(12.3- 3.45)×101=8.85×101=8.9×101となる。
有効数字でない定数は普通に計算してよい。
有効数字を平均しても有効桁は増えない。
ここで、時計に話を移す。アナログ時計を使うと目盛りの1/10まで読み取ることも可 能であるように思えるが、最近のアナログ時計は秒単位で秒針が進むデジタル時計であ る。よって無理にアナログ時計で測定する必要はない。分単位の時計を使って測定する 場合、誤差の範囲は0~59秒である。例えば、3分と測定された場合、真の値は3分0 秒から3分59秒の中にある。言い換えれば、3+1.0/-0.0分である。これを秒に直すと
3*60=180秒となり、有効数字が3桁に増えたかのように見える。しかし、有効数字の
原則によれば乗算結果の有効桁は元の有効桁と等しいので、2×102秒と表すことが適切 である。いかにも大雑把に見えるが、測定値3分は3分59秒かもしれないので、ほぼ4 分の可能性もある。つまり4*60=240秒かもしれない。やはり2×102秒と表すことが適 切である。
測定値1分と9分では何が違うのだろう?いずれも有効桁は1桁である。1分は1分0 秒から1分59秒までの範囲を表し、その誤差は約100%(=59秒/1分)である。一方、9 分は9分0秒から9分59秒までの範囲を表し、その誤差は約10%(=59秒/9分)である。
よって、9分の方が相対的な誤差が少ない。10進数とは量を10段階に区別する方法と考 えられる。よって、誤差が10%以下でなければ0~9までの数字を確定できない。10進 数で有効桁1桁という場合、誤差が10%以下であることを意味する。
測定精度
ものさしによって測定精度は変わる。目盛が1cmの定規を用いて、1mmの大きさを 測定することはできない。人は目分量で目盛以下の数値を読み取ることができるが、こ れはアナログデータの場合に限られる。デジタルデータでは目盛以下のデータは無視さ れる。目盛が1cmの定規を用いて2cmと測定されれば、2.0以上3.0未満である。この 場合、有効数字は1桁である。真の値は2.x…であるが、小数部は不明である。1mmの 定規を用いて2.2cmと測定されれば、2.20以上2.30未満である。この場合、有効数字 は2桁である。
コンピュータで時間を測定する手段として1秒単位の時計しかない場合、10秒待てば 1桁の有効数字(0~9)が得られる。100秒待てば2桁の有効数字(0~99)が得られる。
1分単位の時計を使って2桁の有効数字を求めるには100分(1時間40分)かかる。授 業時間を越えてしまうので授業内では測定できない。
もし1mmの定規がなければ、拡大鏡を用いて対象を10倍に拡大し、1cmの定規を用
いればよい。もしも1cmの定規を用いて22cmと測定されれば、22.0以上23.0未満で ある。この場合も有効数字は2桁である。ただし、そのままでは10倍に拡大されている ので、10で割る必要がある。ここで、コンピュータで時間を測定する場合、拡大鏡に相 当するのは繰り返し回数である。同じ処理を10回繰り返せば、元の処理の10倍の時間 がかかる。10倍の時間を測定し、後で1/10すれば元の時間を測定することができる。
有効数字を明確に示すには浮動小数点表現が望ましい。例えば、有効数字2桁で測定し
た結果を220cmと書けば、1cmの位まで正確に測定されたと誤解される。しかし、
2.2×102cmと書けば、有効数字が2桁であることが明らかである。逆に3桁まで測定で きた場合、2.20×102cmと書く。
平均
有効数字を平均しても有効桁は増えない。それでは平均する意味はないのだろうか?
ものさしで0.1mmを読み取ることは困難である。人によって差が出ることもある。
その差は誤差となる。誤差を小さくするために何度も測定し、平均すれば真の値に近く なる。これはアナログデータの場合である。デジタルデータの場合、測定値はすべて有 効数字となるため、必ずしも平均を取る必要はない。
見方を変えれば平均を取ることは繰り返し回数を増やしていることに他ならない。例 えば、10回繰り返す測定を10回行えば、100回繰り返したことになる。前節で繰り返 し回数を増やすことは精度を上げることだと学んだ。しかし、ある程度の回数を繰り返 さなければ精度はあがらないため、中途半端な回数を繰り返し、その平均を取る意味は ほとんどない。例えば、10回繰り返す測定で有効桁1桁まで求めることができるなら、
100回繰り返す測定で有効数字2桁まで求めることができる。しかし、10回繰り返す測 定を10回行い平均しても有効桁は1桁のままなので100回繰り返す測定1回に及ばない。
それでは、十分な精度があれば平均を取る意味はないのだろうか?
コンピュータは様々な環境に影響される。例えば、2つのプログラムを同時に動かすと 1つしかないCPUを取り合ってどちらも性能が低下する。コンピュータで動作している プログラム(正確にはタスク)の数を負荷という。ネットワークを使うプログラムでは、
ネットワークの混雑が負荷となる。負荷が大きいと正確な測定ができない。正確な測定 をするには負荷を極力減らす必要がある。しかし、実際のOSではユーザが管理できない タスクが多く存在し、完全に負荷を0にすることはできない。そのため、なるべく同じ 条件で測定するために何度も繰り返す必要がある。この場合、負荷の影響を平均化してい ると考えられる。
もし、測定への影響が負荷だけなら、負荷は時間を増やすようにしか影響しないため、
測定された最小値が真の値に近いと考えられる。
バッチファイル
同じコマンドを何度も繰り返し実行するにはバッチ処理がよい。バッチ処理とは複数 の処理を一括して行う処理である。バッチ処理を行うにはバッチファイルを作成する。
バッチファイルとは、バッチ処理したいコマンドを並べて書いたテキストファイルで、
拡張子が.BATである。バッチファイルを作成するにはメモ帳などのテキストエディタで コマンドを1行に1つずつ記述し、拡張子を.BATにして保存すればよい。
コマンドの実行時間を測定するために、以下のようにコマンドを入力すると、実行より キータイプする時間の方が長い。
C:\> TIME /T 10:30
C:\> A.EXE 計測したいコマンド C:\> TIME /T
10:32
そこで、図 1のようなバッチファイルWORK.BATを作成する2。ここで、@echo of はコマンド自身を表示しないようにし、>NULはコマンドの出力を表示しないようにす る。PAUSEは実行を終了するとき確認する。
@echo of TIME /T A.EXE >NUL TIME /T PAUSE
図 1 WORK.BAT
WORK.BATを実行すると、以下のようにキータイプの時間がかからない。
C:\> WORK.BAT 10:30
10:31
続行するには何かキーを押してください...
繰り返し
バッチファイルでは特定のコマンドを何度も繰り返したいことがある。このような場 合、FORコマンドを使う。FORコマンドの詳細な説明は/?オプションを指定して実行す
2短いプログラムは図として掲載することもできる。罫線を用いていても図として扱う。
実行結果もプログラムに準じる。
ると表示される。
今回は以下のような構文を用いる。
FOR /L %変数 IN (開始,ステップ,終了) DO コマンド
例えば、A.EXEコマンドを100回繰り返すには以下のようにする。
FOR /L %%I IN (1,1,100) DO A.EXE
実験1 プログラム全体の実行時間
サンプルプログラム(付録A)について実行時間をTIME、timeのそれぞれ方法で測定し なさい。ただし、TIME、timeの精度はそれぞれ1桁、2桁とする。また、サンプルプロ グラムをコンパイルしたコマンドを用い、サンプルプログラムに手を加えてはいけない 。 つまり、この評価ではプログラムをメモリに読み込む時間も含まれる。2つの方法の実 行時間を比較し、考察しなさい。
ヒント TIMEコマンドの場合、有効数字が1桁になるだけサンプルプログラムを繰り 返すようにWORK1.BAT(付録B参照)を編集する。time関数の場合、秒単位で時 間を表示するコマンド(付録C参照)を作成し、これをTIMEコマンドの代わりに用 いる。
付録A サンプルプログラム
10000以下の素数の数を数えるプログラム(L06X01.cpp)
#include <stdio.h>
void main() {
int n, k, f, c = 0;
for(n=2; n<=10000; n=n+1) { f = 1;
for(k=2; k*k<=n; k=k+1) { if (n % k == 0) {
f = 0;
break;
} }
if (f) {
c = c + 1;
} }
printf("%d\n", c);
}
付録B 分単位で時間を測定するバッチファイル
TIMEコマンドを用いて分単位で時間を測定するバッチファイル(WORK1.BAT)
@echo of TIME /T
FOR /L %%I IN (1,1,100) DO L06X01.exe >NUL TIME /T
PAUSE
付録C 秒単位で時間を測定するプログラム
time関数を用いて秒単位で時間を測定するプログラム(second.cpp)
#include <stdio.h>
#include <time.h>
void main() { time_t t;
time(&t); // 時刻型time_t変数tに現在時刻をセット printf("%s\n", ctime(&t));
}
付録E 秒単位で時間を測定するバッチファイル
second.exeコマンドを用いて秒単位で時間を測定するバッチファイル(WORK2.BAT)
@echo of second.exe
FOR /L %%I IN (1,1,100) DO L06X01.exe >NUL second.exe
PAUSE