Top Banner
1 RubyKaigi2009 Fast, simple I/O concurrency for Ruby Ruby のための、高速・簡単な並行 IO www.espace.com.eg
67

18166746-NeverBlock-RubyKaigi2009

Apr 12, 2017

Download

Documents

Muhammad Ali
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: 18166746-NeverBlock-RubyKaigi2009

1

RubyKaigi2009

Fast, simple I/O concurrency for RubyRubyのための、高速・簡単な並行 IO

www.espace.com.eg

Page 2: 18166746-NeverBlock-RubyKaigi2009

2

Helloこんにちは

Page 3: 18166746-NeverBlock-RubyKaigi2009

3

Egypt's #1 Ruby shop. Embraced Ruby (and Rails) in 2006 and it currently keeps a team of 20 talented developers

busy serving clients in 4 continents

Page 4: 18166746-NeverBlock-RubyKaigi2009

4

● Not the boxer!● eSpace's CTO● 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 ( モハメド アリ )

Page 5: 18166746-NeverBlock-RubyKaigi2009

5

NeverBlock is a Fiber based library that provides I/O concurrency facilities

NeverBlockは Fiberベースのライブラリで、非同期イベントループの複雑さを隠しつつ I/O並行性の性質をアプリケーションに提供する。

What is NeverBlock?

Page 6: 18166746-NeverBlock-RubyKaigi2009

6

How can my Ruby program perform I/O concurrently?Rubyプログラムはどうやって I/Oを並行に実行するのか?

Q

Page 7: 18166746-NeverBlock-RubyKaigi2009

7

Concurrently (並行に )? What is that?

A1

1000.times do socket = TCPSocket.open(host, port) socket.write(request) response = socket.read(chunk)...

socket.closeend

Page 8: 18166746-NeverBlock-RubyKaigi2009

8

Illustration図解

Page 9: 18166746-NeverBlock-RubyKaigi2009

9

Object 1

Page 10: 18166746-NeverBlock-RubyKaigi2009

10

● Trivial to understand and implement

とても簡単に理解できるし、実装もできる● No need to worry about synchronization

同期について考える必要がない● The fastest if your servers have zero latencies

もしサーバがレイテンシゼロなら最速

The Good

Page 11: 18166746-NeverBlock-RubyKaigi2009

11

The Bad● Introduce the slightest latency and it crawls

微少なテイテンシを持ち込み、一定幅をとり続ける

● Large latencies render it unusable

大きく遅延すると利用不能に

Page 12: 18166746-NeverBlock-RubyKaigi2009

12

1000.times do if !fork socket = TCPSocket.open(host, port) socket.write(request) response = socket.read(chunk)

... socket.close exit endendProcess.waitall

Use multiple processesマルチプロセスを使う

A2

Page 13: 18166746-NeverBlock-RubyKaigi2009

13

● Easy to understand and implement

簡単に理解し、実装できる● No synchronization needed (no shared resources)

同期処理が不要(共有リソースがない)● Scales to multiple CPUs!

マルチ CPUだとスケールする!● Scheduling (スケジューリング ) is done by the OS

The Good

Page 14: 18166746-NeverBlock-RubyKaigi2009

14

● A heavy operation that can hurt if excessively used

過度に使うと実行環境にダメージを与えるほど重い処理● Much harder if you want to share resources

リソースの共有が極めて難しい● Communication becomes a lot slower

プロセス間通信を行うと、とても遅くなる

The Bad

Page 15: 18166746-NeverBlock-RubyKaigi2009

15

www.espace.com.eg

@threads = []1000.times do @threads << Thread.new do socket = TCPSocket.open(host, port) socket.write(request) response = socket.read(chunk)

... socket.close [email protected]{|th|th.join}

Use multiple threadsマルチスレッドを使う

A3

Page 16: 18166746-NeverBlock-RubyKaigi2009

16

Illustration図解

Page 17: 18166746-NeverBlock-RubyKaigi2009

17

Object 6

Page 18: 18166746-NeverBlock-RubyKaigi2009

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

Page 19: 18166746-NeverBlock-RubyKaigi2009

19

● Synchronization for shared 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

Page 20: 18166746-NeverBlock-RubyKaigi2009

20

(reactor = Reactor::Base.new).run do 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 endend

Use an event loopイベントループを使う

A4

Page 21: 18166746-NeverBlock-RubyKaigi2009

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

