Ruby, Thread, Fiber, ...Falcon!
背景
Falcon というWebサーバーをご存知ですか。
RubyKaigi 2024 のSamuel Williamsさんによる基調講演において、Flappy BirdのRuby on Rails版がお披露目された際に利用されたWebサーバーの実装です。題材が著名なゲームだったこともあり、講演が記憶に残っている方も多いのではないでしょうか。
個人的な話をすれば、FlappyBirdやRailsでのゲームプログラミングという驚きはあったものの、それがカンファレンスの基調講演のトピックになりえた理由を、完全には消化できていませんでした。当時の私は、Rubyでの開発にどちらかと言うと不慣れでした。
時間を置いて振り返れば、Falconというトピックの価値を理解するには、Ruby(MRI)の並列・並行処理の様相を理解する必要がありました。
本稿は、Rubyにおける並列・並行処理の昨今の概況を総覧し、Falcon x FlappyBird のデモンストレーションの感動の再発見を目指して書かれるものです。
Ruby (MRI)と並列・並行処理
多くのRubyユーザーが利用しているであろう MRI (Matz' Ruby Implementation) は、スレッド分割による並行処理には対応していますが、並列処理については制約を持ちます。
※並列と並行の違いは Ruby 固有のトピックではないため、詳細な説明は割愛します[1]
Thread, GVL
Rubyはいくつかのプログラミング言語同様、プログラムからThreadを作成し、並行処理に利用できます。
しかし MRI においては、1つのRubyプロセスは、常に1つのスレッドのみを実行できる(→並行実行は許容するが、並列実行を許容しない)という制約が存在します。その制約を司るのが GVL(Global VM Lock) です[2]。
MRIはGVLによって並列実行を制限し、その対価としてマルチスレッド実行時のスレッドセーフティを確保します[3]。
Fiber
RubyではThreadの他に、並行実行のための処理単位が存在します。Fiberです。
Rubyコード上のThreadが 基本的に OSのネイティブスレッドと1-1の対応関係を取る一方[4]、Fiberは単一のThread内に複数生成することが可能です。
FiberはあくまでThreadに依存する機構のため、Threadと同じくGVLの並列実行の制約を受けます。
他方、FiberはRubyプロセスがスケジューリングするため、OSがスケジューリングするThreadより高速に実行単位を切り替えられる利点があります。
I/Oバウンド, CPUバウンド
Thread内でのI/O待ちの発生時、Threadは適宜GVLを解放するため、他のThreadが実行可能になります。他方、CPUバウンドな処理[5]については、処理の完了に伴うGVL解放を、他のThreadは待つことになります[6]。
言い換えれば、GVLを前提とするMRIを利用する限り、複数Thread/Fiberで処理を並行化するモチベーションは、I/O待ち発生時の計算リソース利用の最適化の比重が大きくなります。
仮にCPUバウンドな処理に対してThread/Fiberを並列させても、絶対的な実行時間の圧縮に繋がらないどころか、コンテクストスイッチの発生分だけ全体のパフォーマンスは悪化するためです[7]。
I/O待ち時間の効率的利用に並行処理の目的を絞る場合、より軽量に実行単位が切り替え可能な点で、FiberにThreadに対する優位があることがわかります。
I/O バウンド | CPU バウンド | 実行単位の切替 | |
---|---|---|---|
Thread分割 | ✅有効 | ❌制約有(GVL) | ⚠️Fiberよりは高 |
Fiber分割 | ✅有効 | ❌制約有(GVL) | ✅Threadよりは安 |
Ractor
Ractorにも触れておきます。近年[8]開発が進むRactorは、MRIにおいてGVLの制約を克服し、並列実行を実現しうるしくみです。M:N スレッドモデル を採用しており、並行数が膨大になるユースケースへの効率的な対応も目指しているようです[9]。
I/O バウンド | CPU バウンド | |
---|---|---|
Thread分割 | ✅有効 | ❌制約有(GVL) |
Fiber分割 | ✅有効 | ❌制約有(GVL) |
Ractor分割 | ✅有効 | ✅有効 |
I/O、CPUバウンドなどの性質に依らず、とりあえずRactorに載せておけという時代がいずれ来る…のかもしれません。
Falcon
Falconは実験的な目的から作成された、RubyのWebサーバーの実装です。
Falconは1リクエストに対して1Fiberを割り当てます。
Ruby on Railsとセットで利用されることの多いであろうPumaは、1リクエストに対して1Threadです。
1リクエスト-1Threadモデルへの優位は、Fiberの発行コストの低さゆえに、1リクエスト-1Threadモデルよりはるかに多くのクライアントとの同時接続を実現しうることにあります。
ただし前述のとおり、FiberもGVLの制約を受けます。したがって接続数増加の主な恩恵は、I/Oなどの待ちが断続的に発生する処理の並行 に絞られます。
総合すれば、FalconというWebサーバーの優位は、断続的なI/O処理が中心の、大量のクライアントとの同時接続というユースケースにおいて、従来の1リクエスト-1Threadモデルよりもはるかに多くのクライアントに、1台のサーバーで対応し得る点にあると考えます。
Falcon x FlappyBird
FlappyBirdのデモは、断続的なI/Oが発生する典型として活用されたものと考えられます。
すなわちWebSocket接続による 生存期間の長いコネクション であり、秒間30回のイベントループ実行による 断続的なI/O(HTML送信) であり、その並行可能性を示唆するものでした[10][11]。
それは従来の 1リクエスト - 1Thread のモデルでは対応が困難だったユースケースだったと考えれば、Rubyをとりまくエコシステムの日進月歩ぶりに、心を動かされる開発者は少なくなかったのかもしれません。
おわりに
Samuel Williamsさんの基調講演の視聴当時、FlappyBirdというキャッチーな成果物についてのインパクトはあったものの、技術評論社さんの記事で語られたような感動に自分は至ることができませんでした。
Rubyの並行実行についての技術的文脈をある程度理解した今なら、そこに生じ得た感動の気配を、ほんのり感じられている…気がします。
Falconの位置付けはRactorの開発状況やThreadのM:Nスレッド対応状況などの周辺動向が大きく影響すると考えられます。Rubyの並列並行処理の全般の進歩について目を光らせておきたいところです[12]。
-
https://ja.wikipedia.org/wiki/並行計算 / https://ja.wikipedia.org/wiki/並列計算 ↩︎
-
Giant VM Lockと書いてある場合もあります https://docs.ruby-lang.org/ja/latest/class/Thread.html ↩︎
-
参考: https://techracho.bpsinc.jp/hachi8833/2022_09_02/120530 ↩︎
-
Ruby3.3で Thread の M:Nスレッド対応がリリースされています。ただし Ruby 3.3 時点ではexperimental といった位置付けのようで、デフォルトでは従来の 1 Ruby Thread - 1 Native Thread の関係となります。本記事は、従来の1-1関係のスレッドを前提に書かれています ↩︎
-
ローカル変数を用いた大量の数値計算など。参考までに、同じくIOバウンド/CPUバウンドに言及した記事として https://tech.andpad.co.jp/entry/2024/12/20/130000 があります ↩︎
-
現実のアプリケーションにおいては、CPUバウンド・I/Oバウンドな処理は混在することが殆どだと考えられるため、並行化がコストにしかならないケースは限定されると考えます。蛇足ですがこれらが混在するケースでは GVL のロック解放までの制限時間をチューニングすることで、全体の実行速度が改善できることもあるようです(https://bugs.ruby-lang.org/issues/20861) ↩︎
-
Ruby 3.0 でexperimentalリリース。近況は https://techracho.bpsinc.jp/hachi8833/2025_04_01/149342 が新しそうです ↩︎
-
RactorおよびThreadのM:Nスレッドの状況については http://gihyo.jp/article/2024/01/ruby3.3-mn-threads を参照 ↩︎
-
筆者がflappy-birdのコードリーディングした際の備忘記事 https://qiita.com/t407o/items/01aff9475d77404fb56d ↩︎
-
デモではあくまで単一のクライアントとの接続でしたが、技術的にはむしろ、複数クライアントとの同時接続をした場合にその真価が発揮されるものと考えます ↩︎
-
Falconの強みや技術文脈について、よりシャープに言及した記事 (https://note.coincheck.com/n/n47b639917184) も存在します。併せてご参照ください ↩︎
Discussion