読者です 読者をやめる 読者になる 読者になる

JSON パーサの性能評価

[追記] Sagittarius では 053a8ad0a38c で高速な JSON ライブラリが導入されました。[/追記]

Scheme 処理系 Sagittarius が持っている JSON パーサライブラリを以前にいじったことがある。 SagittariusJSON パーサライブラリは下請けに packrat ライブラリが使われているのだが、この場合の packrat は性能に寄与していないのではないかと疑っている。 packrat は途中経過をメモ化することに特徴があるわけだが、 JSON は LL(1) 文法であり、やたらに記憶する意味はないだろうという予想だ。 そこで、性能を評価してみることにした。

評価するにしても比較対象が必要なのでウェブ検索してみると、 Guile 用にまとめられたライブラリが見付かった。 愚直に再帰下降型として実装したもののようだ。

そしてそれを R6RS 用に修正するパッチが以下である。

https://gist.github.com/SaitoAtsushi/12126187f8b50413b4cd

ではいよいよ比較してみよう。

#!r6rs
(import (rnrs)
        (json)
        (json parser)
        (time))

(let ((json-string (call-with-input-file "test.json" get-string-all)))
  (time (json-read (open-string-input-port json-string)))
  (time (json->scm (open-string-input-port json-string))))

テストに用いたデータは仲間内でやりとりしたチャットのログ五千件分 (約 1 メガバイト) である。 その性質上、データを公開するわけにはいかないので、ベンチマークをとりたい場合は適当なデータを各自で用意してもらいたい。

結果は…

;;  (json-read (open-string-input-port json-string))
;;  33.420065 real    33.072212 user    0.343202 sys

;;  (json->scm (open-string-input-port json-string))
;;  1.107601 real    0.967206 user    0.140401 sys

およそ 30 倍の差となった。

残念ながら比較に用いた JSON パーサライブラリは GPL なので MIT ライセンスの Sagittarius に混ぜることは出来ないが、これほどの差があるのならフルスクラッチJSON パーサを書いても充分に意義があると思う。 JSON はかなり簡単な書式ということもある。

ちなみに SagittariusJSON パーサライブラリは Chicken 用のものから移植されたものだ。 最初に書いた人は何を思って packrat を使ったのか…。

Document ID: 8ddac607af852837af9b66b1c3d8210c