SlideShare a Scribd company logo
1 of 36
Download to read offline
Fiber の使いどころ
2017/10/18
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
1RubyKaigi 2017 に行ってきました。
下記の2つで Fiber について取り上げられていました。
Fiber in the 10th year
笹田耕一さん
How to write synchronization mechanisms for Fiber
関将俊(せき・まさとし)さん
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
2Fiber in the 10th year
https://www.slideshare.net/KoichiSasada/fiber-in-the-10th-year
Fiber の実用例
内部イテレータから外部イテレータ
エージェントシミュレーション
ノンブロッキングIOスケジューラ
Fiber の歴史
コンテキストスイッチの軽量化
Auto Fiber の提案
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
3How to write synchronization mechanisms for Fiber
https://speakerdeck.com/m_seki/how-to-write-synchronization-
mechanisms-for-fiber
Fiber を実装したときの知見の話
Process の方が Thread よりほとんどの人にとって良い
Thread の方が Fiber よりほとんどの人にとって良い
Fibonacci (Enumerator)
Rdv (SizedQueue、Channel)
Multiplexer
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
4Fiber と Thread の違い
Thread
プリエンプティブなコンテキストスイッチ
プリエンプティブ=割り込み
Process よりも軽量だが、Fiber よりは重い
私が Rubyist になった理由は、スレッドライブラリがまとも
だったから(2000年前後)
(Perl のスレッドはバッドノウハウが必要な状況だった)
Fiber
ノンプリエンプティブなコンテキストスイッチ
※ 要は割り込みがなく明示的に切り替えが必要ということ
Process 、Thread と比較して軽量
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
5RubyKaigi2017 でのできごと
m_seki さんが「Fiber より Thread を使うべき」と
発表内で発言していた。
この発言に強く疑問を感じた
他言語での実装状況
C# は 2005年(C# 2.0)で Generator を導入
Python は 2006年(Python 2.5)で Generator を導入
Ruby は 2007年(Ruby 1.9) で Fiber を導入
Java は 2014年(Java 8)で Generator を導入
ECMA Script は2015年(ES2015)で Generator を導入
C++ は 2020年(?)(C++20) Coroutine を導入予定
こんなに広範に支持される機能が推奨できないというの
は考えられないのではないか、と思った
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
6Fiber の利用例
イテレータ
かなり頻繁に使う。
ただし、Fiber を直接使わなくても Enumerator.new などを使
えばほとんどの場合十分
7
以下、2013年の
Ruby関西での発表
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
単純な実装 8
a = 0; b = 1
loop do
a, b = b, a + b
break if 100 < a
puts a
end
■ (1) もっとも単純な実装 ■ (2) 多重代入の利用
もっとも単純なフィボナッチ数列の Ruby による実装
ポイント: ループ、多重代入、while の構文
a = 0; b = 1
loop do
tmp = a
a = b
b = tmp + b
break if 100 < a
puts a
end
a = 0; b = 1
while a < 100
puts b
a, b = b, a + b
end
■ (3) while の利用
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
ブロック付メソッド 9
■ (4) ブロック付メソッド呼び出し ■ 処理の実施順
yield キーワードにより、ブロック引数に値を渡せる。
ブロック付メソッド呼出しにより、
値の生成と、値の利用の処理を分離できる
def fibonacci
a = 0; b = 1
loop do
a, b = b, a + b
yield a
end
end
n = 1
fibonacci do |i|
break if 100 < a
puts “#{n} #{i}"
n += 1
end
①
②
③
⑤
⑦
⑤ → ①
→ ② → ⑦ → ③
→ ② → ⑦ → ③
→ ② → ⑦ → ③
・・・ (以下おなじ)
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
クラスの利用 10
■ (5) クラスの利用
クラスを利用し、内部状態をインスタンス変数に持たせる方法もある
この場合は、next を実行したときに内部状態が変化する。
class Fibonacci
def initialize
@a = 0; @b = 1
end
def next
@a, @b = @b, @a + @b
return @a
end
end
fibonacci = Fibonacci.new
loop do
i = fibonacci.next()
break if i > 100
puts i
end
インスタンス変数の初期化
次の値の生成
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
クロージャ 11
■ (6) クロージャの利用
クロージャという機能により、ブロックの中から
ネストの外側の変数を参照できる。
内部状態を持つ関数を簡易に作ることができる。
fibonacci = proc do
a = 0; b = 1
lambda do
a, b = b, a + b
return a
end
end.call()
loop do
i = fibonacci.()
break if i > 100
puts i
end
スコープを導入するためのクロージャ
外側のクロージャを呼ぶ出すための Proc#call
内側のクロージャを作るための lambda
中で return を使いたいので 「 lambda 」にしている。
クロージャを呼び出し、Fibonacci 数列の次の値を取得する
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
Ruby 1.8: Generator の利用 12
■ (7) Generator の利用
Ruby 1.8 には、標準ライブラリに Generator があった
内部実装に callcc を使うので、ちょいと遅い
require 'generator'
g = Generator.new do |yielder|
a = 0; b = 1
loop do
a, b = b, a + b
yielder.yield a
end
end
g.each do |i|
break if i > 100
puts i
end
Generator が生成する項を順に取得する
次の値を生成する
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
Ruby 1.9: Enumerator の利用 13
■ (8) Enumerator の利用
Ruby 1.9 では Enumerator を利用可能
内部実装に Fiber を使うため、高速
Enumerable モジュールのメソッド(each_cons など)を利用可能
e = Enumerator.new do |yielder|
a = 0; b = 1
loop do
a, b = b, a + b
yielder << a
end
end
e.each do |i|
break if i > 100
puts i
end
Enumerator が生成する項を順に取得する
次の生成する値を yield する
Enumerator による実装は、
利用側のコード行数がコンパクト
になる。
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
(参考) Enumerator と Fiber 14
e = Enumerator.new do |yielder|
a = 0; b = 1
loop do
a, b = b, a + b
yielder << a
end
end
e.each do |i|
break if 100 < i
puts i
end
■ (8) 《再掲》 Enumerator の例
fib = Fiber.new do
a = 0; b = 1
loop do
a, b = b, a + b
Fiber.yield a
end
end
def fib.each
loop do
yield self.resume
end
end
fib.each do |i|
break if 100 > i
puts i
end
■ 《参考》 (9) Fiberの例
Enumerator は Fiber を活用するデザインパターンの1つ
Fiber における難しい概念を隠ぺい
Fiber.yield => Enumerator::Yielder#<< 、 Enumerator#each による列挙
Enumerator を使いこなせれば、十分 Fiber のメリットを活用できる
《参考: Fiber にできて、Enumerator でできないこと》
親 Fiber から 子Fiber へのデータの送付(Fiber.yield の返り値の利用)
これが必要。
だけど、これが
難しい。(汗)
Enumerator の
内部実装まで
考えなくていい
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
Enumerator はベンリ 15
fib = Enumerator.new do |yielder|
a = 0; b = 1
loop do
a, b = b, a + b
yielder << a
end
end
fib.each do |i|
break if 100 < I
puts i
end
fib.each_cons(2) do |i, j|
break if 100 < i
puts "#{i} #{j}"
end
■ (10) Enumerator の例
def fibonacci
a = 0; b = 1
loop do
a, b = b, a + b
yield a
end
end
fibonacci do |i|
break if 100 < i
puts i
end
prev = nil
fibonacci do |i|
break if 100 < i
puts "#{prev} #{i}" if prev
prev = i
end
■ 《参考》 (11) ブロック付メソッド呼び出しの例
Enumerator なら Enumerable の便利メソッドを使える。
ブロック付メソッド呼び出しは単純なイテレーションならいいけど。。。
Enumerator はオブジェクトなので、使い回しが簡単。
引数にしたり、返り値にしたり、インスタンス変数に格納したり
Enumerator オブジェクトの生成も慣れれば簡単。
慣れれば、
カンタン
面倒 ! !
Enumerable
の便利メソッド
を使い放題
単純なイテレーション
ならいいけど・・・。
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
Enumerable#lazy の利用 16
fib = Enumerator.new do |yielder|
a = 0; b = 1
loop do
a, b = b, a + b
yielder << a
end
end
fib.select do |i|
i < 100
end.
take(10).each do |i|
puts i
end
■ (12) うまく動かない例
fib = Enumerator.new do |yielder|
a = 0; b = 1
loop do
a, b = b, a + b
yielder << a
end
end
fib.lazy.select do |i|
i < 100
end.take(10).each do |i|
puts i
end
■ (13) Enumerable#lazy を利用する例
Ruby 2.0.0 から、Enumerable#lazy が利用可能となる
Enumerable#lazy を使うと、遅延評価になり、無限を無限のまま扱える
宇宙ヤバい
Ruby 1.9 でも gem でインストール可能
@yhara さんの作品
ここで、無限ループに
なり、うまく動かない。
※ take_while を
使う方法もある。
Enumerable#lazy
を使えば、正しく
期待通りに動作する
17
引用終わり
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
Agent でのFiber の利用例
Agent
ゲームのキャラクターごとに Fiber に対応させ、フレームごと
に動作させるというのが自然に実装できる。
下記の例はささださんの発表そのまま
ただし、私自身はゲーム制作などしないし・・・。
18
enemy = Fiber.new do
loop do
move_up
Fiber.yield
move_right
Fiber.yield
move_down
Fiber.yield
move_left
Fiber.yield
end
end
内部状態があり、状態遷移している。
1フレームごとに、順に異なる動作をするという
ことを自然に実装できる
ゲームのキャラの動きなどに使うとベンリ。
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
IO での Fiber の利用
m_seki さんの発表はこの例
正直、ネットワークIO に Fiber は向かないと感じる
この点で、せきさんに同意
理由
IO#read_nonblock を使い、改行区切りの処理を自分で記述す
る必要がある
そもそも、Ruby の Thread は IOブロックがあると、別スレッ
ドに切り替わる。
IO 処理の場合は、IO 自体がパフォーマンス上のボトルネック
Fiber によるパフォーマンス向上効果は限定的 [要出典]
C10K 問題が顕在化している場合は別かもしれないが・・・。
19
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
GitHub での実事例の調査結果
GitHub で Ruby 言語で Fiber で検索してみた
Fiber の練習用の実装がほとんど。
なんとなく使ってそうなプロジェクトを調査。
Concurrent Ruby
使ってなかった。(CRuby に限定しないため)
Fluentd
使ってなかった。
Celluloid::IO
Celluloid::Task::Fibered というのがあったが詳細不明
逆に使っているプロジェクト
em-synchrony
前ページで個人的に否定していたネットワークIO が主な用途。。
20
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
思考実験
あえて Fiber がネットワークIO で有効な状況を想像
HTTP サーバで Fiber を利用
クライアントが非常に低速(モバイル回線など)
1つのサーバに非常に多数のクライアントが接続する
クライアントあたり1スレッド占有されると困る
サーバ資源にかなり余裕があるのに、DoS 状態になる
ある意味アタック
Thread ではメモリ資源などの理由で困難だが、
Fiber ならカジュアルにワーカを増減できる [要出典]
多数の低速クライアントなどの状況でもなんとかなる?
21
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
とはいえ、、、
ふつう、Nginx と組合わせて使うよね・・・?
HTTP なら低速クライアントが多数でも問題ないはず
ほかのプロトコルなら状況違うかも(識者おしえて)
22
ブラウザ
アプリケーションサーバ
低速だから
時間かかる
待ちっぱなしで
ワーカ占有
ブラウザ
NGINX
アプリケーションサーバ
NGINX に送信完了
で、仕事終わり
ワーカが解放される
NGINX がよしなに
処理してくれる
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
ほか、事例
自分が過去、Fiber の実用的な使ったことがあることを
思い出した。
23
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
24
以下、2013年の
amagasakirb
での発表の引用
「Ruby と LLVM 」
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
Parslet と
Ruby-LLVM の
夢の競演
25
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
Parslet とは
Parslet
PEG (Parsing Expression Grammer) パーサ生成ライブラリ
Ruby の内部 DSL で PEG を表現する。
有力な対抗馬
TreeTop: Rubyっぽい独自言語を使う
citrus: Ruby っぽい独自言語を使う
rsec:Rubyの内部DSL だけど、なんかイマイチ。
強力な点
エラーレポート機能に注力
テストしやすいコード
parser と transformer の2層に明確に分離されている
抽象構文木が慣れ親しんだ Ruby のハッシュで表現されている
26
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
Parslet で電卓を実装してみる
仕様
計算できるのは、Int だけ
計算できる演算は +、-、*、/ の4つだけ
答えももちろん Int だけ
*、/ は +、- より優先順位が高い
出力、計算
Ruby-LLVM で LLVM のコードを出力させる
実際の計算はしないつもり
工夫している点
BasicBlock#build の引数のブロック内で、
LLVM の命令を追加していくことが必要。
この仕様は Transformer の rule と相性ワルし!
BasicBlock#build を別の Fiber で実行することで解決!
27
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
電卓の Parser の実装 28
class Parser < Parslet::Parser
rule(:space) { match('¥s').repeat(1) }
rule(:sp) { space.maybe }
rule(:integer) { match('[0-9]').repeat(1).as(:int) }
rule(:mul) {
integer.as(:left) >> sp >> match('[*/]').as(:op) >>
sp >> multiplication.as(:right)
}
rule(:add) {
multiplication.as(:left) >> sp >> match('[+-]').as(:op) >>
sp >> addition.as(:right)
}
rule(:multiplication) { mul | integer }
rule(:addition) { add | multiplication }
rule(:expression) { addition.as(:expr) }
root :expression
end
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
電卓の Transformer の実装
LLVM 関係の処理は、LLVMBuilder クラスに外出し
rule を記述するだけで比較的簡単に実装可能
29
class LLVMTransformer <Parslet::Transform
b = @@builder = LLVMBuilder.new
rule(:int => simple(:n)){
b.int n
}
rule(:left => simple(:l),
:right => simple(:r),
:op => simple(:op)){
b.calc op, l, r
}
rule(:expr => simple(x)){
b.ret x
}
def do(tree)
apply(tree)
@@builder.dump
end
end
パーサによって生成された
"int" 要素を処理。
LLVM の整数に変換。
パーサによって生成された
"left"、 "right"、 "op" の
要素を持つサブツリーに対して
処理を行う。
LLVM の命令に変換する。
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
電卓の LLVMBuilder の実装
LLVMBuilder では LLVM の命令を生成する。
building_loop で rule からの指示を待つ
30
class LLVMBuilder
def initialize
@fiber = Fiber.new do
@module = LLVM::Module.new("calculator")
@module.functions.add("add", [], LLVM::Int) do |f,|
bb = f.basic_blocks.append("entry")
bb.build do |builder|
building_loop builder
end
end
end
@fiber.resume
end
...
end
新しい Fiber を生成している。
1. Module の生成
2. Function の追加
3. Basic Block の追加
4. Builder を使い、LLVM 命令を追加
building_loop まで Fiber を実行させておく。
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
電卓の LLVMBuilder の実装 31
def building_loop builder
llvm_ir = nil
loop do
op, l, r = Fiber.yield llvm_ir
case op
when "+"
llvm_ir = builder.add l, r
when "-"
llvm_ir = builder.sub l, r
when '*'
llvm_ir = builder.mul l, r
when "/"
llvm_ir = builder.sdiv l, r
when :exit
llvm_ir = builder.ret l
break
end
end
end
Fiber から呼出し元への返り値みたいなもの。
Fiber なので、Fiber.yield の引数で処理結果を
渡して呼出し元に処理を戻す。
呼出し元から来る引数みたいなもの。
Fiber なので、Fiber.yield の返り値で
呼出し元から受け取る。
四則演算の LLVM
の命令を生成
計算結果を return する
LLVM の命令を生成
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
電卓の LLVMBuilder の実装
Fiber の処理を calc 、ret というメソッドで隠ぺい
calc +、-、×、÷ の四則演算を LLVM の処理に変換する
ret 処理結果を LLVM の関数の返り値にする。
32
def int n
LLVM.Int n.to_i
end
def calc op, l, r
@fiber.resume op, l, r
end
def ret x
@fiber.resume :exit, x
end
def dump
@module.dump
end
Fiber の外から、演算を受け取って、
Fiber に渡す。
さらに Fiber での処理結果を返す。
生成された LLVM の計算プログラムを
出力する。
Ruby の整数を LLVM の整数に
変換する。
処理を終了し、演算結果を返り値
として返す
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
33
引用終わり
Ebisu.rb #12 発表資料 「Fiber の使いどころ」
まとめ
RubyKaigi では Fiber に関連した発表が2つあった
m_seki さんが「Fiber より Thread 推奨」と言ってた
調査したが、ネットワークIO 程度しか、有効な事例は
見つからなかった
が、Ruby の Thread は IO でブロックされず、切り替わる
IO の多重化については Ruby の Thread で十分満足している
Nginx で多重化処理させるテクもある。
ただし私自身、過去 Fiber の有効な活用事例があった
ブロック引数を必要とするライブラリを使いたい
けど、そのブロックの外から処理を与えたい
Fiber 使えば、すっきり!
そういえば、Ruby 1.9 で話題だったマルチVM って、
どうなったのか詳しい人おしえてください。
34
35
ご清聴ありがとう
ございました

