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

例外は投げなければそんなに重くなかったが,それでも継続の方が速い.