HTMLパーサ - ボトルネック解消

昨日のブログにあるHTMLパーサを試していたら、(組み込みのものではない)read-xxx系関数がボトルネックとなっていることが分かったので、修正する。

一番、大きな影響を与えていたのは、この関数(若干簡略化)。

;; (funcall fn c)がtになるまで、文字列を読み込む
(defun read-until (fn stream)
  (coerce
   (loop FOR     c = (read-char stream)
         UNTIL   (funcall fn c)
         COLLECT c)
   'string))

僕は文字列を一旦listに蓄積しておいて、最後にcoerceするということをよくやっているのだが、どうやらそれがいけないらしい。

この部分を可変配列を使うようにしたら、2倍以上処理速度が上がった。

;; 同じ関数の配列を使う版
(defvar *buffer* (make-array 64 :element-type 'character :adjustable t :fill-pointer 0))

(defun read-until (fn stream)
  (setf (fill-pointer *buffer*) 0)
  (do ((c #1=(read-char stream) #1#))
      ((funcall fn c))
    (vector-push-extend c *buffer*))
  (subseq *buffer* 0 (length *buffer*)))

今まで、adjustable-array(及びfill-pointer)は、ほとんど使ったことがなかったのだが、これくらい速度差が出るなら、これからは使うようにした方が良さそうだ。


その他にも若干宣言を追加したり、他のread-xxx系の関数定義を修正したりしたら、結局もともとのコードの3倍弱程度速くなった。

というか、今は速度よりも、それ以外の部分を作り込む(パースに失敗するケースを少なくするとか)方が先なような気がするが...。