More Related Content

What's hot

Apache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォームApache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォーム
Kouhei Sutou
 

What's hot (20)

Apache Avro vs Protocol Buffers
Apache Avro vs Protocol BuffersApache Avro vs Protocol Buffers
Apache Avro vs Protocol Buffers
 
C++ マルチスレッド 入門
C++ マルチスレッド 入門C++ マルチスレッド 入門
C++ マルチスレッド 入門
 
デキるプログラマだけが知っているコードレビュー7つの秘訣
デキるプログラマだけが知っているコードレビュー7つの秘訣デキるプログラマだけが知っているコードレビュー7つの秘訣
デキるプログラマだけが知っているコードレビュー7つの秘訣
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
できる!並列・並行プログラミング
できる!並列・並行プログラミングできる!並列・並行プログラミング
できる!並列・並行プログラミング
 
ruby-ffiについてざっくり解説
ruby-ffiについてざっくり解説ruby-ffiについてざっくり解説
ruby-ffiについてざっくり解説
 
Apache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォームApache Arrow - データ処理ツールの次世代プラットフォーム
Apache Arrow - データ処理ツールの次世代プラットフォーム
 
Rustに触れて私のPythonはどう変わったか
Rustに触れて私のPythonはどう変わったかRustに触れて私のPythonはどう変わったか
Rustに触れて私のPythonはどう変わったか
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っている
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
 
