&& は & より遅い
Compiler に面倒見てもらえないのかと言ったやつ。とりあえずの解釈として、&& は & や % などとの組合せの代数的性質がよくないので compiler が reduce しにくい/しようとしないのかと考えてみた。つまり、&& などの条件演算子はあくまで独立条件を加味した bool 代数演算で reduce して、個々の条件にある bit 演算子は剰余代数で変換をかけ、両方を合わせた reduce は (殆ど) しない。という風に gcc が code されてるのかも。
ちょっと実験したところ、
(x & 4) && (x & 8) --> (x & 4) && (x & 8) (x & 4) & (x & 8) --> 0 (x & 4) | (x & 8) && (x & 2) --> (x & 12) && (x & 2)
という風に処理された。これ以上は code 見た方が早いかな。
■
この expander なかなか優秀ではないか。Transcribe した後の symbol lookup はちょっと遅いけど、expansion そのものは Oleg Kiselyov の stress test を基準に言えば高速な部類に入るらしい。試してみた範囲では
- Guile, Gauche, Bigloo, STklos → error (bigloo はネタ元の site に載ってるのより version が新しい)
- Gambit → 正しい使い方がわからず(泣) Manual を見てやってみたけど、何か当たり障りの無い単純 macro でも文句言って評価してくれない。
- Scheme48 → 5 分 (!)
- SigScheme (1 object = 4 word, assert 有効) → 4.9 〜 6.2 秒
- SigScheme (1 object = 4 word, NDEBUG) → 0.8 〜 1.1 秒
- Petite Chez → 0.7 〜 1.0 秒
時間は real time だけど、どの場合も sys の寄与は誤差程度。この stress test は match pattern が小さくて template が大きく、ellipsis が使われていません。よって時間の大半は多分 transcription に費されてます (instrument してないけど)。しかし、走らせる毎に遅くなっていくのは何故?
まあそれはいいとして、この結果を見て Shiro さんに忠告。Scheme48 が 400 倍近く時間かかってるのは多分 expander が scheme で書かれてるからです。Gauche の expander を Scheme で書き直すという事ですが、かなり cost conscious な coding を覚悟しないと、高くつくかもしれませんよ。あるいはカリカリに tune した bytecode / C code を吐く compiler を用意するとか。
それにしても、やるなあ Chez。Compact したらもっと引き離されるし。
;; とは言っても何箇所か code size と robustness を優先して書いた部分もあるので、
;; speed に向けて tune したらもうちょっと詰められるのかも知れない。
■
↓Memory 使用量も調べたいなあ…。Shell の `time' builtin みたいに手軽にできるんだろうか。