&& は & より遅い

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 したらもうちょっと詰められるのかも知れない。