RailsGirls から始める エンジニアリングはじめの一歩
RailsGirls から始める エンジニアリングはじめの一歩RailsGirls から始める エンジニアリングはじめの一歩
RailsGirls から始める エンジニアリングはじめの一歩
 
分散システムについて語らせてくれ
分散システムについて語らせてくれ分散システムについて語らせてくれ
分散システムについて語らせてくれ
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
エンジニアから飛んでくるマサカリを受け止める心得
エンジニアから飛んでくるマサカリを受け止める心得エンジニアから飛んでくるマサカリを受け止める心得
エンジニアから飛んでくるマサカリを受け止める心得
 
何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
人生がときめくAPIテスト自動化 with Karate
人生がときめくAPIテスト自動化 with Karate人生がときめくAPIテスト自動化 with Karate
人生がときめくAPIテスト自動化 with Karate
 

Similar to Fiberの使いどころ

フィボナッチ数列の作り方
フィボナッチ数列の作り方フィボナッチ数列の作り方
フィボナッチ数列の作り方
Tomoya Kawanishi
 
18166746-NeverBlock-RubyKaigi2009
18166746-NeverBlock-RubyKaigi200918166746-NeverBlock-RubyKaigi2009
18166746-NeverBlock-RubyKaigi2009
Muhammad Ali
 
