11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RubyKaigi2023 "Ractor" reconsideredで分からなかった部分などを調べてみた

Last updated at Posted at 2023-05-11

RubyKaigi2023が始まりました!!!

待ちに待ったRubyKaigiがついに始まりました。
今年はRubyKaigiにオンライン参加として、オフィスでRubyKaigiを生中継しながらワイワイする会をやりました。
Qiitaとしてはスポンサーとして協賛、ブース出展しており、現地参加メンバーもいます。

"Ractor" reconsidered

初日の今日もいくつかセッションを見ましたが、その中で理解の不十分だったこのセッションについて、過去の発表等も振り返りながら調べてみました。

Ractorについて

Ractorについては色々な記事ですでに説明されているのですが、軽くおさらいします。

RubyKaigi Takeout 2020

Ractorはマルチコアを使って並列に処理を行えるように作られました。
マルチプロセスを使う方法もありますが、データの共有に制限があったり、メモリ空間が別れていることやforkの性能の影響があったりすることで、スレッドを使いたいことがあります。
ただ、RubyのThreadはGVLにより、並列に実行することはできない制限がかかっています。またThreadプログラミングは難しい (同期等の問題やデバックの問題)という背景があり、並列実行できるようにするRactorが作られました。

Difficulties and Issues of "Ractor"

本セッションでRactorに関するDifficulties and Issuesとして挙げられていたものについて少し詳しく調べてみます。

APIについて

Ractor間で共有できるオブジェクトには制限があります。 (Shareable Objects)

  • Class/module objects
  • Immutable objects (frozen objects which don't refer to unshareable-objects)
  • Special objects (Ractor object)

また外側のローカル変数にはアクセスできず、エラーになるという制約もあります。
そして、このオブジェクトをメッセージとして送信する方法として、deep copyして渡す方法と、move: trueを使ってshallow copyで渡す方法があります。(送信元のRactorからは参照できなくなる)

Ractor間のコミュニケーションは2種類あります。

1. 送り先のRactorを指定してpushする r.send(obj)Ractor.receive

r2 = Ractor.new Ractor.current do |cr|
  cr.send Ractor.receive + 1
end

r1 = Ractor.new r2 do |r2|
  r2.send Ractor.receive + 1
end

r1.send 1

# r1 -> r2 -> mainへ
p Ractor.receive
# => 3

2. 受信元を指定してpullする Ractor.yield(obj)r.take

r = Ractor.new do
  val = 0
  loop do
    Ractor.yield val
    val += 1
  end
end

3.times do
  puts r.take
end

# 0
# 1
# 2

それぞれ、「receiveはキューが空の時にブロックし、sendはブロックしない」「yieldはtakeされるまでブロックし、takeはyieldされるまでブロックする」という挙動をします。

これにより、以下のような挙動をします。

r = Ractor.new do
  while msg = Ractor.receive
    Ractor.yield msg
  end
  :fin
end

r.send 1
r.send 2 # rはyieldでブロックされているがキューに積む
r.send 3
r.send nil
p r.take #=> 1
p r.take #=> 2
p r.take #=> 3
p r.take #=> :fin

Poor performance

パフォーマンスに関して3点課題が挙げられていました。

  • GC実行時にすべてのRactorが止まってしまう
    • → Future work
  • Ractorごとに1ネイティブスレッドを使うので、システムコールが増えてしまう
    • → 改善予定
  • Ractor.select(*rs)のオーダー O(n)
    • → 改善予定

Ractor.selectの改善

selectは複数Ractorの監視のためのメソッドです。
Ractor::RemoteErrorをrescueすることで監視の役割を行うことができます。
このAPIをごそっと改善中で Ractor::Selector というものを検討しているようです。

ネイティブスレッドの問題

MaNy ProjectとしてM:Nスレッドの説明が昨年のRubyKaigiにて発表されていました。

現状はネイティブスレッドとRubyスレッドが1:1ですが、これをM:Nにするのを目指しています。

こちらのセッションで、M:Nのメリットとして軽量になることが挙げられています。GVL(Ruby)のスケジューリングがネイティブスレッドのスケジューリングを気にせず実行できるので、オーバーヘッドが減らせるようです。

また、そもそもOSの制限により、一定以上のネイティブスレッドを作れない制限もあるようです。

M:Nスレッドを実現するために、2つのスケジューリングをRubyレイヤで行います。

このうち、去年は対応されていなかったRactorレベルの対応ができたことで、masterと比較してRactor作成が4.5倍、Ring exampleの処理スピードが22.6倍に向上したようです。

GCによるパフォーマンスへの影響

現時点ではまだ対応できていないようですが、Future worksとして対応が期待されます。

感想

今までうまく使われていなかったRactorですが、パフォーマンスが大幅に向上する!となり、どんどんエコシステムが発達していき、Ractorがたくさん使われるようになる未来が楽しみです。

参考

11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?