Haskellで外部実行ファイルの実行時間を調べる。

タイトルの通り、ちょっと実行ファイルの実行速度を調べたいってことがあって、勉強がてらにHaskellで作ってみるか!


Haskellなんて、まともにコードなんて書いてなく、参考の本も手元に無い。でもgoogle先生を信じれば何だって出来るはず!

とりあえず外部ファイルの実行

どう書くorgにあった。
http://ja.doukaku.org/comment/8717/

import System.Process
wait :: String -> IO ExitCoe
wait = (>>= waitForProcess) . runCommand

noWait :: String -> IO (Maybe ExitCode)
noWait = (>>= getProcessExitCode) . runCommand

めっちゃポイントフリー。
きっとwait関数の部分をコピペしてごにょごにょしたらすぐ出来るだろう。
と、高を括ってたんだが、うおおおおおぃ、関数の定義の仕方わかんねぇ!落ち着け、俺!ググればなんとかなる。


おk、Systemモジュールをimportするだけだった。

--引数に入れたファイルを実行するぜ!(第一引数のみ)
module Main where
import System
import System.Process
main = do
    args <- getArgs
    wait $ head $ args

wait :: String -> IO ExitCode
wait = (>>= waitForProcess) . runCommand


他人のソースを見ながらてきとーに実装。こんなけ書くのにえらい苦労したな。
最近は言語の勉強をしてから、コーディングするというスタイルとってたから、こういう試行錯誤は懐かしいな。


えーと確か「$」が関数の()の代わりで、「.」が関数合成だっけ?
「>>=」がわからないのと、ExitCodeとかrunCommandとか後で調べる。


とりあえず、第一引数のみを取得して実行するやつ。
コンパイルして実行。
hello.exeというコンソールに「hello!」と出力するのみの実行ファイルのパスを渡してあげる。

>theResult.exe hello.exe
hello!


うむ、うまく呼び出せた。

実行時間の計測

よし、あとは時間を計るだけ!
ググったらCPUTime.getCPUTimeっていう関数がある!
参考サイトThe Haskell 98 Library Report: CPU Time

計算 getCPUTime は現在のプログラムが使っている CPU 時間をピ コ秒単位であらわした数字を替えす。この結果の精度は cpuTimePrecision であたえられている。これは、実装上、最も短 い計測可能な CPU 時間間隔で、これはピコ秒単位の整数であたえられている。

これ使えば一発じゃねーか!


…詰まった。IOモナド理解しないと出来ないだろ。
だって、getCPUTimeの戻り値が、IO Integerなんだぜ?
なんだよIOって!


まぁ、時間を計るというどう見ても副作用入ってるから、IO知らないと出来ないのは大方予想できたんだけどね。
私は「ふつうのHaskell」を読んで勉強したんだが、残念ながらモナドの章はてきとーに読み流してて理解して無いんだぜ?
まぁ、google先生に聞けば答えてくれるだろう。


…何とかなった。いやぁ大変。
まず一個目のミス。getCPUTimeじゃなくて、getClockTimeを使うべきだった。
参考サイトhttp://www.nabble.com/getCPUTime----td1589341.html

getCPUTime gets the amount of CPU time used by the program so far, in
picoseconds (though with limited resolution). Use getClockTime from
System.Time to get the current clock time.

getClockTimeを使えばいいことは分かったぜ!

IO aからaを取り出す方法もなんてことは無い「<-」を使えばいいだけ。
すでに引数取ってくるときに使ってた。


あとはgetClockTimeの仕様をみれば大体分かる。
以下実装。

--引数に入れた外部ファイルの実行速度を測るぜ!
module Main where
import System
import System.Process
import Time
main = do
    args <- getArgs
    start <- getClockTime
    wait $ head args
    end <- getClockTime
    putStrLn $ show $ diffClockTimes end start
	
wait :: String -> IO ExitCode
wait = (>>= waitForProcess) . runCommand


んで、同じようにコンパイルして実行。

>theResult.exe hello.exe
hello!
TimeDiff {tdYear = 0, tdMonth = 0, tdDay = 0, tdHour = 0, tdMin = 0, tdSec = 0, tdPicosec = 31250000000}

やっほおおおおおい。やっと時間測れたよ!俺感激。
ピコセカンドっていう単位が分からん。
ググった。1ピコ秒 = 1/1,000,000,000,000秒。
つまり31250000000picosec≒0.03s。
その数値にwait関数自体の実行時間を弾いた時間がhello.exeにかかった時間になる。
測定した値が十分大きければ、wait自体の実行時間は無視しても問題ないだろう。


今の所TimeDiffをそのままshowしてるから形が汚いんだけど、後は拡張すればいいな。

今後の拡張

とりあえず疲れたから今日はここまで。
もう5時間ぐらいやってるぞ。
今後の拡張としては、

一つ目は、ライブラリ見れば解決できると思う。
二つ目に関しては、代数的データ型をよく知らないと出来ないのでもっと調べる。
三っ目に関しても同様。

あまり勉強せずに新しいプログラミング言語に挑戦してみて思ったこと

あーうん。たまにはこういう経験も面白い。
ただやっぱり効率を考えるなら、まずは本を読んで言語仕様をしっかり学ぶことが重要!
やはり本というのは情報がまとまっているから、新しいプログラミング言語の導入としては最適。
その後はググりながらやればいい。
ある程度基礎を学べばググった方が早い。


APIとか調べるにはやっぱりオンラインドキュメントが最強!
で、今回参考にさせていただいたリファレンスは、
http://www.zvon.org/other/haskell/Outputglobal/index.html
のサイト。
ほしい名前さえ分かれば後はドキュメントを見れば、芋づる式に知りたいものが出てくる。
時間の取得とか、時刻の引き算とかはここを見て書いた。


早く「ふつうのHaskell」届け!