Ractor でフィボナッチ数列
まずは小手調べ
take
するまで待って、take
するたびに無限に同じオブジェクトを返してくれる Ractor は、「Pull型通信」を使ってこんな風に作れる。
r = Ractor.new do loop { Ractor.yield 1 } end Array.new(5) { r.take } #=>[1, 1, 1, 1, 1]
わかりやすいですね。
では、例えば単調増加列も簡単に作れる。
r = Ractor.new do i = 1 loop do Ractor.yield i i += 1 end end Array.new(5) { r.take } #=>[1, 2, 3, 4, 5]
フィボナッチ数列を Ractor で計算してみる
こんなコードを書いてみた。
def fibonacci fib = Array.new(N + 1) 2.times do |i| fib[i] = Ractor.new(i) do |j| loop { Ractor.yield j } end end (2..N).each do |i| fib[i] = Ractor.new(fib[i - 1], fib[i - 2]) do |a, b| e = a.take + b.take loop { Ractor.yield e } end end fib[N].take end
fib[n]
はfib[n - 1]
とfib[n - 2]
が計算するまで待ち(これらすべて Ractor オブジェクト)、双方が計算を終了したらtake
されるのを待つ。それで、take
されるたび、計算結果を返し続ける。
ってまあ、ほとんど意味のないプログラムだが、計算と一種の「メモ化」ですね。おもしろいといえばおもしろい。パフォーマンスはどうかというと、N = 3000 としてわたしの環境で 3.4秒くらい。3001個の Ractor オブジェクトが生成されるわけだが、どうなのかな。
なお、fib[3000].take
の結果は、
4224696333392304878706725602341482782579852840250681098010280137314308584370130707224123599639141511088446087538909603607640194711643596029271983312598737326253555802606991585915229492453904998722256795316982874482472992263901833716778060607011615497886719879858311468870876264597369086722884023654422295243347964480139515349562972087652656069529806499841977448720155612802665404554171717881930324025204312082516817125
という具合です。