OCaml プログラムに対して2種類のプロファイルのとりかたがある:
ocamlcp
と ocamlprof
でバイトコードのプロファイルを計測する。 例を示す:
$ ocamlcp -p a graphics.cma graphtest.ml -o graphtest $ ./graphtest $ ocamlprof graphtest.ml Random.self_init ();; Graphics.open_graph " 640x480";; let rec iterate r x_init i = (* 12820000 *) if i == 1 then (* 25640 *) x_init else (* 12794360 *) let x = iterate r x_init (i-1) in r *. x *. (1.0 -. x);; for x = 0 to 640 do (* 641 *) let r = 4.0 *. (float_of_int x) /. 640.0 in for i = 0 to 39 do (* 25640 *) let x_init = Random.float 1.0 in let x_final = iterate r x_init 500 in let y = int_of_float (x_final *. 480.) in Graphics.plot x y done done;;
(* nnn *)
というコメントが ocamlprof
が付け加えたもので、 そのコード部分が何回呼ばれたかを示している。
ネイティブコードのプロファイルについては、 使っている OS のプロファイラにネイティブ対応している。 Linux の場合 gprof
を使う。
ネイティブコードのプロファイルの実例を示すにあたり、 エラトステネスの篩(元コード) で最初の3000個の素数を求める。 このプログラムはチュートリアルの範囲外のテクニックである stream と camlp4 を使っている。
let rec filter p = parser [< 'n; s >] -> if p n then [< 'n; filter p s >] else [< filter p s >] let naturals = let rec gen n = [< 'n; gen (succ n) >] in gen 2 let primes = let rec sieve = parser [< 'n; s >] -> [< 'n; sieve (filter (fun m -> m mod n <> 0) s) >] in sieve naturals ;; for i = 1 to 3000 do ignore (Stream.next primes) done
コンパイラに対して gprof
のプロファイル情報を含めるよう、 コンパイル時に ocamlopt
に -p
オプションを積む。
$ ocamlopt -p -pp "camlp4o pa_extend.cmo" -I +camlp4 sieve.ml -o sieve
普通にプログラムを走らせると、 プロファイル情報が gmon.out
という gprof
用のファイルに出力される。
$ gprof ./sieve Flat profile: Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls s/call s/call name 10.86 0.57 0.57 2109 0.00 0.00 sweep_slice 9.71 1.08 0.51 1113 0.00 0.00 mark_slice 7.24 1.46 0.38 4569034 0.00 0.00 Sieve__code_begin 6.86 1.82 0.36 9171515 0.00 0.00 Stream__set_data_140 6.57 2.17 0.34 12741964 0.00 0.00 fl_merge_block 6.29 2.50 0.33 4575034 0.00 0.00 Stream__peek_154 5.81 2.80 0.30 12561656 0.00 0.00 alloc_shr 5.71 3.10 0.30 3222 0.00 0.00 oldify_mopup 4.57 3.34 0.24 12561656 0.00 0.00 allocate_block 4.57 3.58 0.24 9171515 0.00 0.00 modify 4.38 3.81 0.23 8387342 0.00 0.00 oldify_one 3.81 4.01 0.20 12561658 0.00 0.00 fl_allocate 3.81 4.21 0.20 4569034 0.00 0.00 Sieve__filter_56 3.62 4.40 0.19 6444 0.00 0.00 empty_minor_heap 3.24 4.57 0.17 3222 0.00 0.00 oldify_local_roots 2.29 4.69 0.12 4599482 0.00 0.00 Stream__slazy_221 2.10 4.80 0.11 4597215 0.00 0.00 darken 1.90 4.90 0.10 4596481 0.00 0.00 Stream__fun_345 1.52 4.98 0.08 4575034 0.00 0.00 Stream__icons_207 1.52 5.06 0.08 4575034 0.00 0.00 Stream__junk_165 1.14 5.12 0.06 1112 0.00 0.00 do_local_roots [ etc. ]
このプログラムではガベージコレクタが時間を多く消費していることがわかる。 (驚くなかれ、このプログラムはエレガントだが最適化はしていない。 配列やループを使った方がはるかに速く動作するだろう)