Gnu Common Lisp(GCL)/Kyoto Common Lisp(KCL)の最適化について
Common Lispは、実用言語である。
なので、Common Lispの最適化がすごい例を、ちょっと書いてみたり。
GCLは、もともとのKCLを強化して、AKCL (Austin KCL)となり、それを維持強化しているもの。
(GCLのページの歴史にもそれは書いてある http://www.gnu.org/software/gcl/ )
下に示す最適化は、KCLの時代からまったく同じで、強力だった。
素朴に書くと、下のような階乗のプログラム…
(defun fa2 (x y)
(if(zerop x) y
(fa2 (1- x) (* y x))))
型をきちんと指定して、関数の返す型や、途中の変数が保持するデータが、
fixnum(小さな整数,実現によるが大体28bit〜30bitぐらい?)であると、
処理系に教える、と、下のようになる。
(defun fa3 (x y)
(declare (fixnum x y))
(if(zerop x) (the fixnum y)
(fa3 (the fixnum(1- x)) (the fixnum(* y x)))))
これをコンパイルする。
※実際には、
(disassemble 'fa3)
を実行する。
:
;; Note: Tail-recursive call of FA3 was replaced by iteration.
:
OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3
:
/* function definition for FA3 */
static void L1()
{
:
{long V1;
long V2;
:
TTL:;
if(!((V1)==0)){
goto T2;}
base[2]= CMPmake_fixnum(V2);
vs_top=(vs_base=base+2)+1;
return;
goto T2;
T2:;
{long V3;
V3= (long)(V1)-1;
V2= (long)(V2)*(V1);
V1= V3;}
goto TTL;
}
}
となる。
ということで、TTL - goto TTL;の単純なループとなり、
ループの中の演算は、longの
乗算, マイナス1, 0との比較
という整数演算のみになっている。
ちなみに、機械語は
30: 85 d2 test %edx,%edx
32: 74 06 je 3a <_L1+0x3a>
34: 0f af ca imul %edx,%ecx
37: 4a dec %edx
38: eb f6 jmp 30 <_L1+0x30>
となっている。
(いまどきのCコンパイラなら、V3という変数はなくなって当然)
ちなみに、GCL/KCLは、コンパイルのオプションは、デフォールトで
OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3
速度優先、実行時エラーチェック無し
となる。今回もそのまま。
今回使用したのは、
GCL (GNU Common Lisp) 2.6.6 CLtL1 Feb 10 2005 08:19:54
OSはWindowsXP
蛇足)
GCL/KCLは、Lispソースからコンパイルした、オブジェクトはC言語である。
なので、コンパイル結果を見るのが、非常に楽である。
また、GCL/KCLは、disassembleすると、こっそりとソースを再コンパイルしてそれを表示する。
GCLの基本的なアーキテクチャはKCLからほとんど変わっていない。
僕の自慢は、
このKCLの最適化を、1986年ごろ(「KCLが凄いぞ!」と世界中で有名になっていたころ)に、
KCLの作者の一人である萩谷さんに、KABAでじきじきに教えてもらったこと
である。
たけおかの Lispページ 目次
GnuEmacsと、Gnu Common Lispを一緒に使うと便利! こちら。
CommonLispで記述した、StarTrek(1976年頃流行した古いゲーム)
Prologの入門文書に飽きた人に
Prologで記述した、StarTrek(1976年頃流行した古いゲーム)
たけおか(竹岡尚三)のホームページ