Gaucheで継続と例外の実行時間を比較してみた
継続と例外をそれぞれ大域脱出に使って,実行時間を比較してみた.3回計測して中央値をとった.
$ gosh -V Gauche scheme interpreter, version 0.8.14 [utf-8,pthreads]
まず,最も単純な例.
(use srfi-42) (time (do-ec (: i 0 100000) (call/cc (lambda (cont) (cont i)))))
(use srfi-42) (time (do-ec (: i 0 100000) (guard (e (else e)) (raise i))))
$ gosh callcc.scm ;(time (do-ec (: i 0 100000) (call/cc (lambda (cont) (cont i))))) ; real 0.191 ; user 0.190 ; sys 0.010 $ gosh raise.scm ;(time (do-ec (: i 0 100000) (guard (e (else e)) (raise i)))) ; real 0.807 ; user 0.630 ; sys 0.180
継続は重いイメージがあったけど,意外にも例外の方が断然重い.
次に,再帰の深い所から深い所への脱出.
(use srfi-42) (define (f x) (if (= x 0) (begin (time (do-ec (: i 0 100000) (call/cc (lambda (cont) (cont i))))) 0) (+ x (f (- x 1))))) (f 10000)
(use srfi-42) (define (f x) (if (= x 0) (begin (time (do-ec (: i 0 100000) (guard (e (else e)) (raise i)))) 0) (+ x (f (- x 1))))) (f 10000)
$ gosh callcc2.scm ;(time (do-ec (: i 0 100000) (call/cc (lambda (cont) (cont i))))) ; real 0.215 ; user 0.210 ; sys 0.010 $ gosh raise2.scm ;(time (do-ec (: i 0 100000) (guard (e (else e)) (raise i)))) ; real 0.800 ; user 0.630 ; sys 0.180
大きな変化はなし.
最後に,再帰の深い所から浅い所への脱出と,普通の再帰の場合.
(use srfi-42) (define (f x cont) (if (= x 0) (cont x) (+ x (f (- x 1) cont)))) (time (do-ec (: i 0 10000) (call/cc (lambda (cont) (f i cont)))))
(use srfi-42) (define (f x cont) (if (= x 0) (raise x) (+ x (f (- x 1) cont)))) (time (do-ec (: i 0 10000) (guard (e (else e)) (f i #f))))
(use srfi-42) (define (f x cont) (if (= x 0) x (+ x (f (- x 1) cont)))) (time (do-ec (: i 0 10000) (f i #f)))
$ gosh callcc3.scm ;(time (do-ec (: i 0 10000) (call/cc (lambda (cont) (f i cont))))) ; real 17.661 ; user 17.270 ; sys 0.260 $ gosh raise3.scm ;(time (do-ec (: i 0 10000) (guard (e (else e)) (f i #f)))) ; real 18.248 ; user 17.700 ; sys 0.330 $ gosh normal3.scm ;(time (do-ec (: i 0 10000) (f i #f))) ; real 18.747 ; user 18.420 ; sys 0.280
所要時間は,継続<例外<通常となった.(少なくとも,Gaucheでは)再帰の深さはあまり関係なさそう.あと,再帰が深い場合は,call/ccかraiseを使って大域脱出した方が速いということもわかった.
最後に,脱出しない場合.
(use srfi-42) (time (do-ec (: i 0 100000) (call/cc (lambda (cont) i))))
(use srfi-42) (time (do-ec (: i 0 100000) (guard (e (else e)) i)))
$ gosh callcc4.scm ;(time (do-ec (: i 0 100000) (call/cc (lambda (cont) i)))) ; real 0.157 ; user 0.150 ; sys 0.010 $ gosh raise4.scm ;(time (do-ec (: i 0 100000) (guard (e (else e)) i))) ; real 0.224 ; user 0.220 ; sys 0.010
例外は投げなければそんなに重くなかったが,それでも継続の方が速い.