3
Egypt's #1 Rubyshop. Embraced Ruby (and Rails) in 2006
and it currently keeps a team of 20 talented developers
busy serving clients in 4 continents
4.
4
●
Not the boxer!
●
eSpace'sCTO
●
11 years of C, C++, Java, PHP and JavaScript
●
Met Ruby in 2005, it's been 4 years now
●
Researching software concurrency models
●
Backed up by a wonderful team of developers
Mohammad Ali ( モハメド アリ )
5.
5
NeverBlock is aFiber based library that
provides I/O concurrency facilities
NeverBlock は Fiber ベースのライブラリで、
非同期イベントループの複雑さを隠しつつ I/O
並行性の性質をアプリケーションに提供する。
What is NeverBlock?
6.
6
How can myRuby
program perform
I/O concurrently?
Ruby プログラムは
どうやって I/O を並
行に実行するのか?
Q
7.
7
Concurrently ( 並行に)?
What is that?
A1
1000.times do
socket = TCPSocket.open(host, port)
socket.write(request)
response = socket.read(chunk)
...
socket.close
end
10
● Trivial tounderstand and implement
とても簡単に理解できるし、実装もできる
● No need to worry about synchronization
同期について考える必要がない
●
The fastest if your servers have zero latencies
もしサーバがレイテンシゼロなら最速
The Good
11.
11
The Bad
● Introducethe slightest latency and it crawls
微少なテイテンシを持ち込み、一定幅をとり続ける
● Large latencies render it unusable
大きく遅延すると利用不能に
12.
12
1000.times do
if !fork
socket= TCPSocket.open(host, port)
socket.write(request)
response = socket.read(chunk)
...
socket.close
exit
end
end
Process.waitall
Use multiple processes
マルチプロセスを使う
A2
13.
13
● Easy tounderstand and implement
簡単に理解し、実装できる
● No synchronization needed (no shared resources)
同期処理が不要(共有リソースがない)
●
Scales to multiple CPUs!
マルチ CPU だとスケールする!
●
Scheduling ( スケジューリング ) is done by the OS
The Good
14.
14
● A heavyoperation that can hurt if excessively used
過度に使うと実行環境にダメージを与えるほど重い処理
● Much harder if you want to share resources
リソースの共有が極めて難しい
●
Communication becomes a lot slower
プロセス間通信を行うと、とても遅くなる
The Bad
15.
15
www.espace.com.eg
@threads = []
1000.timesdo
@threads << Thread.new do
socket = TCPSocket.open(host, port)
socket.write(request)
response = socket.read(chunk)
...
socket.close
end
end
@threads.each{|th|th.join}
Use multiple threads
マルチスレッドを使う
A3
18
●
Simple implementations (実装しよう ) are easy
● The logic inside the thread body is straight forward
スレッド本体内のロジックは一直線
●
Creating threads is much faster than forking (~200x)
スレッド生成はフォークに比べて極めて速い(~ 200 倍)
●
Scheduling is done by the OS
●
Communication ( 間通信 ) among threads is very fast
The Good
19.
19
● Synchronization forshared data is tricky
共有データの同期はトリッキーな方法が必要
●
Big locks cause idleness and eventually deadlocks
巨大ロックはアイドル状態を生むし、場合によってはデッドロックする
● Many Tiny locks cause degradation and races
たくさんの小さなロックは性能低下とレースの原因になる
● Ruby 1.9.x does not like to have too many threads
Ruby 1.9.x は多くのスレッドを抱えることを嫌う
The Bad
20.
20
(reactor = Reactor::Base.new).rundo
1000.times do
socket = TCPSocket.open(host, port)
reactor.attach(:write, socket) do
socket.write(request)
reactor.detach(:write, socket)
reactor.attach(:read, socket) do
response = socket.read(chunk)
socket.close
reactor.detach(:read, socket)
end
end
end
end
Use an event loop
イベントループを使う
A4
21.
21
● Extremely fast,no context switching overhead
恐ろしく速いし、コンテキストスイッチのオーバーヘッドもない
● Can scale enormously (if epoll/kqueue are used)
果てしなくスケールする ( もし epoll/kqueue が使えれば )
● Uses much less system resources than threads
スレッドと比べ、ほとんどシステムリソースを使わない
The Good
22.
22
● Non continuousflow, hard to understand and use
不連続なフローになるため、把握しづらく、使いにくい
●
If a single action blocks the whole loop is blocked
1つのアクションがブロックすれば、全体がブロックされる
●
Deadlocks can happen (with very careless coding)
デッドロックが起こりうる ( とてもいい加減なコーディングの場合 )
The Bad
23.
23
• Processes areeasy but expensive
マルチプロセスは簡単だが高くつく
• Threads are cheaper but tricky
マルチスレッドは比較的低コストだがトリッキー
• Event loops are the best performers but too hard
イベントループはベストな性能だが難しすぎる
In Summary ( 要するに )
25
(reactor = Reactor::Base.new).rundo
1000.times do
Fiber.new do
fiber = Fiber.current
socket = TCPSocket.open(host, port)
reactor.attach(:write, socket){fiber.resume}
Fiber.yield
reactor.detach(:write, socket)
socket.write(request)
reactor.attach(:read, socket){fiber.resume}
Fiber.yield
reactor.detach(:read, socket)
response = socket.read(chunk)
socket.close
end.resume
end
end
Yes, Ruby 1.9.x can use fibersA
26.
26
● Very fast,switch context only when needed
とても速い。必要なときだけコンテキストを切り替える
●
Can scale almost as much as the event loop can do
ほぼイベントループ並にスケールすることができる
● Low memory overhead, 4KB stack space per fiber
メモリはも低い。 1 fiber あたり 4KB 程度のスタックスペース
● No race conditions in non I/O code
I/O 以外のコードでは競合状態が発生しない
The Good
27.
27
● Scheduling fibersis done manually
fiber のスケジューリングは手動で行う
● The event loop is still explicitly managed
イベントループは明示的に管理されている
● Recursive calls can easily run out of stack
再帰呼び出しは、簡単にスタックを使い果たす
●
Context switching (mem)copies the fiber stacks
コンテキストスイッチは fiber スタックをコピる
The Bad
28.
28
That's it? Noother
ways to do
concurrency in
Ruby?
それだけ? Ruby には
他の並行性のやり方は
ないのか?
Q
29.
29
●
Revactor, for example,is an Actor library implemented
using Fibers
例えば、 Revactor は Fiber で実装した Actor ライブラ
である。
● And of course, NeverBlock.
そして、勿論、 NeverBlock 。
There are many ( たくさんある )
A
31
● Developers canwrite normal “blocking” Ruby code
開発者は、普通の "blocking" Ruby コードを書ける
●
Scheduling is handled almost transparently
スケジューリングはほぼ透過的に扱われる
● Looks a lot like threads but without synchronization
同期しない複数のスレッドのように見える
● Generally faster than processes and threads
通常、マルチスレッドや、マルチプロセスより速い
The Good
32.
32
● A bitslower than Event Loops
イベントループよりわずかに遅い
● Uses more memory
より多くのメモリを使う
●
CPU operations block
CPU 演算中は切り替わらない
The Bad
34
# @pipes isan array of processes to ping
NB::Reactor.run do
@pipes.each do |pipe|
NB::Fiber.run do
loop do
pipe.write('ping')
sleep 3
end
end
end
end
Writing to a pipe every 3 seconds
パイプに3秒ごとに書き出す
35.
35
# @images isan array of images to process
# concurrently
NB::Reactor.run do
@images.each do |image|
NB::Fiber.run do
system('slow_processor', image.path)
end
end
end
Calling slow external programs
遅い外部プログラムを呼び出す
36.
36
# connecting toa server that might not respond in time
NB::Reactor.run do
@servers.each do |s|
NB::Fiber.run do
timeout(3) do
conn = TCPSocket.connect(s.host, s.port)
conn.write(request)
response = conn.gets('rnrn')
conn.close
end
end
end
end
Network I/O with timeout
タイムアウトなしのネットワーク I/O
36
37.
37
# sending alarge response in chunks
NB::Reactor.run do
@connections.each do |conn|
NB::Fiber.run do
response.each_chunk do |chunk|
conn.send(chunk)
end
end
end
end
Large data transfers
巨大なデータの送信
39
# the reactorsupports fiber assisted
# waiting for notifications
class NB::Reactor
def wait(mode, io)
fiber = Fiber.current
self.attach(mode, io){fiber.resume}
Fiber.yield
self.detach(mode, io)
end
end
It's all Fibers and Event loops
すべてが Fiber で、イベントループ
40.
40
# The I/Oclasses use NeverBlock for transparent
# concurrency
class MySQL
def query(sql)
send_query(sql)
# this method selects the appropriate reactor and
# calls it
NB.wait(:read, self.socket)
# the query method continues once the socket is ready
get_restult
end
end
Along with modified I/O classes
改良された I/O クラスもある
41.
41
All hidden fromclient code
クライントコードからみえない
# The query method will yield the current
# fiber till the query results are ready.
# This gives way for other fibers to run.
mysql.query(sql).each{|r|..}
# note that this requires the use of
# the MySQLPlus adapter which extends
# the original adapter with an async
# query API.
61
Findings
● Reactor patternprovides highest performance
●
NeverBlock is generally the second fastest
● There is a room for more performance with faster
fiber context switching
64
● Phusion Passengersupport
●
Sequel Support
● AIO support for file operations
●
epoll/kqueue support via ktools
● Deeper integration with Ruby
Planned for NeverBlock
65.
65
● Faster fiberswitching
●
Using (set/get)context.
● Wish for context switching performance similar to
Erlang processes or Stackless Python tasklets
Wish list
66.
66
● Reactor, apure Ruby event loop
●
Arabesque, a new Ruby queuing library
● Shihabd, the ultimate Rack server
●
And more
Wanted to a share a few exciting projects
undergoing at eSpace
eSpace で現在進行中のエキサイティングな
プロジェクトについて紹介したい