\r\n\r\n
プロセスの実行時間などを知りたいと思いませんか? Linuxのtimeコマンドは時間の統計情報を返し、プログラムが使用しているリソースについてより深く知ることができます。
Linuxには多くのディストリビューションやUnix系OSがあり、それぞれにデフォルトのコマンドシェルが存在します。最近のLinuxディストリビューションで最も一般的なデフォルトのシェルはbashシェルですが、他にもzshell(zsh)やKornシェル(ksh)など、多くのシェルがあります。
これらのシェルはすべて、組み込みコマンドまたは予約語によって、独自の時間コマンドを含んでいます。ターミナルウィンドウに時間を入力すると、Linuxディストリビューションの一部として提供されているGNU timeバイナリを使用するのではなく、シェルが内部コマンドを実行します。
GNU版のtimeを使いたかったのは、より多くのオプションがあり、より柔軟性があるからです。
どのバージョンが実行されるかは、typeコマンドで確認することができます。このタイプは、シェルがそれ自身でコマンドを処理するのか、あるいはその内部ルーチンをGNUバイナリに渡すのかを知らせるものです。
ターミナルウィンドウにtypeという単語とスペースを入力し、timeという単語を入力してEnterキーを押します。
type timeバッシュシェルでは、timeが予約語になっていることがわかります。これは、Bashがデフォルトでそのinternaltimeルーチンを使用することを意味します。
type timezshell (zsh) では、time は予約語なので、デフォルトで内部シェルルーチンが使用されます。
type timekornshellでは、timeはキーワードであり、GNU timeコマンドの代わりに内部ルーチンが使用されます。
関連:ZSHとは何ですか、なぜBashの代わりにZSHを使うべきなのですか?
Linuxシステムのシェルが内部時間ルーチンを持っている場合、GNU時間バイナリを使いたい場合は、明示する必要があります。しなければなりません。
which timeコマンドは、バイナリファイルのパスを指定します。
GNUバイナリを起動するコマンドとして/usr/bin/timeを使用することで、これをテストすることができます。これはよく効きますね。timeコマンドから、コマンドライン引数が与えられていないことを伝える応答が返ってくる。
コマンドのtimeを入力するのも効果的で、timeから同じ使用情報を得ることができます。このコマンドは、次のコマンドを無視するようにシェルに指示し、シェルの外で処理できるようにします。
コマンド名の前に "hello "をつけるのは、コマンド名の前に "command "をつけるのと同じです。
GNU timeバイナリを確実に使用する最も簡単な方法は、バックスラッシュオプションを使用することです。
time \timeⅷはシェル版timeを呼び出す。
いくつかのプログラムをタイムアウトさせよう。loop1とloop2という2つのプログラムを使っています。これらはloop1.cとloop2.cから作成され、1種類の非効率なコーディングの効果を示す以外、何の役にも立ちません。
これはloop1.cである。2つのネストしたループの中で文字列の長さが要求されるが、2つのネストしたループの外側で長さをあらかじめ取得している。
#include "stdio.h" #include "string.h" #include "stdlib.h" int main (int argc, char* argv[]) { int i, j, len, count=0; char szString[]="how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek"; // get length of string once, outside of loops len = strlen( szString ); for (j=0; j<500000; j++) { for (i=0; i < len; i++ ) { if (szString[i] == '-') count++; } } printf("Counted %d hyphens\n", count); exit (0); } // end of mainこれはloop2.cで、外側ループの各ループで文字列の長さを何度も取得する。この非効率性は、タイミングに現れるはずです。
#include "stdio.h" #include "string.h" #include "stdlib.h" int main (int argc, char* argv[]) { int i, j, count=0; char szString[]="how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek"; for (j=0; j<500000; j++) { // getting length of string every // time the loops trigger for (i=0; i < strlen(szString); i++ ) { if (szString[i] == '-') count++; } } printf("Counted %d hyphens\n", count); exit (0); } // end of mainloop1プログラムを起動し、その性能を時間軸で計測してみましょう。
\time ./loop1では、loop2についても同様にしてみましょう。
\time ./loop2この結果、2つの結果が得られますが、非常に醜い形式です。これは後でどうにかするとして、結果からいくつかの情報を拾ってみましょう。
プログラムが実行されているとき、その間を行き来する2つの実行モードがあり、これをユーザーモードとカーネルモードと呼びます。
つまり、ユーザーモードのプロセスは、自身の割り当て外のハードウェアや参照メモリに直接アクセスすることはできません。これらのリソースにアクセスするためには、プロセスがカーネルに要求を出す必要がある。カーネルが要求を承認した場合、プロセスはカーネルモードに入り、要求が満たされるまで実行される。その後、ユーザーモードでの実行に切り替わります。
loop1の結果は、ユーザーモードで0.09秒かかっていることが分かります。カーネルモードでの滞在時間がゼロであったか、カーネルモードでの滞在時間が少なすぎて一旦切り上げられ、登録されなかったかのどちらかです。総実行時間は0.1秒。loop1は総実行時間のうち、平均89%のCPU時間を獲得している。
非効率なloop2のプログラムは、実行時間が3倍かかる。その総実行時間は0.3秒です。ユーザーモードでの処理時間は0.29秒です。カーネルモードは登録されていません。loop2は、実行時に平均96%のCPU時間を取得します。
フォーマット文字列を使用して、時間出力をカスタマイズすることができます。フォーマット文字列は、テキストとフォーマット指定子の両方を含むことができる。フォーマット指定子の一覧は、マニュアルページに記載されています。各フォーマット記述子は、情報の断片を表す。
文字列を印刷する場合、書式指定子は、それらが表す実際の値に置き換えられます。例えば、CPUのパーセンテージを表すフォーマットディスクリプタはPという文字ですが、通常の文字以上のものであることを時間的に示すために、%Pのようにパーセント記号を付けます。 例を挙げてみましょう。
f(フォーマット文字列)オプションは、時刻の後にフォーマット文字列を指示するために使用します。
このフォーマット文字列は、"Program: "という文字とプログラム名(およびプログラムに渡されたすべてのコマンドライン引数)を表示します。Cフォーマット指定子は、「計時するコマンド名とコマンドライン引数」を意味します。\⑭は、出力を次の行に移動させます。
大文字と小文字を区別する書式指定子がいくつかあるので、自分でこの操作を行う場合は、正しく入力してください。
次に、"Total time: "という文字に続いて、このプログラムの総実行時間の値(%Eで表示)を表示することにします。
また新しい線を出すために、୧⃛(๑⃙⃘◡̈๑⃙⃘)そして、"User Mode(s) "という文字の後に、%Uで示されるユーザーモードに費やしたCPU時間の値を表示します。
また新しい線を出すために、୧⃛(๑⃙⃘◡̈๑⃙⃘)今回はカーネルの時間値について準備中です。Kernel Mode(s)」という文字の後に、カーネルモードで費やしたCPU時間のフォーマット指定子、すなわち「%s」を表示するのです。
最後に、このデータ値に対して改行とヘッダーを提供するために、" \nCPU:" という文字を出力します。Pフォーマット記述子は、タイムアウトしたプロセスが使用したCPU時間の平均的なパーセンテージを示す。
フォーマット文字列全体は、逆カンマで囲む。値の配置にこだわるなら、出力にタブを配置するためのいくつかの \t 文字を含めることができます。
\time -f "Program: %C\nTotal time: %E\nUser Mode (s) %U\nKernel Mode (s) %S\nCPU: %P" ./loop1実施したテストのタイミングを記録しておくために、いつでも出力をファイルに送信することができます。そのためには、-o(出力)オプションを使用します。プログラムの出力は、そのままターミナルウィンドウに表示されます。時からの出力だけがファイルにリダイレクトされます。
次のようにテストを再実行し、その出力をtest_results.txtファイルに保存することができます。
\time -o test_results.txt -f "Program: %C\nTotal time: %E\nUser Mode (s) %U\nKernel Mode (s) %S\nCPU: %P" ./loop1 cat test_results.txtloop1プログラムの出力はターミナルウィンドウに表示され、timeからの結果はtest_results.txtファイルに表示されます。
次の結果を同じファイルに取り込むには、次のように -a (append) オプションを使用する必要があります。
\time -o test_results.txt -a -f "Program: %C\nTotal time: %E\nUser Mode (s) %U\nKernel Mode (s) %S\nCPU: %P" ./loop2 cat test_results.txtこれで、形式文字列の出力にプログラム名を含めるために%C形式指定子を使用する理由が明らかになったはずです。
プログラマーや開発者が自分のコードを微調整するための最も一般的な方法であろうtimeコマンドは、プログラムを起動するたびに舞台裏で何が起こっているのかを詳しく知りたい人にとっても有用である。