osdev-jpでは、OS開発に有用な情報を収集し公開しています

View My GitHub Profile

OpeLa メインページ

Ver.1 と Ver.2 で、生成された機械語の性能を測定しました。測定対象の OpeLa プログラムは次の通り。 ただし、計測当時、Ver.2 コンパイラはまだ関数定義に対応してなかったので、func main() を取り除いたプログラムをコンパイラへの入力とします。

func main() {
  s:=0;
  for i:=1; i<10000; i=i+1 {
    for j:=1; j<200000; j=j+1 {
      s=s+i*j;
    }
  }
  s;
}

計測に使用した OpeLa のコミットは 34e686cb7d4bc4c13887ade446fc7194b5faf609 です。

出力されたアセンブリ言語プログラムは Gist に載せました。Ver.1 の出力 Ver.2 の出力

Ver.2 はデバッグ情報を大量に出力しているため、ファイルの行数は多いです。コメントと、main の前にあるディレクティブを除いた行数(純粋なプログラム部分)は、Ver.1 が 123 行、Ver.2 が 54 行でした。

出力されたアセンブリ言語プログラムを gcc でアセンブル&リンクし、time コマンドで実行時間を計測しました。(real, user, sys のうちの real を集計)

バージョン 出力行数 実行ファイルサイズ 平均秒数 1 回目の秒数 2 回目の秒数 3 回目の秒数
Ver.1 123 8,456 14.613 14.590 14.569 14.680
Ver.2 54 8,392 4.937 4.947 4.901 4.962

Ver.2 の出力は Ver.1 に比べ、行数ベースで約 43%、実行時間ベースで約 34% となりました。実行速度が約 3 倍になったのは嬉しいですね。

興味深いことに実行ファイルサイズはあまり変化しませんでした。 リンクする前のオブジェクトファイル(main 関数を含むアセンブリ言語プログラムをコンパイルしただけしたもの)のサイズを見ても、Ver.1 は 1,208 バイト、Ver.2 は 1,064 バイトと、行数に比べると僅かな差です。 これは、push/pop が 1 バイト命令のため、Ver.1 が push/pop を大量に出力してもサイズにあまり影響しなかったからです。

フィボナッチ数

func main() int {
  return fib(40) != 102334155;
}

func fib(n int) int {
  if n <= 1 {
    return n;
  } else {
    return fib(n-1) + fib(n-2);
  }
}
バージョン 出力行数 実行ファイルサイズ 平均秒数
Ver.1 104 8328 1.740
Ver.2 93 8408 1.070
GCC -O0   8192 0.715
GCC -O3   8192 0.372
Python3     29.716(1 回だけ測定)

使用したコミット fa1d12f8318c77ff53a01b7a2556c62d23cef093

Ver.1 の性能測定

フィボナッチ数列の計算にかかる時間を,OpeLa,C,Python で比較してみました。 使用した OpeLa コンパイラのバージョンは 94d09ecd4221162689d67fd8986b00cde16b829b

計測対象のプログラム

計測に用いたソースコードは次の通りです。これは OpeLa のものですが,C,Python も同様のプログラムになっています。 使用した OpeLa バージョンでは標準出力を使えませんので,exit code で結果を出力します。C,Python も公平のために exit code で出力を行います。

$ cat fib.opl
func main() {
  fib(36) != 14930352;
}

func fib(n) {
  if n<=1 {
    n;
  } else {
    fib(n-1)+fib(n-2);
  }
}

計測環境

計測結果

結果を示します。平均を取ってない,適当な計測であることをご了承ください。

言語 実行時間
OpeLa 0.243 秒
C 0.117 秒
C -O3 0.084 秒
Python 3.643 秒

“C -O3” は C プログラムを最適化レベル 3 でコンパイルしたことを意味します。

OpeLa は最適化をしていない C(Clang)の 2 倍程度の時間がかかりましたが,愚直なスタックマシン実装ですから,まあ妥当なレベルでしょう。 Python の遅さが際立ちますね…

詳細結果

OpeLa

$ time sh -c "./fib_opela && echo ok"
ok

real	0m0.243s
user	0m0.243s
sys	0m0.000s

C

$ time sh -c "./fib_c && echo ok"
ok

real	0m0.117s
user	0m0.117s
sys	0m0.000s

Python

$ time sh -c "python3 fib.py && echo ok"
ok

real	0m3.643s
user	0m3.635s
sys	0m0.008s

コンパイルを含んだ時間計測

上記の結果は Python だけ構文解析などの時間が含まれてしまっています。 不公平なので,OpeLa と C に関してもコンパイル時間を含んだ時間を計測してみました。

OpeLa のコンパイル時間(0.046 秒)はそんなに遅くない,むしろ C のコンパイル時間(0.061 秒)より短いほどです。 OpeLa のコンパイラの実装は素直な感じですが,それがむしろ良いのかもしれません。

言語 実行時間(コンパイル時間を含む)
OpeLa 0.289 秒
C 0.178 秒
C -O3 0.143 秒
Python 3.643 秒

OpeLa

$ time sh -c "cat fib.opl | ./opelac > fib.s; nasm -f elf64 fib.s; cc -o fib_opela fib.o; ./fib_opela && echo ok"
ok

real	0m0.289s
user	0m0.285s
sys	0m0.007s

C

$ time sh -c "clang -o fib_c fib.c; ./fib_c && echo ok"
ok

real	0m0.178s
user	0m0.127s
sys	0m0.034s

C -O3

$ time sh -c "clang -O3 -o fib_c fib.c; ./fib_c && echo ok"
ok

real	0m0.143s
user	0m0.096s
sys	0m0.029s