Page 22: 18166746-NeverBlock-RubyKaigi2009

22

● Non continuous flow, 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

Page 23: 18166746-NeverBlock-RubyKaigi2009

23

• Processes are easy but expensive

マルチプロセスは簡単だが高くつく• Threads are cheaper but tricky

マルチスレッドは比較的低コストだがトリッキー• Event loops are the best performers but too hard

イベントループはベストな性能だが難しすぎる

In Summary (要するに )

Page 24: 18166746-NeverBlock-RubyKaigi2009

24

What about those fibers in Ruby 1.9.x?Ruby 1.9.xの fiberはどうなのだろう?

Q

Page 25: 18166746-NeverBlock-RubyKaigi2009

25

(reactor = Reactor::Base.new).run do 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 endend

Yes, Ruby 1.9.x can use fibersA

Page 26: 18166746-NeverBlock-RubyKaigi2009

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

Page 27: 18166746-NeverBlock-RubyKaigi2009

27

● Scheduling fibers is 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

Page 28: 18166746-NeverBlock-RubyKaigi2009

28

That's it? No other ways to do concurrency in Ruby?それだけ? Rubyには他の並行性のやり方は

ないのか?

Q

Page 29: 18166746-NeverBlock-RubyKaigi2009

29

● Revactor, for example, is an Actor library implemented using Fibers

例えば、 Revactorは Fiberで実装した Actorライブラ

である。● And of course, NeverBlock.

そして、勿論、 NeverBlock。

There are many (たくさんある )A

Page 30: 18166746-NeverBlock-RubyKaigi2009

30

NB::Reactor.new do 1000.times do NB::Fiber.new do socket = TCPSocket.open(host, port) socket.write(request) response = socket.read(chunk) socket.close end.resume endend

NeverBlock In Action

Page 31: 18166746-NeverBlock-RubyKaigi2009

31

● Developers can write 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

Page 32: 18166746-NeverBlock-RubyKaigi2009

32

● A bit slower than Event Loops

イベントループよりわずかに遅い

● Uses more memory

より多くのメモリを使う

● CPU operations block

CPU演算中は切り替わらない

The Bad

Page 33: 18166746-NeverBlock-RubyKaigi2009

33

Examples?例は?

Page 34: 18166746-NeverBlock-RubyKaigi2009

34

# @pipes is an 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秒ごとに書き出す

Page 35: 18166746-NeverBlock-RubyKaigi2009

35

# @images is an 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遅い外部プログラムを呼び出す

Page 36: 18166746-NeverBlock-RubyKaigi2009

36

# connecting to a 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('\r\n\r\n')

conn.close

end

end

end

end

Network I/O with timeout タイムアウトなしのネットワーク I/O

36

Page 37: 18166746-NeverBlock-RubyKaigi2009

37

# sending a large 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巨大なデータの送信

Page 38: 18166746-NeverBlock-RubyKaigi2009

38

And how does that work?どうやって動い

ている?

Q

Page 39: 18166746-NeverBlock-RubyKaigi2009

39

# the reactor supports 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で、イベントループ

Page 40: 18166746-NeverBlock-RubyKaigi2009

40

# The I/O classes 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クラスもある

Page 41: 18166746-NeverBlock-RubyKaigi2009

41

All hidden from client 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.

Page 42: 18166746-NeverBlock-RubyKaigi2009

42

Illustration図解

Page 43: 18166746-NeverBlock-RubyKaigi2009

43

Object 7

Page 44: 18166746-NeverBlock-RubyKaigi2009

44

Case Study:

Thin static file serving performance

Page 45: 18166746-NeverBlock-RubyKaigi2009

45

Thin vs Mongrel (small static files)

~200 Bytes ~1 KB ~30KB ~126KB0

500

1000

1500

2000

2500

3000

3500

MongrelThin

File Size

Req

uest

s / S

ec

Page 46: 18166746-NeverBlock-RubyKaigi2009

46

Thin vs Mongrel (large static file, ~170MB)

10/40 20/40 40/400

50

100

150

200

250

300

MongrelThin

Concurrency/Requests

MB

/sec

Page 47: 18166746-NeverBlock-RubyKaigi2009

47

# Send the response

@response.each do |chunk|

trace { chunk }

send_data chunk

end

