-
Notifications
You must be signed in to change notification settings - Fork 5
OpeLa Compiler Speed
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
フィボナッチ数列の計算にかかる時間を,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);
}
}
- CPU: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
- 論理 8 コア
- MEM: 16GiB
- OS: Ubuntu 18.04.5 LTS
結果を示します。平均を取ってない,適当な計測であることをご了承ください。
言語 | 実行時間 |
---|---|
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