メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックカンパニーのエンジニアブログです。読者に有用な情報発信ができるよう心がけたいので応援のほどよろしくお願いします。

Ruby 3.3(+YJIT)へのアップデートによるパフォーマンス変化の計測

こんにちは。サーバーサイドエンジニアの @atolix_です。

今回はメドピアで本番運用をしているアプリケーションの1つであるやくばと for Clinicにて、Ruby 3.2からRuby 3.3にアップデートを行った際のパフォーマンスの変化を計測しました。

Ruby 3.3ではYJITの大幅な改善が含まれているので、これによるアプリケーションへの影響を確認していきます。

www.ruby-lang.org

gihyo.jp

前提

本記事に記載されたデータは以下の条件で計測をしています。

  • Rails: 7.1.3.4
  • YJIT有効化時のオプションは特に付与していない状態(Dockerfileから環境変数を与えて有効化)
  • 3.2.4(+YJIT)から3.3.1(+YJIT)へのアップデート
  • 有効化前後の1週間を比較

パフォーマンスの変化

やくばと for Clinicではモノレポのアプリケーション内で、クリニックAPI・患者APIといった括りでエンドポイントおよびサーバーを分割しているので、それぞれのAPI Latency/CPU/Memoryの変化を確認していきます。

API Latency(低い方が性能が良い)

クリニックAPI

パーセンタイル before(avg) after(avg)
p50 49ms 45ms
p90 344ms 310ms
p95 518ms 470ms
p99 644ms 580ms

約9~10%の短縮がされているので、かなり改善されていることが分かります。

患者API

パーセンタイル before(avg) after(avg)
p50 132ms 104ms
p90 742ms 523ms
p95 888ms 619ms
p99 937ms 645ms

患者API側はより大きく効果が出ており、全体で30%弱の短縮が実現できています。

CPU使用率(低い方が性能が良い)

クリニックAPI

before(avg) after(avg)
0.42% 0.34%

0.08%減で改善されているようです。

患者API

before(avg) after(avg)
0.73% 0.51%

0.22%減でクリニックAPIよりも大きく効果が出ていそうです。

メモリ使用率(低い方が性能が良い)

クリニックAPI

before(avg) after(avg)
441MiB 455MiB

メモリ使用率は3%ほど増加しています。

患者API

before(avg) after(avg)
517MiB 533MiB

こちらもメモリ使用率は3%ほど増加しています。

RubyVM::YJIT.enableでの有効化

Ruby 3.3からのYJITの変更点として、RubyVM::YJIT.enableの実装も挙げられます。

これまでのRubyでは環境変数RUBY_YJIT_ENABLE=1の設定やコマンドラインで--yjitの付与をしないとYJITを有効化することができませんでしたが、Ruby 3.3からはコード内でRubyVM::YJIT.enableを呼び出すことでYJITの有効化が行えるようになりました。

開発中のRails7.2ではデフォルトでRubyVM::YJIT.enableを呼び出すinitializerが実装される予定です。

また、YJITの起動方法をRubyVM::YJIT.enableに切り替えることでメモリ消費量の点でもメリットがあります。

k0kubun.hatenablog.com

もう一つの利点は、YJITの起動を遅延させることで、 アプリ初期化後は使われないコードのコンパイルを避けメモリ消費量を削減できる点である。 Railsのイニシャライザでも効果はあるが、理想的にはUnicornのafter_forkやPumaのafter_worker_forkから呼び出すと良い。 これにより、起動するワーカーの半分だけYJITを有効化し、インタプリタと性能を比較する基盤として利用することもできる。

という訳でYJITの有効化方法をDockerfile上での環境変数からRubyVM::YJIT.enableに切り替えて、上記のメモリ消費量から更にどのような変化が現れるか1週間分の計測をします。

Rails7.2の実装と同じようにinitializer配下にRubyVM::YJIT.enableを呼び出すコードを配置します。

if defined? RubyVM::YJIT.enable
  Rails.application.config.after_initialize do
    RubyVM::YJIT.enable
  end
end

クリニックAPI

Ruby 3.2.4(avg) Ruby 3.3.1 環境変数によるYJIT有効化時(avg) Ruby 3.3.1 RubyVM::YJIT.enableによるYJIT有効化時(avg)
441MiB 455MiB 424MiB

455Mib -> 424MiBなので7%ほどの減少で効果が出ていそうです。

患者API

Ruby 3.2.4(avg) Ruby 3.3.1 環境変数によるYJIT有効化時(avg) Ruby 3.3.1 RubyVM::YJIT.enableによるYJIT有効化時(avg)
517MiB 533MiB 503MiB

533MiB -> 503MiBで6%ほど消費量が抑えられています。

まとめ

既にYJITを有効化しているRuby 3.2からのアップデートでも大幅なAPIレイテンシ/CPU使用率改善が確認できました。 また、RubyVM::YJIT.enableによってメモリ消費量の削減も確認できたので、まだ有効化方法を切り替えていないプロジェクトでは積極的に導入するのが良さそうです。 一方でプロジェクトによってはメモリの使用量が増加する場合も有り得るのでアップデート前後で継続的な監視が必要そうです。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら medpeer.co.jp

■エンジニア紹介ページはこちら engineer.medpeer.co.jp

■メドピア公式YouTube  www.youtube.com

■メドピア公式note
style.medpeer.co.jp