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. ]
このプログラムではガベージコレクタが時間を多く消費していることがわかる。 (驚くなかれ、このプログラムはエレガントだが最適化はしていない。 配列やループを使った方がはるかに速く動作するだろう)