Ruccola VM Crystal版の速度改善 (2022-08)

github.com

fastvm/rclvm.cr の高速化の自分用メモ。


  • オペコードを String のまま取り回す実装だった → これをシンボルや enum にするとどうなるか
  def execute : Int32 | Nil
    insn = @mem.main[@pc]

    opcode = insn[0]
    case opcode
    when "exit"     then return insn[1].as(Int32)
    when "cp"       then cp()       ; @pc += 1
    when "lea"      then lea()      ; @pc += 1
    when "add_ab"   then add_ab()   ; @pc += 1
    # ...

修正前はこうなっていて、文字列比較してるとこで遅くなってそう。


  • 使っている Crystal のバージョンが 1.0.0 のままだった → これを最新の 1.5.0 に上げるとどうなるか

計測

変更前
real    1m34.613s / user    1m44.672s / sys     0m2.679s
real    1m34.598s / user    1m44.409s / sys     0m2.869s
real    1m34.340s / user    1m44.597s / sys     0m2.555s

opcode をシンボルに変更しただけ
real    1m29.133s / user    1m36.528s / sys     0m2.152s
real    1m28.814s / user    1m36.282s / sys     0m2.106s
real    1m29.255s / user    1m36.347s / sys     0m2.400s

opcode を enum に変更しただけ
real    1m28.696s / user    1m35.704s / sys     0m2.447s
real    1m29.589s / user    1m36.684s / sys     0m2.367s
real    1m28.533s / user    1m35.828s / sys     0m2.244s

Crystal のバージョン変更だけ(1.0.0 => 1.5.0)
real    1m13.030s / user    1m21.938s / sys     0m2.984s
real    1m12.881s / user    1m21.927s / sys     0m2.772s
real    1m12.474s / user    1m21.444s / sys     0m2.840s

Crystal 1.5.0 + enum
real    1m8.694s / user    1m16.653s / sys     0m2.906s
real    1m8.094s / user    1m15.968s / sys     0m2.952s
real    1m8.741s / user    1m16.860s / sys     0m2.683s

メモ

  • v1.5.0 に上げただけでけっこう速くなる
    • 処理時間は 23% 減、速度は 1.30倍
  • バージョンアップの寄与の方が大きいが、シンボル化・enum化でも多少速くなる
  • 速度向上の優先度
    • そんなに高くない。あまりがんばらずに速くできるならやってしまおうか、くらいの温度感。
    • 可読性・保守性を損なわない方を優先

Crystal では String#to_sym は使えないが、 オペコードの種類は限られているので自前で変換してやれば String からシンボルへの変換は可能。

      opcode =
        case opcode_str
        when "exit"     then :exit
        when "cp"       then :cp
        when "lea"      then :lea
        when "add_ab"   then :add_ab
        # ... snip ...
        else
          raise # 不正なオペコード
        end

……という方法を思いついてないせいでシンボル化できないと思っていたか、 面倒そうなので後回しにしていたか(忘れたけどたぶん前者)。

シンボル (Symbol) - Crystal
https://ja.crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html

シンボルはコンパイル時に解釈されるもので、動的に生成することはできません。シンボルを生成する唯一の方法はシンボルリテラルを使うことです。


  • シンボルと enum どちらを使うか
    • せっかく Crystal を使っているので、この場合は enum でいいんじゃないかなと
    • 煩雑さはシンボルを使う場合でもあまり変わらない (String#to_sym が使えないので)

列挙型 (Enum) - Crystal
https://ja.crystal-lang.org/reference/syntax_and_semantics/enum.html


最終的に Crystal 1.5.0 + enum 化によって

  • 処理時間は 27.5% 減
  • 速度は 1.38倍

になった。


コミット: