- バックエンド / リーダー候補
- PdM
- Webエンジニア(シニア)
- Other occupations (19)
- Development
- Business
RubyKaigi 2023 参加記 #8 - Learn Ractor(Day 2)
Photo by Kelly Sikkema on Unsplash
こんにちは! Wantedlyでエンジニアをしているnasaです。
本記事では2日目のLearn Ractorについて紹介していきます。
Ractorの利点
セッションの始めでは並行処理がいかにプログラム上で存在するが話されていました。
MRIや天文台で動作するソフトウェアがありMRIや天文台では様々なセンサー類を使っており、その状態を購読するプロセスが複数動作しています。また、これらのプロセスから情報を受け取り管理するプロセスも多く存在します。
このプロセスは1マシンに存在するプロセスではなく他の計算機を含めた複数のマシンのプロセスになります。
1つ1つのプロセスを逐次実行のつもりで実装したとしてもこれらは実際には並行に動作することになります。
つまり世の中のソフトウェアは、ある一瞬を切り取った場合に逐次実行に見えるだけであり、実際は並行処理に動作といえます。
本セッションでは、並列処理を通常ケースだとすると、逐次実行というのは特殊ケースだと言える。そういう気持ちでプログラミングするのが良いだろうと話されていました。
このような状況においてRubyのThreadはうまく機能するものの並列で動作してくれません、またforkによる並列化では多くのデータがコピーされるという問題があります。
Ractorは並列に動作するかつメモリコピーが発生しない(ように書ける?)ため、Rubyでの並列処理の問題を解決できそうですね。
Ractorの制約
Ractorには競合状態を発生させないためにいくつかの制約が存在します。
1. ブロックのスコープ制約
でRactorの実行完了を待ち処理結果を受け取ることが出来ます。Ractorは下記のようにブロックを渡すことで生成することでき、Ractor#take
でRactorの実行完了を待ち処理結果を受け取ることが出来ます。
r = Ractor.new do
puts "hello world"
1
end
puts r.take # => 1
しかし、Ractorのブロックは通常のブロックとは異なりスコープ外のオブジェクトを参照することが出来ません。例えば下記のコードはArgumentErrorとなります
a = 2
r = Ractor.new do
puts a
end
# => <internal:ractor>:267:in `new': can not isolate a Proc because it accesses outer variables (a). (ArgumentError)
Ractorにオブジェクトを渡したい場合はRactorの初期化時点で渡す必要があります。
このときRactorに渡るのは参照ではなくコピーです。つまり、オブジェクトaをRactor内外で変更した際ににはデータは共有されません。
r = Ractor.new(1) do |a|
puts a
end
2. Ractorと共有可能なデータの制約
共有可能なデータは下記の3つのいずれかに制限されています。
- Immutable
- module/class
- Special object
- Ractor
- Env
Immutableなオブジェクトは通常freezeで作成しますが、Ractor.make_shareableを使って作ることも出来るようです。
s = "share".freeze
s = Ractor.make_shareable("share")
EnvはRactor内で読み書きできるようですが、これだと競合状態が発生してしまいます。
これは意図して共有可能にしているのではなく技術的に対応が難しいという話でした。
詳しいRactorの記述方法に関しては公式のドキュメントがあるので気になった方はこちらを
https://github.com/ruby/ruby/blob/master/doc/ractor.md
実際に使ってみて
詳細は省きますが、発表者のMasatoshi SEKIさんはポケモンカードの検索システムを作っており、そこの一部をRactorに書き換えたそうです。
アルゴリズムの話は資料が公開され次第そちらを見てほしいのですが、ザックリ言うと大きなデータを走査していた箇所を並列ループに書き換えると言ったものです。また、このときデータをコピーしてしまうとRactorの利点が半減してしまうため、コピーが発生しないような工夫がいくつもなされていました。
検索システムの2箇所の処理を置き換えてみての結果ですが、一方は40%高速化、もう一方は300~400%高速化することが出来ていました。(速い!)
ただし問題もあったようで、Ractorを大量に作る際には性能が悪化したようです。これが発覚した経緯が個人的には面白くて、検索システムの宣伝ツイートをしたところ多くのリクエストが来るようになり発覚したようです。
今回のRubyKaigiではRactorの性能に関する話をよく聞きますが、鋭意改善中らしいので今後が楽しみですね。
感想
個人的にも並列処理に関心があるためとても面白い発表でした。普段はGoやRustを利用して並行プログラミングを行っていますが、RubyのRactorはそれらとかなり違う書き味だと知りました。RactorはActor model、GoはCSPという設計思想の違いが根本にあるのでシンタックスとしても違いが出るのは当然ですね。並行プログラミングについてもっとよく知りたいなと思いました。
またこのセッションを聞いてRactorの今後のアップデートがますます楽しみになりました!勉強になるセッションをありがとうございます