再帰関数(ハノイの塔)にinline宣言をつけたら・・・

Forthでハノイの塔 - sileの日記ハノイの塔を解くcommon lispプログラムを載せたら「inline宣言を付けるともっと早くなりますよ」というコメントを頂いたので試してみた。

inline宣言付与結果

再帰関数をinline展開する際の深さはsb-ext:*inline-expansion-limit*変数で制御できるらしい。
今回はデフォルトの設定値をそのまま利用した。

;; sbcl-1.0.49(linux/64bit)
> sb-ext:*inline-expansion-limit*
--> 200 

;; inline宣言
(declaim (inline hanoi-impl))

;; 前回定義したhanoi-impl関数とhanoi関数を再コンパイル 
;; (*inline-expansion-limit*を越えました、という長いメッセージが出力される)
;; ... 略 ...

;; 実行
> (time (hanoi 'a 'b 'c 25))
Evaluation took:
  0.271 seconds of real time   ; 約 2.7 秒
  0.270000 seconds of total run time (0.270000 user, 0.000000 system)
  99.63% CPU
  541,031,826 processor cycles
  0 bytes consed
  
--> (:COUNT 33554431)

確かに速くなっている。


ついでにhanoi関数にもinline宣言をつけてみた。

;; inline宣言
(declaim (inline hanoi))

;; hanoi関数を再コンパイル 
;; ... 略 ...

;; 実行
> (time (hanoi 'a 'b 'c 25))   ; 実行時にinline展開される
;; (*inline-expansion-limit*を越えました、という長いメッセージが出力される)
Evaluation took:
  0.169 seconds of real time  ; 約 0.17 秒
  0.170000 seconds of total run time (0.170000 user, 0.000000 system)
  100.59% CPU
  337,858,167 processor cycles
  0 bytes consed
  
--> (:COUNT 33554431)

hanoi関数を実行時に展開するようにしたら、C++と同じくらいに高速になった。


ただsbclのtime関数はinline関数の展開時間は所要時間に含めていないようで、少し不公平な感があるので、別の方法でも図ってみた。

;; 実行時のinline展開時間も含めて図ってみる
> (get-internal-real-time) (hanoi 'a 'b 'c 25) (get-internal-real-time) (- * ***)
--> 304455 ; 開始時間
--> (:COUNT 33554431)
--> 304730 ; 終了時間
--> 275    ; 終了 - 開始 = 0.275 秒

展開時間も含めると、特別速いということはなさそう。
それでもinline宣言を全く使用しない場合に比べるとだいぶ良い。

各言語でのハノイの塔の処理時間をまとめておく。

C++
gcc
Forth
gforth
Forth
VFX-Forth
common lisp
sbcl(宣言無)
common lisp
sbcl(hanoi-implをinline宣言)
common lisp
sbcl(hanoiとhanoi-implをinline宣言)
0.168s3.359s0.190s0.403s0.271s0.169s (0.275s*1 )

教訓

再帰関数でもinline展開すると速くなることがある。
覚えておこう。

*1:実行時のinline展開処理も含めて計測した場合の数値