Enumerable lazy について
Enumerable lazy についてEnumerable lazy について
Enumerable lazy について
Tomoya Kawanishi
 
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい) 泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
Akihiro Kuwano
 
ゲットーの斜め上をゆくWebアプリケーションフレームワークの開発
ゲットーの斜め上をゆくWebアプリケーションフレームワークの開発ゲットーの斜め上をゆくWebアプリケーションフレームワークの開発
ゲットーの斜め上をゆくWebアプリケーションフレームワークの開発
emasaka
 
20110820 metaprogramming
20110820 metaprogramming20110820 metaprogramming
20110820 metaprogramming
Masanori Kado
 

Similar to Fiberの使いどころ (20)

フィボナッチ数列の作り方
フィボナッチ数列の作り方フィボナッチ数列の作り方
フィボナッチ数列の作り方
 
18166746-NeverBlock-RubyKaigi2009
18166746-NeverBlock-RubyKaigi200918166746-NeverBlock-RubyKaigi2009
18166746-NeverBlock-RubyKaigi2009
 
Enumerable lazy について
Enumerable lazy についてEnumerable lazy について
Enumerable lazy について
 
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい) 泥臭い運用から、プログラマブルインフラ構築(に行きたい)
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
 
Perlでちょいモテデザインパターン
PerlでちょいモテデザインパターンPerlでちょいモテデザインパターン
Perlでちょいモテデザインパターン
 
