OCaml プログラムをコンパイルする

インストールとか環境構築については今度書くとして、今日はコンパイルについて。

バイトコードとネイティブコード

OCaml をインストールすると標準で 2 種類のコンパイラがついてくる。

バイトコード

ネイティブコード

  • コンパイルしたプログラムは機種(ハードウェア・OS)に依存する
  • コンパイルした環境と異なる環境では動かない場合がある
  • インタプリタは不要でハードウェアから直接実行できる
  • ネイティブコードの方が実行速度は速い

コンパイルしてみる

サンプルとして階乗を求める関数 fact と、

(* fact.ml *)

let rec fact n =
  if n = 0 then
    1
  else
    n * fact (n - 1)

それに引数 10 を渡して実行した結果を表示するプログラムを書く。

(* main.ml *)

open Fact
let () = print_int (fact 10); print_newline()

ocamlc でコンパイル

コンパイルして実行ファイルを作成するには以下のように入力する。-o オプションで実行ファイルの名前を指定する。

$ ocamlc -o fact10 fact.ml main.ml

コンパイルすると実行ファイル fact10fact.cmi, fact.cmo, main.cmi, main.cmo というファイルが生成され、fact10 を実行すると結果が表示される。

$ ./fact10
3628800

しかし、ocamlc に渡す順番を逆にするとエラーになる。

$ ocamlc -o fact10 main.ml fact.ml
File "main.ml", line 1, characters 5-9:
Error: Unbound module Fact

これは main.ml の中で fact.ml で定義されている関数 fact を参照している、つまり main.mlfact.ml に依存しているからである。

ocamlopt でコンパイル

ocamlc と同様に -o で出力するファイル名を指定する。

$ ocamlopt -o fact10 fact.ml main.ml

実行ファイルの他に生成されるファイルが ocamlc の場合とは一部異なった。

ocamlc では fact.ml から fact.cmi, fact.cmo が生成されたのに対して、fact.cmi は同じで、fact.cmo の代わりに fact.cmxfact.o が生成されていた。

この *.cmi, *.cmo, *.cmx, *.o とやらは何だろうか。こういう普段見慣れないファイル形式が一度に複数出てくると、ウッてなるんだよね。落ち着いてマニュアルを見てみる。

.cmi とは

.cmo とは

.o とは

.cmx とは

参照

ネイティブコードにコンパイルされたコンパイラ

上述の ocamlcocamloptバイトコードコンパイルされたコンパイラのようで、ネイティブコードでコンパイルされたものもあり、通常はこちらを使うらしい。

  • ocamlc.opt
  • ocamlopt.opt

ネイティブコード にコンパイルされた バイトコード および ネイティブコード コンパイラ。 ネイティブコード コンパイルが可能な環境では通常このコンパイラを使う。

OCaml コンパイラソースで make opt の後に make opt.opt を行うと作成される。 通常の ocamlc, ocamlopt は バイトコード で実行されるが、 *.opt コンパイラはネイティブコードに コンパイルされているため ocamlc, ocamlopt よりコンパイル速度が早い。 (バイトコードコンパイラがひどく遅いわけではないが。)

ocamlc, ocamlopt 以外の OCaml のツールにも、.opt の suffix がついた ネイティブコードバージョンが存在するのでそちらを使ったほうがよい。