プロファイラって?
自分が書いたスクリプトが、実際どのように動いたか確認してみたくありませんか? 書いたスクリプトは、引数で渡されるデータによって動作が違ってきたりします。 このとき、もし一番よく使われる部分がわかれば、その部分の処理をうまく高速化できれば処理の効率がよくなります。 また、実行されていない部分がわかれば、その部分には bug が残っているかもしれません。 こういうときにプロファイラというものが役立ちます。
ここでは、とても簡単なプロファイラを用意しました。 基本的には、「ある」スクリプトに計測用の処理を埋め込み、そのスクリプトを実行します。 実行中に処理の結果をファイルに出力し、その結果をスクリプトのリストと合わせて表示するということをします。
ちなみに Ruby は、標準でプロファイラが使用可能です。
確認してみてください。
のように実行してみてください。 ここで扱っているものとは違う情報がとれますよ。$ ruby -rprofile ruby_script.rb
処理の流れは次のようになります。
計測したいスクリプトを ruby_script.rb としています。
mkprof.rb を実行して、計測用の仕組みを組み込んだ新しいスクリプトを作成します。
ここでは junk.rb という名前をつけています。
このスクリプトを実行することで、計測を行います。
計測結果は、prof_log.txt というファイルに出力されます。
計測結果をオリジナルのスクリプトに組み込むのが prprof.rb です。
結果は画面に表示されるので、適当に less などを使用してください。
$ mkprof.rb ruby_script.rb > junk.rb $ ruby junk.rb 引数 ここで 'prof_log.txt' というファイルが作成される $ prprof.rb ruby_script.rb | less
prprof.rb では、prof_log.txt という計測ファイル名を省略できます。
もし、ファイル名を別のものに置き換えた場合は、そのファイル名を指定してください。
ここでは、計測のための仕組みを組み込むとか、結果を組み込むとか書いていますが、直接スクリプトを修正するものではありません。 それぞれ標準出力に結果がでますので、リダイレクトしてファイルを生成して利用してください。 間違って、オリジナルのファイルにリダイレクトしないようにしてください。 オリジナルのファイルは大切にしましょう!
制限があります。
現在、スクリプトの行数は 1000 行までしか対応していません。
これは、mkprof.rb を修正することで対応可能です。
mkprof.rb したスクリプトを実行すると、カレントディレクトリに prof_log.txt というファイルをつくってしまいます。
同じファイル名を使っている場合には注意してください。
さてさて、実際に「使ってみたい」と思っていただくには、ここに実例を書くべきなのですが...
次のものは、mkprof.rb のプロファイル結果です。
処理対象は prprof.rb としました。
while gets
44 if prof_flag
9 print '$r__prof[', $., '] += 1;'
end
if /\bexit\b/
2 pr_exit
elsif init_flag and /^$/
1 print '$r__prof = []; $r__prof[0, ', MAX_line, '] = [0] * ', MAX_line, "\n"
init_flag = FALSE
elsif /<<(?:\"|\')?(\w+)(?:\"|\')?/
一部だけ抜き出したのですが、while が 44 回実行され、if が真のときの回数、条件での各処理の回数などがわかります。
いかがですか?
完全に対応しているわけではありません。 このため抜けがでてくると思います。 自分のスクリプトの書き方に合わせて適当に修正してお使いください。 または、アイディアだけ使って自分で新規に作られるのもいいと思いますよ。
スクリプトをセーブして、使用する場合には「テキスト」でセーブしてください。 これで使えるようになるはずです。
#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/sdk/mkprof.rb
# Created: September 13,1998 Sunday 15:00:44
# Author: tetsu(WATANABE Tetsuya)
# $Id: mkprof.rb,v 1.9 2001/08/14 18:27:55 tetsu Exp $
# usage: mkprof.rb ruby_script.rb > junk.rb
# ruby junk.rb
# prprof.rb ruby_script.rb | less
def pr_exit
print <<'E'
END {
f = File.open('prof_log.txt', 'w')
$r__prof.each_index do |i|
f.print i, ' ', $r__prof[i], "\n" if $r__prof[i] != 0
end
f.close
}
E
end
MAX_line = 1000
line_no = 0
init_flag = true
prof_flag = false
here_doc_flag = false
exitp_flag = false
while gets
if prof_flag
print '$r__prof[', $., '] += 1;'
end
if ~/^__END__/
pr_exit
exitp_flag = true
end
print
$_.sub!(/\s*\#.*$/, '')
if init_flag and ~/^$/
print '$r__prof = []; $r__prof[0, ', MAX_line, '] = [0] * ', MAX_line, "\n"
init_flag = false
elsif ~/<<(?:\"|\')?(\w+)(?:\"|\')?/
here_doc_mark = $1
here_doc_flag = true
elsif here_doc_flag and ~/^#{here_doc_mark}/
here_doc_flag = false
end
comma_flag = if ~/,$/ then true else false end
prof_flag =
if ~/\b(def|do|if|then|elsif|else|while|until)\b/ and
not here_doc_flag and
not comma_flag
true
else
false
end
end
if $. > MAX_line
STDERR.print "over line (#{MAX_line})\n"
end
pr_exit if not exitp_flag
#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/sdk/prprof.rb
# Created: September 13,1998 Sunday 15:36:22
# Author: tetsu(WATANABE Tetsuya)
# $Id: prprof.rb,v 1.4 1998/09/14 05:08:05 tetsu Exp $
# mkprof.rb ruby_script.rb > junk.rb
# ruby junk.rb
# usage: prprof.rb ruby_script.rb [prof_log.txt] | less
def usage
STDERR.print "usage: #{$0} src_file [log_file]\n"
exit 1
end
src_file = ARGV.shift
log_file = ARGV.shift || 'prof_log.txt'
if File.exist? log_file
log_f = File.open(log_file)
else
usage
end
if File.exist? src_file
src_f = File.open(src_file)
else
usage
end
c = {}
while log_f.gets
line_no, count = $_.split
c[line_no.to_i] = count
end
while src_f.gets
if c.key?($.)
printf('%7d ', c[$.])
else
print ' '
end
print
end
exit
「とりあえず」ということで解説します。
Ruby のスクリプトの中でどのポイントの実行回数を計測するか? は、「実行ブロック」となるところにカウンタを設定します。
ここでの「実行ブロック」は、if のような条件により実行される文の固まり end までや、do から end までの文の固まりのことです。
この実行ブロックが何度実行されたか? を計測します(同じことを繰り返し書いていますが)。
実際のところは、mkprof.rb を実行して、スクリプトを作成してみてください。
ポイントポイントに、オリジナルのソースコードの行番号をインデックスとした配列を埋め込んで計測しています。
たぶん、実用的に使うにはまだ必要なところが計測できなかったり、うまく初期化できなかったり、結果がちゃんとでなかったりするかもしれません。
ですが、mkprof.rb で作成されたスクリプトをもとに計測部分を追加したりして、結果をうまく活用してください。
ほしい部分の実行回数は「手」で追加するのもありですから(あらら)。
私のツールは、ややこしい仕事の 80% くらいを自動化して、残りの 20% をうまく... 以下省略。
完全自動化というのももちろんありなんですが、どんどん使っていくうちによくなっていくものです。
まあ、プロファイラというのは、使って自分のスクリプトの動作を理解していく手助けをするものなので、そちらの話題を書いた方がいいですよね。
いま、土台ができましたから、気がつくたびに使っていくということかな。
最後のしめも「とりあえず」ということで。
プロファイラとして実行時にどのような処理がより多く実行されるか? という確認が可能です。 そのほかに、プログラムの動作を確認するのに使えます。 デバックなどで「処理の流れを追いかける」のにもとても有効です。 もし、データによってよくわからない現象が起きているような場合、この行レベルプロファイラを使ってみるのも方法と思います。
プロファイラについては、次の本がとても参考になります。
プログラマのうちあけ話ここでとり上げられている、素数を出力するプログラムを Ruby で書いてみて、ここで紹介している
続・プログラム設計の着想
J.L.ベントリー 著
野下浩平・古郡廷治 共訳
近代科学社
ISBN4-7649-0177-3
More Programming Pearls
mkprof.rb/prprof.rb を使うと、同じような出力が得られます。
作成したスクリプトがどのように動作するか?
実際に確認できるので、プロファイラはなかなかいい道具です。
上記の本が、次のような形で新しくなりました。 私は扱っている内容とかとっても好きです。
珠玉のプログラミングまた、次の本もいろいろ勉強になることが多いです。
本質を見抜いたアルゴリズムとデータ構造
Programming Pearls Second Edition
ジョン ベントリー 著
小林健一郎 訳
ピアソン エデュケーション
ISBN4-89471-236-9
プログラミング作法これらの本を題材に、Ruby を試してみてはいかがでしょうか?
The Practice of Programming
Brian W.Kernighan
Rob Pike 著
福崎俊博 訳
アスキー
ISBN4-7561-3649-4
さて、ここを読んでいるみなさん。 「プロファイルを使おう」ということで、今月をプロファイラ月間として、あれこれプロファイラを試してみませんか? 上記参考書でも勧めていますが、いいことだと思っています。
この他、「プログラミング言語 AWK」(トッパン)でも同様にプロファイラのことがとり上げられているようです。 私はいま手元にこの本がないので、詳しくはわからないのですが(会社に置いたまま...)。 この本は、AWK の解説書であると同時に、コンピュータサイエンスの手頃な入門書でもあります。 AWK という手軽な言語をいろいろ応用に利用しているので、スクリプト言語に興味がある方はぜひ一度手にとってみてはいかがでしょう? この本でとり上げられている題材は、Ruby で書き直すのにそれほど苦労は必要ないはずです。 著作権の問題があってできませんが、こういういい本が少し古くなってきたりしているので、Ruby で書き直すとかができるといいんですけど。 「UNIX プログラミング環境」とか「ソフトウェア作法」とか... みんな作者が同じだなぁ。
ruby 1.7.1 2001-08-06 対応です。
「__END__」があった場合に終了処理をその前に出力するように対応しました。
終了時処理に END {} を使うようにしました。
スクリプト中に exit が記述されていなくても OK です。