InfiniBand on Debian
InfiniBand on DebianInfiniBand on Debian
InfiniBand on Debian
 
ffi for rubyists
ffi for rubyistsffi for rubyists
ffi for rubyists
 
メタメタプログラミングRuby
メタメタプログラミングRubyメタメタプログラミングRuby
メタメタプログラミングRuby
 
Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021
 
201911 のの会@関数Talk 17th @function-talk-in-notesknows-workshop
201911 のの会@関数Talk 17th @function-talk-in-notesknows-workshop201911 のの会@関数Talk 17th @function-talk-in-notesknows-workshop
201911 のの会@関数Talk 17th @function-talk-in-notesknows-workshop
 
ゲットーの斜め上をゆくWebアプリケーションフレームワークの開発
ゲットーの斜め上をゆくWebアプリケーションフレームワークの開発ゲットーの斜め上をゆくWebアプリケーションフレームワークの開発
ゲットーの斜め上をゆくWebアプリケーションフレームワークの開発
 
高トラフィックサイトをRailsで構築するためのTips基礎編
高トラフィックサイトをRailsで構築するためのTips基礎編高トラフィックサイトをRailsで構築するためのTips基礎編
高トラフィックサイトをRailsで構築するためのTips基礎編
 
最速C# 7.x
最速C# 7.x最速C# 7.x
最速C# 7.x
 
20110820 metaprogramming
20110820 metaprogramming20110820 metaprogramming
20110820 metaprogramming
 
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第5回 ‟配列と構造体„
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第5回 ‟配列と構造体„【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第5回 ‟配列と構造体„
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第5回 ‟配列と構造体„
 
Kanazawa.js.Next
Kanazawa.js.NextKanazawa.js.Next
Kanazawa.js.Next
 
JavaScript.Next Returns
JavaScript.Next ReturnsJavaScript.Next Returns
JavaScript.Next Returns
 
きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回
 
Ansible2.9 ネットワーク対応のアップデート #ansiblejp
Ansible2.9 ネットワーク対応のアップデート #ansiblejpAnsible2.9 ネットワーク対応のアップデート #ansiblejp
Ansible2.9 ネットワーク対応のアップデート #ansiblejp
 
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第6回 ‟文字列とオブジェクト„
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第6回 ‟文字列とオブジェクト„【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第6回 ‟文字列とオブジェクト„
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第6回 ‟文字列とオブジェクト„
 

More from Tomoya Kawanishi

Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーRuby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Tomoya Kawanishi
 

More from Tomoya Kawanishi (20)

英単語の覚え方
英単語の覚え方英単語の覚え方
英単語の覚え方
 
ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例
 
エンジニア転職のノウハウ
エンジニア転職のノウハウエンジニア転職のノウハウ
エンジニア転職のノウハウ
 
Ruby の文字列について
Ruby の文字列についてRuby の文字列について
Ruby の文字列について
 
Ruby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてRuby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構について
 
Ruby初心者からよく質問されること
Ruby初心者からよく質問されることRuby初心者からよく質問されること
Ruby初心者からよく質問されること
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler について
 
Ruby の正規表現について
Ruby の正規表現についてRuby の正規表現について
Ruby の正規表現について
 
Ruby での外部コマンドの実行について
Ruby での外部コマンドの実行についてRuby での外部コマンドの実行について
Ruby での外部コマンドの実行について
 
Ruby のワンライナーについて
Ruby のワンライナーについてRuby のワンライナーについて
Ruby のワンライナーについて
 
AWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことAWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったこと
 
PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選
 
HTTPと Webクローリングについて
HTTPと WebクローリングについてHTTPと Webクローリングについて
HTTPと Webクローリングについて
 
Rake
RakeRake
Rake
 
Active record query interface
Active record query interfaceActive record query interface
Active record query interface
 
Active Support のコア拡張機能について
Active Support のコア拡張機能についてActive Support のコア拡張機能について
Active Support のコア拡張機能について
 
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーRuby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
 
RubyのDir、File、IO について
RubyのDir、File、IO についてRubyのDir、File、IO について
RubyのDir、File、IO について
 
Thread の利用事例紹介
Thread の利用事例紹介Thread の利用事例紹介
Thread の利用事例紹介
 
Ruby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてRuby の制御構造とリテラルについて
Ruby の制御構造とリテラルについて
 

Recently uploaded

Recently uploaded (11)

Utilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native IntegrationsUtilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native Integrations
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
 
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
 
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
 

