#RubyKaigi 2025 で「Improvement of REXML and speed up using StringScanner 」というタイトルで発表しました

2025/04/16〜2025/04/18にかけて松山で開催されたRubyKaigi 2025に参加し、今回は RubyKaigi 本編に登壇するという実績を解除することができました🎉

rubykaigi.org

昨年の RubyKaigi 2024 のLT でお話した話

naitoh.hatenablog.com

の続きで、今回、発表した資料は下記になります。

slide.rabbit-shocker.org

REXML 3.2.6 (Ruby 3.3.0 添付のもの) から最新の REXML 3.4.1 の間で、StringScanner を使った実装に置き換える事で、XMLパース処理が最大6割速くなったので、 StringScanner を使うと何故パース処理が速くなるのかどのような点に気をつけてパース処理を書けば速くなるのか などを、REXML の実装を踏まえながら高速化のポイントを紹介させて頂きました。

高速化のポイント

  1. XMLのパース処理にRegexpクラスの代わりにStringScanner を使うと速い
    • 単純な性能比較で StringScanner の方が5割程度速い
  2. StringScanner を効果的に使うことで StringScanner (C拡張部分) の処理を最適化する
    • StringScanner のRegexpマッチの代わりにStringマッチを使うとより高速
      • 正規表現は便利だが処理が重いので、文字列でのマッチで代替可能な場合はStringマッチ(内部処理は memcmp()rb_memsearch() での文字列比較)を使う事で速く処理できる
    • Stringマッチならマッチ対象文字列が長くなっても、性能劣化を最小限に抑えられる
      • 正規表現の場合、マッチ対象文字列が長くなるとパフォーマンス低下に繋がりやすい
    • String Object の生成を避けると速い
      • マッチ結果を返すために Object 生成するのにコストがかかるので、マッチ結果を Integer で返すと Object 生成しないので速くなる
  3. Ruby 実装部分で遅い処理には定数やメモ化を使うことでボトルネックを解消する

という感じで、細かな改善の積み重ねで最大6割の高速化を実現した内容をベンチマークコードとその結果を用いて説明させて頂きました。

また、上記の高速化の取り組みを実施後、YJITを有効化すると、Ruby 実装部分が JIT コンパイルされるので、さらに高速化されます。

StringScanner の 新機能(#scan_until(String))追加による Stringマッチ新メソッド(#peek_byte)追加による String Object 生成回避 といった高速化手法が増えた事も、今回の高速化に繋がっているので、StringScanner を使ってパース処理を実装してみたいという方は、ぜひトライしてみてください。

最後に

今回、RubyKaigi 本編初登壇で、当日朝まで資料を修正してたりしていたので、やり切った感が半端ない感じでした。

自分の発表が終わった後に、質問に来てくれた方が 5人ぐらいおられました。 その中で、内藤の発表を受けて、「モチベーションが非常に上がったので、とりあえずその感動を伝えたい」とその事だけを伝えに来てくれた方もおられたので、RubyKaigi で「何かを持ち帰る」だけでなく「何かを持ち帰って頂ける」ようになれたんだなと思いました。

来年も登壇できるように頑張ります!