The offending code(lib/thin/connection.rb, line #103)

Page 48: 18166746-NeverBlock-RubyKaigi2009

48

All file data will be buffered in memory before Eventmachine is given a

chance to run

The problem?

Page 49: 18166746-NeverBlock-RubyKaigi2009

49

Build a new backend that uses NeverBlock instead of EventMachine

The solution?

Page 50: 18166746-NeverBlock-RubyKaigi2009

50

Thin vs Mongrel (large static file, ~170MB)

10/40 20/40 40/400

50

100

150

200

250

300

350

MongrelThin NBThin

Concurrency / Requests

MB

/ Se

c

Page 51: 18166746-NeverBlock-RubyKaigi2009

51

What about small files?

Page 52: 18166746-NeverBlock-RubyKaigi2009

52

~200 Bytes ~1 KB ~30KB ~126KB0

5001000150020002500300035004000

MongrelThinThin NB

File Size

Req

uest

s / S

ec

Thin vs Mongrel (small static files)

Page 53: 18166746-NeverBlock-RubyKaigi2009

53

What exactly do I get with NeverBlock?を使うことで何がで

きる?

?

Page 54: 18166746-NeverBlock-RubyKaigi2009

54

● Concurrent Socket and Pipe I/O

並行 Socket & Pipe I/O● Concurrent File I/O (by delegating to threads)

並行な File I/O (スレッドへの委譲を使う )● Kernel#system, Kernel#sleep and Timeout.timeout

Supported Concurrency Featuresサポートされている並行性の特徴

Page 55: 18166746-NeverBlock-RubyKaigi2009

55

● A Fiber pool class

Fiberプールクラス● A generic connection pool class

汎用コネクションプールクラス● A reactor backend

reactorバックエンド

Other Features (その他の特徴 )

Page 56: 18166746-NeverBlock-RubyKaigi2009

56

● Net/HTTP● ActiveRecord● ActiveResource● Thin Web Server

Supported Librariesサポートしているライブラリ

Page 57: 18166746-NeverBlock-RubyKaigi2009

57

Any performance figures or benchmarks?性能に関する数値や

ベンチマークは?

Page 58: 18166746-NeverBlock-RubyKaigi2009

58

Servers with zero delay

1000/100000

2000

4000

6000

8000

10000

12000

BlockingForkingThreadedReactorNeverBlock

Concurrency / Requests

Req

uest

s / S

ec

Page 59: 18166746-NeverBlock-RubyKaigi2009

59

Servers with 10ms delay

1000/100000

2000

4000

6000

8000

10000

12000

BlockingForkingThreadedReactorNeverBlock

Concurrency / Requests

Req

uest

s / S

ec

Page 60: 18166746-NeverBlock-RubyKaigi2009

60

Servers with 1 second delay

1000/10000

50100150200250300350400450

BlockingForkingThreadedReactorNeverBlock

Concurrency / Requests

Req

uest

s / S

ec

Page 61: 18166746-NeverBlock-RubyKaigi2009

61

Findings● Reactor pattern provides highest performance● NeverBlock is generally the second fastest● There is a room for more performance with faster fiber context switching

Page 62: 18166746-NeverBlock-RubyKaigi2009

62

NeverBlock enables

Conclusiontransparent async event loops 透過的な非同期イベントループ

synchronization free threads同期フリーなマルチスレッド

automatic fiber scheduling自動的な fiberスケジューリングhigh I/O performanceハイ I/Oパフォーマンス

Page 63: 18166746-NeverBlock-RubyKaigi2009

63

Wait,there is more

もう少し聞いて。まだある。

Page 64: 18166746-NeverBlock-RubyKaigi2009

64

● Phusion Passenger support● Sequel Support● AIO support for file operations● epoll/kqueue support via ktools● Deeper integration with Ruby

Planned for NeverBlock

Page 65: 18166746-NeverBlock-RubyKaigi2009

65

● Faster fiber switching● Using (set/get)context.● Wish for context switching performance similar to Erlang processes or Stackless Python tasklets

Wish list

Page 66: 18166746-NeverBlock-RubyKaigi2009

66

● Reactor, a pure 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 eSpaceeSpaceで現在進行中のエキサイティングなプロジェクトについて紹介したい

Page 67: 18166746-NeverBlock-RubyKaigi2009

67

Thank Youشكرا�

ありがとう

● http://www.espace.com.eg/neverblock ● http://github.com/oldmoe/neverblock● http://github.com/oldmoe/mysqlplus● http://oldmoe.blogspot.com● [email protected]