Fiberの使いどころ

  • 2. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 1RubyKaigi 2017 に行ってきました。 下記の2つで Fiber について取り上げられていました。 Fiber in the 10th year 笹田耕一さん How to write synchronization mechanisms for Fiber 関将俊(せき・まさとし)さん
  • 3. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 2Fiber in the 10th year https://www.slideshare.net/KoichiSasada/fiber-in-the-10th-year Fiber の実用例 内部イテレータから外部イテレータ エージェントシミュレーション ノンブロッキングIOスケジューラ Fiber の歴史 コンテキストスイッチの軽量化 Auto Fiber の提案
  • 4. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 3How to write synchronization mechanisms for Fiber https://speakerdeck.com/m_seki/how-to-write-synchronization- mechanisms-for-fiber Fiber を実装したときの知見の話 Process の方が Thread よりほとんどの人にとって良い Thread の方が Fiber よりほとんどの人にとって良い Fibonacci (Enumerator) Rdv (SizedQueue、Channel) Multiplexer
  • 5. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 4Fiber と Thread の違い Thread プリエンプティブなコンテキストスイッチ プリエンプティブ=割り込み Process よりも軽量だが、Fiber よりは重い 私が Rubyist になった理由は、スレッドライブラリがまとも だったから(2000年前後) (Perl のスレッドはバッドノウハウが必要な状況だった) Fiber ノンプリエンプティブなコンテキストスイッチ ※ 要は割り込みがなく明示的に切り替えが必要ということ Process 、Thread と比較して軽量
  • 6. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 5RubyKaigi2017 でのできごと m_seki さんが「Fiber より Thread を使うべき」と 発表内で発言していた。 この発言に強く疑問を感じた 他言語での実装状況 C# は 2005年(C# 2.0)で Generator を導入 Python は 2006年(Python 2.5)で Generator を導入 Ruby は 2007年(Ruby 1.9) で Fiber を導入 Java は 2014年(Java 8)で Generator を導入 ECMA Script は2015年(ES2015)で Generator を導入 C++ は 2020年(?)(C++20) Coroutine を導入予定 こんなに広範に支持される機能が推奨できないというの は考えられないのではないか、と思った
  • 7. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 6Fiber の利用例 イテレータ かなり頻繁に使う。 ただし、Fiber を直接使わなくても Enumerator.new などを使 えばほとんどの場合十分
  • 9. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 単純な実装 8 a = 0; b = 1 loop do a, b = b, a + b break if 100 < a puts a end ■ (1) もっとも単純な実装 ■ (2) 多重代入の利用 もっとも単純なフィボナッチ数列の Ruby による実装 ポイント: ループ、多重代入、while の構文 a = 0; b = 1 loop do tmp = a a = b b = tmp + b break if 100 < a puts a end a = 0; b = 1 while a < 100 puts b a, b = b, a + b end ■ (3) while の利用
  • 10. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 ブロック付メソッド 9 ■ (4) ブロック付メソッド呼び出し ■ 処理の実施順 yield キーワードにより、ブロック引数に値を渡せる。 ブロック付メソッド呼出しにより、 値の生成と、値の利用の処理を分離できる def fibonacci a = 0; b = 1 loop do a, b = b, a + b yield a end end n = 1 fibonacci do |i| break if 100 < a puts “#{n} #{i}" n += 1 end ① ② ③ ⑤ ⑦ ⑤ → ① → ② → ⑦ → ③ → ② → ⑦ → ③ → ② → ⑦ → ③ ・・・ (以下おなじ)
  • 11. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 クラスの利用 10 ■ (5) クラスの利用 クラスを利用し、内部状態をインスタンス変数に持たせる方法もある この場合は、next を実行したときに内部状態が変化する。 class Fibonacci def initialize @a = 0; @b = 1 end def next @a, @b = @b, @a + @b return @a end end fibonacci = Fibonacci.new loop do i = fibonacci.next() break if i > 100 puts i end インスタンス変数の初期化 次の値の生成
  • 12. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 クロージャ 11 ■ (6) クロージャの利用 クロージャという機能により、ブロックの中から ネストの外側の変数を参照できる。 内部状態を持つ関数を簡易に作ることができる。 fibonacci = proc do a = 0; b = 1 lambda do a, b = b, a + b return a end end.call() loop do i = fibonacci.() break if i > 100 puts i end スコープを導入するためのクロージャ 外側のクロージャを呼ぶ出すための Proc#call 内側のクロージャを作るための lambda 中で return を使いたいので 「 lambda 」にしている。 クロージャを呼び出し、Fibonacci 数列の次の値を取得する
  • 13. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 Ruby 1.8: Generator の利用 12 ■ (7) Generator の利用 Ruby 1.8 には、標準ライブラリに Generator があった 内部実装に callcc を使うので、ちょいと遅い require 'generator' g = Generator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder.yield a end end g.each do |i| break if i > 100 puts i end Generator が生成する項を順に取得する 次の値を生成する
  • 14. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 Ruby 1.9: Enumerator の利用 13 ■ (8) Enumerator の利用 Ruby 1.9 では Enumerator を利用可能 内部実装に Fiber を使うため、高速 Enumerable モジュールのメソッド(each_cons など)を利用可能 e = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end e.each do |i| break if i > 100 puts i end Enumerator が生成する項を順に取得する 次の生成する値を yield する Enumerator による実装は、 利用側のコード行数がコンパクト になる。
  • 15. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 (参考) Enumerator と Fiber 14 e = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end e.each do |i| break if 100 < i puts i end ■ (8) 《再掲》 Enumerator の例 fib = Fiber.new do a = 0; b = 1 loop do a, b = b, a + b Fiber.yield a end end def fib.each loop do yield self.resume end end fib.each do |i| break if 100 > i puts i end ■ 《参考》 (9) Fiberの例 Enumerator は Fiber を活用するデザインパターンの1つ Fiber における難しい概念を隠ぺい Fiber.yield => Enumerator::Yielder#<< 、 Enumerator#each による列挙 Enumerator を使いこなせれば、十分 Fiber のメリットを活用できる 《参考: Fiber にできて、Enumerator でできないこと》 親 Fiber から 子Fiber へのデータの送付(Fiber.yield の返り値の利用) これが必要。 だけど、これが 難しい。(汗) Enumerator の 内部実装まで 考えなくていい
  • 16. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 Enumerator はベンリ 15 fib = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end fib.each do |i| break if 100 < I puts i end fib.each_cons(2) do |i, j| break if 100 < i puts "#{i} #{j}" end ■ (10) Enumerator の例 def fibonacci a = 0; b = 1 loop do a, b = b, a + b yield a end end fibonacci do |i| break if 100 < i puts i end prev = nil fibonacci do |i| break if 100 < i puts "#{prev} #{i}" if prev prev = i end ■ 《参考》 (11) ブロック付メソッド呼び出しの例 Enumerator なら Enumerable の便利メソッドを使える。 ブロック付メソッド呼び出しは単純なイテレーションならいいけど。。。 Enumerator はオブジェクトなので、使い回しが簡単。 引数にしたり、返り値にしたり、インスタンス変数に格納したり Enumerator オブジェクトの生成も慣れれば簡単。 慣れれば、 カンタン 面倒 ! ! Enumerable の便利メソッド を使い放題 単純なイテレーション ならいいけど・・・。
  • 17. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 Enumerable#lazy の利用 16 fib = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end fib.select do |i| i < 100 end. take(10).each do |i| puts i end ■ (12) うまく動かない例 fib = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end fib.lazy.select do |i| i < 100 end.take(10).each do |i| puts i end ■ (13) Enumerable#lazy を利用する例 Ruby 2.0.0 から、Enumerable#lazy が利用可能となる Enumerable#lazy を使うと、遅延評価になり、無限を無限のまま扱える 宇宙ヤバい Ruby 1.9 でも gem でインストール可能 @yhara さんの作品 ここで、無限ループに なり、うまく動かない。 ※ take_while を 使う方法もある。 Enumerable#lazy を使えば、正しく 期待通りに動作する
  • 19. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 Agent でのFiber の利用例 Agent ゲームのキャラクターごとに Fiber に対応させ、フレームごと に動作させるというのが自然に実装できる。 下記の例はささださんの発表そのまま ただし、私自身はゲーム制作などしないし・・・。 18 enemy = Fiber.new do loop do move_up Fiber.yield move_right Fiber.yield move_down Fiber.yield move_left Fiber.yield end end 内部状態があり、状態遷移している。 1フレームごとに、順に異なる動作をするという ことを自然に実装できる ゲームのキャラの動きなどに使うとベンリ。
  • 20. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 IO での Fiber の利用 m_seki さんの発表はこの例 正直、ネットワークIO に Fiber は向かないと感じる この点で、せきさんに同意 理由 IO#read_nonblock を使い、改行区切りの処理を自分で記述す る必要がある そもそも、Ruby の Thread は IOブロックがあると、別スレッ ドに切り替わる。 IO 処理の場合は、IO 自体がパフォーマンス上のボトルネック Fiber によるパフォーマンス向上効果は限定的 [要出典] C10K 問題が顕在化している場合は別かもしれないが・・・。 19
  • 21. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 GitHub での実事例の調査結果 GitHub で Ruby 言語で Fiber で検索してみた Fiber の練習用の実装がほとんど。 なんとなく使ってそうなプロジェクトを調査。 Concurrent Ruby 使ってなかった。(CRuby に限定しないため) Fluentd 使ってなかった。 Celluloid::IO Celluloid::Task::Fibered というのがあったが詳細不明 逆に使っているプロジェクト em-synchrony 前ページで個人的に否定していたネットワークIO が主な用途。。 20
  • 22. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 思考実験 あえて Fiber がネットワークIO で有効な状況を想像 HTTP サーバで Fiber を利用 クライアントが非常に低速(モバイル回線など) 1つのサーバに非常に多数のクライアントが接続する クライアントあたり1スレッド占有されると困る サーバ資源にかなり余裕があるのに、DoS 状態になる ある意味アタック Thread ではメモリ資源などの理由で困難だが、 Fiber ならカジュアルにワーカを増減できる [要出典] 多数の低速クライアントなどの状況でもなんとかなる? 21
  • 23. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 とはいえ、、、 ふつう、Nginx と組合わせて使うよね・・・? HTTP なら低速クライアントが多数でも問題ないはず ほかのプロトコルなら状況違うかも(識者おしえて) 22 ブラウザ アプリケーションサーバ 低速だから 時間かかる 待ちっぱなしで ワーカ占有 ブラウザ NGINX アプリケーションサーバ NGINX に送信完了 で、仕事終わり ワーカが解放される NGINX がよしなに 処理してくれる
  • 24. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 ほか、事例 自分が過去、Fiber の実用的な使ったことがあることを 思い出した。 23
  • 25. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 24 以下、2013年の amagasakirb での発表の引用 「Ruby と LLVM 」
  • 26. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 Parslet と Ruby-LLVM の 夢の競演 25
  • 27. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 Parslet とは Parslet PEG (Parsing Expression Grammer) パーサ生成ライブラリ Ruby の内部 DSL で PEG を表現する。 有力な対抗馬 TreeTop: Rubyっぽい独自言語を使う citrus: Ruby っぽい独自言語を使う rsec:Rubyの内部DSL だけど、なんかイマイチ。 強力な点 エラーレポート機能に注力 テストしやすいコード parser と transformer の2層に明確に分離されている 抽象構文木が慣れ親しんだ Ruby のハッシュで表現されている 26
  • 28. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 Parslet で電卓を実装してみる 仕様 計算できるのは、Int だけ 計算できる演算は +、-、*、/ の4つだけ 答えももちろん Int だけ *、/ は +、- より優先順位が高い 出力、計算 Ruby-LLVM で LLVM のコードを出力させる 実際の計算はしないつもり 工夫している点 BasicBlock#build の引数のブロック内で、 LLVM の命令を追加していくことが必要。 この仕様は Transformer の rule と相性ワルし! BasicBlock#build を別の Fiber で実行することで解決! 27
  • 29. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 電卓の Parser の実装 28 class Parser < Parslet::Parser rule(:space) { match('¥s').repeat(1) } rule(:sp) { space.maybe } rule(:integer) { match('[0-9]').repeat(1).as(:int) } rule(:mul) { integer.as(:left) >> sp >> match('[*/]').as(:op) >> sp >> multiplication.as(:right) } rule(:add) { multiplication.as(:left) >> sp >> match('[+-]').as(:op) >> sp >> addition.as(:right) } rule(:multiplication) { mul | integer } rule(:addition) { add | multiplication } rule(:expression) { addition.as(:expr) } root :expression end
  • 30. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 電卓の Transformer の実装 LLVM 関係の処理は、LLVMBuilder クラスに外出し rule を記述するだけで比較的簡単に実装可能 29 class LLVMTransformer <Parslet::Transform b = @@builder = LLVMBuilder.new rule(:int => simple(:n)){ b.int n } rule(:left => simple(:l), :right => simple(:r), :op => simple(:op)){ b.calc op, l, r } rule(:expr => simple(x)){ b.ret x } def do(tree) apply(tree) @@builder.dump end end パーサによって生成された "int" 要素を処理。 LLVM の整数に変換。 パーサによって生成された "left"、 "right"、 "op" の 要素を持つサブツリーに対して 処理を行う。 LLVM の命令に変換する。
  • 31. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 電卓の LLVMBuilder の実装 LLVMBuilder では LLVM の命令を生成する。 building_loop で rule からの指示を待つ 30 class LLVMBuilder def initialize @fiber = Fiber.new do @module = LLVM::Module.new("calculator") @module.functions.add("add", [], LLVM::Int) do |f,| bb = f.basic_blocks.append("entry") bb.build do |builder| building_loop builder end end end @fiber.resume end ... end 新しい Fiber を生成している。 1. Module の生成 2. Function の追加 3. Basic Block の追加 4. Builder を使い、LLVM 命令を追加 building_loop まで Fiber を実行させておく。
  • 32. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 電卓の LLVMBuilder の実装 31 def building_loop builder llvm_ir = nil loop do op, l, r = Fiber.yield llvm_ir case op when "+" llvm_ir = builder.add l, r when "-" llvm_ir = builder.sub l, r when '*' llvm_ir = builder.mul l, r when "/" llvm_ir = builder.sdiv l, r when :exit llvm_ir = builder.ret l break end end end Fiber から呼出し元への返り値みたいなもの。 Fiber なので、Fiber.yield の引数で処理結果を 渡して呼出し元に処理を戻す。 呼出し元から来る引数みたいなもの。 Fiber なので、Fiber.yield の返り値で 呼出し元から受け取る。 四則演算の LLVM の命令を生成 計算結果を return する LLVM の命令を生成
  • 33. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 電卓の LLVMBuilder の実装 Fiber の処理を calc 、ret というメソッドで隠ぺい calc +、-、×、÷ の四則演算を LLVM の処理に変換する ret 処理結果を LLVM の関数の返り値にする。 32 def int n LLVM.Int n.to_i end def calc op, l, r @fiber.resume op, l, r end def ret x @fiber.resume :exit, x end def dump @module.dump end Fiber の外から、演算を受け取って、 Fiber に渡す。 さらに Fiber での処理結果を返す。 生成された LLVM の計算プログラムを 出力する。 Ruby の整数を LLVM の整数に 変換する。 処理を終了し、演算結果を返り値 として返す
  • 34. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 33 引用終わり
  • 35. Ebisu.rb #12 発表資料 「Fiber の使いどころ」 まとめ RubyKaigi では Fiber に関連した発表が2つあった m_seki さんが「Fiber より Thread 推奨」と言ってた 調査したが、ネットワークIO 程度しか、有効な事例は 見つからなかった が、Ruby の Thread は IO でブロックされず、切り替わる IO の多重化については Ruby の Thread で十分満足している Nginx で多重化処理させるテクもある。 ただし私自身、過去 Fiber の有効な活用事例があった ブロック引数を必要とするライブラリを使いたい けど、そのブロックの外から処理を与えたい Fiber 使えば、すっきり! そういえば、Ruby 1.9 で話題だったマルチVM って、 どうなったのか詳しい人おしえてください。 34