2019年3月12日火曜日

AGC 026 C - String Coloring

AGC 026 C - String Coloring

最初、簡単のためにハッシュのキーに(文字の)リストを使ったらまったく間に合わなかった。経験上、リストは一般に言われるほど遅くないので(計算量的に不適切でなければ)頑張って避ける必要はないというスタンスでやっているのだが、ハッシュ値を求める場合は確かに差が出そうな気がする……

あるいは1回ごとにリストを作り直すのも問題なのかなー。ツリーを作る感じでコンシングを減らしていくとどうなるんだろう。→やってみたが多少ましになるだけで焼け石に水だった。ここまで遅いとどこかでオーダーが増えている可能性もある。(→増えていたらしい

https://github.com/sbcl/sbcl/blob/894477d6a8ec1c6ad9d43069d819d9fb895bf664/src/code/target-sxhash.lisp
ハッシュテーブル周りのコードを読んでみると、SBCLのsxhashsb-int:psxhashは細かく最適化する感じではなさそうだった。例えばbase-string(simple-array (unsigned-byte 8) (*))をワードごとに処理していくみたいなことはやってないように見える。あとでsb-ext:define-hash-table-testを使って高速化してみるかも。→やってみたらだいぶ速くなった。実践で役立つかはわからないが、いちおうこういう手段があるということで。以下は長さ40(=5ワード)のsimple-base-stringに特化したハッシュ関数を定義する例。

(declaim (inline simple-base-string40=))
(defun simple-base-string40= (s1 s2)
  (declare ((simple-base-string 40) s1 s2))
  (string= s1 s2))

(declaim (inline sxhash-sbs40))
(defun sxhash-sbs40 (string)
  "Based on SB-IMPL::%SXHASH-SUBSTRING"
  (declare ((simple-base-string 40) string))
  (macrolet ((set-result (form)
               `(setf result (ldb (byte 64 0) ,form))))
    (let ((result 0))
      (declare ((unsigned-byte 64) result))
      (dotimes (i 5) ; Unrolling made little difference.
        (set-result (+ result (sb-kernel:%vector-raw-bits string i)))
        (set-result (+ result (ash result 10)))
        (set-result (logxor result (ash result -6))))
      (set-result (+ result (ash result 3)))
      (set-result (logxor result (ash result -11)))
      (set-result (logxor result (ash result 15)))
      (logand result most-positive-fixnum))))

(sb-ext:define-hash-table-test simple-base-string40= sxhash-sbs40)