1つのライブラリを最適化できないかなぁと思って、とりあえずインライン展開を試みてみた。
(大別するとグローバル最適化?でも、R6RSだとライブラリは1つのS式だから、そうでもないか)
とりあえず試したのは以下のこと。
exportされていない関数でかつ非再帰、相互参照なし、ライブラリ内で代入されていないものを抽出。
最適化時に上記の関数でかつそこそこサイズが小さいものをインライン展開。
たったこれだけ!
1個の巨大なS式と思えばまぁこんなものだろう。
で、効果があるのかちょっと検証。
以下のプログラム。
(library (test)
(export process set-parameter!)
(import (rnrs))
(define *paramter* #f)
(define (set-parameter! param)
(set! *paramter* param))
(define (get-paramter name default)
(or (and *paramter* (hashtable-ref *paramter* name default))
default))
(define (get-count)
(get-paramter 'count 1))
(define (get-process)
(get-paramter 'process (lambda args args)))
(define (get-args)
(get-paramter 'args #f))
(define (process)
(let ((n (get-count)))
(let loop ((i 0))
(unless (= i n)
(let ((proc (get-process))
(args (get-args)))
(apply proc (list args)))
(loop (+ i 1))))))
)
(import (test))
(define param (make-eq-hashtable))
(hashtable-set! param 'count 500000)
(hashtable-set! param 'args '(a b c d e f))
(hashtable-set! param 'process (lambda (a . b)
(fold cons a b)))
(set-parameter! param)
(time
(process)
)
ちょっと作為的過ぎるだろうか?イメージとしてはオブジェクト指向のプロパティな感じということで。
実際にインストラクションのダンプを取るとprocessの中にget-count、get-process、get-argsの姿はない。
次に速度を比較。
インライン展開あり:
$ ./build/sash.exe -L. test.scm
;; 0.632000 real 0.641000 user 0.000000 sys
インライン展開なし:
$ ./build/sash.exe -fno-library-inline -L. test.scm
;; 0.681000 real 0.672000 user 0.000000 sys
40~50ms速いけど、50万回まわしてこの結果か。一応入れておくか程度だな。うっかりすると遅くなりそうで怖いが・・・
何より悲しいのはこれはGambitのベンチマークではまったく意味がないところか。