Gauche:クラスとレコード型
クラスとレコード型
- Gauche で、オブジェクト指向的なプログラミングをしようとした場合、
クラスまたはレコード型が使用できます。
- ユーザリファレンスによると、クラスの方が柔軟で高機能だが、
レコード型は制約がある分最適化の余地が大きいとのことです。
- また、Gauche のレコード型には pseudo record type というものもあり、
これは、普通のリストやベクタを、レコード型のように見せるという仕組みのようです。
- それで、簡単な例を作って、実行時間を測定してみました。
クラス、レコード型、pseudo record type のそれぞれについて、
設定用のメソッドと手続きを用意して実行しました。
また、万能アクセサ(~)を使用した場合についても追加しました。
(use gauche.time) (use gauche.record) ;; ***** class ***** (define-class <point-A> () ((x :init-value 0) (y :init-value 0) (sum :init-value 0) )) (define-method set-data-A-1 ((p <point-A>) (x <real>) (y <real>)) (slot-set! p 'x x) (slot-set! p 'y y) (slot-set! p 'sum (+ (slot-ref p 'x) (slot-ref p 'y)))) (define (set-data-A-2 p x y) (slot-set! p 'x x) (slot-set! p 'y y) (slot-set! p 'sum (+ (slot-ref p 'x) (slot-ref p 'y)))) (define-method set-data-A-3 ((p <point-A>) (x <real>) (y <real>)) (set! (~ p 'x) x) (set! (~ p 'y) y) (set! (~ p 'sum) (+ (~ p 'x) (~ p 'y)))) (define (set-data-A-4 p x y) (set! (~ p 'x) x) (set! (~ p 'y) y) (set! (~ p 'sum) (+ (~ p 'x) (~ p 'y)))) ;; ***** record type ***** (define-record-type point-B #t #t (x) (y) (sum)) (define-method set-data-B-1 ((p point-B) (x <real>) (y <real>)) (point-B-x-set! p x) (point-B-y-set! p y) (point-B-sum-set! p (+ (point-B-x p) (point-B-y p)))) (define (set-data-B-2 p x y) (point-B-x-set! p x) (point-B-y-set! p y) (point-B-sum-set! p (+ (point-B-x p) (point-B-y p)))) (define-method set-data-B-3 ((p point-B) (x <real>) (y <real>)) (set! (~ p 'x) x) (set! (~ p 'y) y) (set! (~ p 'sum) (+ (~ p 'x) (~ p 'y)))) (define (set-data-B-4 p x y) (set! (~ p 'x) x) (set! (~ p 'y) y) (set! (~ p 'sum) (+ (~ p 'x) (~ p 'y)))) ;; ***** pseudo record type ***** (define-record-type (point-C (pseudo-rtd <vector>)) #t #t (x) (y) (sum)) (define-method set-data-C-1 ((p <vector>) (x <real>) (y <real>)) (point-C-x-set! p x) (point-C-y-set! p y) (point-C-sum-set! p (+ (point-C-x p) (point-C-y p)))) (define (set-data-C-2 p x y) (point-C-x-set! p x) (point-C-y-set! p y) (point-C-sum-set! p (+ (point-C-x p) (point-C-y p)))) ;; ***** test ***** (define n 1000000) (define p-A (make <point-A>)) (define p-B (make-point-B 0 0 0)) (define p-C (make-point-C 0 0 0)) (print "cls-method : " (time-this n (lambda () (set-data-A-1 p-A 1 2)))) (print "rcd-method : " (time-this n (lambda () (set-data-B-1 p-B 1 2)))) (print "psd-method : " (time-this n (lambda () (set-data-C-1 p-C 1 2)))) (print) (print "cls-proc : " (time-this n (lambda () (set-data-A-2 p-A 1 2)))) (print "rcd-proc : " (time-this n (lambda () (set-data-B-2 p-B 1 2)))) (print "psd-proc : " (time-this n (lambda () (set-data-C-2 p-C 1 2)))) (print) (print "cls-method~: " (time-this n (lambda () (set-data-A-3 p-A 1 2)))) (print "rcd-method~: " (time-this n (lambda () (set-data-B-3 p-B 1 2)))) (print) (print "cls-proc~ : " (time-this n (lambda () (set-data-A-4 p-A 1 2)))) (print "rcd-proc~ : " (time-this n (lambda () (set-data-B-4 p-B 1 2)))) (print) (print "cls-make : " (time-this n (lambda () (set! p-A (make <point-A>))))) (print "rcd-make : " (time-this n (lambda () (set! p-B (make-point-B 0 0 0))))) (print "psd-make : " (time-this n (lambda () (set! p-C (make-point-C 0 0 0)))))
- Gauche 0.9.6_pre7 での結果は以下となりました。(OS: Windows 8.1 (64bit))
cls-method : #<time-result 1000000 times/ 0.438 real/ 0.438 user/ 0.000 sys> rcd-method : #<time-result 1000000 times/ 0.656 real/ 0.655 user/ 0.000 sys> psd-method : #<time-result 1000000 times/ 0.344 real/ 0.344 user/ 0.000 sys> cls-proc : #<time-result 1000000 times/ 0.375 real/ 0.375 user/ 0.000 sys> rcd-proc : #<time-result 1000000 times/ 0.641 real/ 0.640 user/ 0.000 sys> psd-proc : #<time-result 1000000 times/ 0.297 real/ 0.297 user/ 0.000 sys> cls-method~: #<time-result 1000000 times/ 1.813 real/ 2.000 user/ 0.047 sys> rcd-method~: #<time-result 1000000 times/ 1.797 real/ 1.827 user/ 0.062 sys> cls-proc~ : #<time-result 1000000 times/ 1.750 real/ 1.764 user/ 0.078 sys> rcd-proc~ : #<time-result 1000000 times/ 1.734 real/ 1.844 user/ 0.031 sys> cls-make : #<time-result 1000000 times/ 1.266 real/ 1.500 user/ 0.297 sys> rcd-make : #<time-result 1000000 times/ 0.234 real/ 0.282 user/ 0.047 sys> psd-make : #<time-result 1000000 times/ 0.187 real/ 0.235 user/ 0.047 sys>
- また、Gauche 0.9.5 での結果は以下となりました。(OS: Windows 8.1 (64bit))
cls-method : #<time-result 1000000 times/ 0.984 real/ 1.282 user/ 0.203 sys> rcd-method : #<time-result 1000000 times/ 1.281 real/ 1.547 user/ 0.141 sys> psd-method : #<time-result 1000000 times/ 0.939 real/ 0.969 user/ 0.203 sys> cls-proc : #<time-result 1000000 times/ 0.375 real/ 0.374 user/ 0.000 sys> rcd-proc : #<time-result 1000000 times/ 0.797 real/ 0.750 user/ 0.047 sys> psd-proc : #<time-result 1000000 times/ 0.484 real/ 0.485 user/ 0.000 sys> cls-method~: #<time-result 1000000 times/ 4.328 real/ 4.858 user/ 0.906 sys> rcd-method~: #<time-result 1000000 times/ 4.313 real/ 4.953 user/ 0.891 sys> cls-proc~ : #<time-result 1000000 times/ 3.859 real/ 4.298 user/ 0.687 sys> rcd-proc~ : #<time-result 1000000 times/ 3.813 real/ 4.172 user/ 0.828 sys> cls-make : #<time-result 1000000 times/ 1.797 real/ 2.124 user/ 0.438 sys> rcd-make : #<time-result 1000000 times/ 0.250 real/ 0.219 user/ 0.078 sys> psd-make : #<time-result 1000000 times/ 0.203 real/ 0.250 user/ 0.047 sys>
- 結果として、レコード型が少し遅いということになりました(?)
また、メソッドと手続きでは、手続きの方が少し速いようでした (0.9.6_pre7 は差が小さい)。
また、万能アクセサ(~)を使うと遅くなるようでした。
また、0.9.6_pre7 では、万能アクセサ(~)が 0.9.5 の倍くらい速くなっているようでした。
- ただ、どれも百万回実行しているので、これらの差が意味を持つかどうかは、
プログラムの内容や目的次第ということになりそうです。
普通に使う分には、万能アクセサ(~)が使いやすいように思います。
また、メソッドの引数の型チェックも、デバッグ中はありがたいです。
(ただし pseudo record type は、型が <vector> 等になるので要注意です)
hamayama(2018/06/05 13:27:34 UTC)(2018/06/05 14:25:10 UTC)
(2018/06/05 15:05:45 UTC)(2018/06/06 12:05:13 UTC)
(2018/06/06 23:01:58 UTC)(2018/06/08 02:38:21 UTC)
(2018/06/17 13:35:30 UTC)(2018/06/20 13:47:31 UTC)
- Shiro(2018/06/05 21:57:13 UTC): 最近入れたメソッド呼び出しの最適化が効いてるようで何よりです ( http://blog.practical-scheme.net/gauche/20170422-next-method http://blog.practical-scheme.net/gauche/20170425-skipping-sort-methods )。 recordのアクセサの最適化はまだ入れていません。インライン化をもっと賢くすれば速くなると期待してます。
hamayama(2018/06/06 12:05:13 UTC): テストの内容を少し見直しました(random-real と atan を削除しました)。
結果の傾向は、ほぼ同じです。
また、make のテストを追加してみました。
こちらは、クラスが一番時間がかかるようでした。