Open7

Ribbon: もうちょっとreadを最適化する

okuokuokuoku

さすがに起動に2分掛かるのはちょっと。。まぁランタイムライブラリを全部S式から読んでバイトコードコンパイルするコストが掛かっているとは言え。。

real    1m56.997s
user    1m56.703s
sys     0m0.234s
okuokuokuoku

文字クラスの取得を表引きにする

https://github.com/okuoku/yuni/commit/44b7e4525832a89ec6ddcc6840b7ed09142331e5

従来は cond

https://github.com/okuoku/yuni/blob/ae159b0c4671d16387cedb23a8a6c27413efbdd1/lib/yuni/miniread/charclasses.sls#L33-L43

のように分けていた。ribbonではこれは長い ifthenelse になるので、O(N)になる。これを vector から拾うようにすれば O(1) になる。

real    1m48.052s
user    1m47.812s
sys     0m0.218s

期待した程早くは無いな。。

okuokuokuoku

ループの中でクロージャを作らないようにする

https://github.com/okuoku/yuni/commit/828b2a89aa119d285ce51c4add12f719a2c343f7

これはSchemeインタプリタ上で動くコードの最適化手法としてはよくある奴。 define の中で手続きを define するとクロージャが作られるので、実行のたびにメモリ確保が入ってしまう。

real    1m32.697s
user    1m32.577s
sys     0m0.108s

これはかなり効果あるな。。

https://www.npmjs.com/package/babel-plugin-closure-elimination

この手の最適化はJavaScript界隈でも見られる。

okuokuokuoku

文字ステートを開いたら逆に遅くなった

read中のストリームには以下のようなステートがある:

;; 初期状態 = 0
(define CHARLIT 1)
(define OBJ0 2)
(define OBJ0/SHARP 3)
(define OBJ0/DOT 4)
(define OBJ1 5)
(define OBJ1/SHARP 6)
(define OBJ2 7)
(define STRING0 8)
(define STRING1 9)
(define LINECOMMENT0 10)
(define LINECOMMENT1 11)
(define BLOCKCOMMENT0 12)
(define BLOCKCOMMENT1 13)

この case を1文字づつ実行することになっているので、これを開いて vector 参照一発にしてみた:

real    1m39.809s
user    1m39.531s
sys     0m0.280s

... これはダメだな。。case の各分岐を vector に入れるためにはクロージャにする必要があるのと、GCされるオブジェクトが大きくなるぶんで相殺されてしまっていると見られる。没。

okuokuokuoku

バッチサイズを増やしても逆に遅くなる

diff --git a/lib/yuni/miniread/reader.sls b/lib/yuni/miniread/reader.sls
index 4a8760f..7f76e84 100644
--- a/lib/yuni/miniread/reader.sls
+++ b/lib/yuni/miniread/reader.sls
@@ -290,7 +290,7 @@
          (values b bv c))))))

 (define (utf8-read bv) ;; => eof-object if comment-only
-  (define tkn (make-tkn 128))
+  (define tkn (make-tkn 1024))
   (define cb (%utf8-in bv))
   (define mr (make-miniread))
   (define (capture tkn idx)
@@ -299,7 +299,7 @@
           (type (tkn-type tkn idx)))
       (vector type start-index end-index)))
   (define (itr cur)
-    (let ((r (miniread-main mr tkn 0 128 cb)))
+    (let ((r (miniread-main mr tkn 0 1024 cb)))
      (cond
        ((eq? r #f)
         (%realize bv (reverse cur)))

クロージャの生成コストは相対的に減るけど、まぁGC負荷が増えるぶんで相殺できないんだろうね。。

real    1m57.722s
user    1m57.483s
sys     0m0.202s
okuokuokuoku

GC頻度が高すぎるのでは

VisualStudioのプロファイラで処理時間を確認してみた。

試しにGC頻度を1/100にすると、

real    0m56.238s
user    0m56.000s
sys     0m0.202s

うーむ。。もっと真面目にヒープを実装すべきか。。?