Slide 1

Slide 1 text

歴史からひもとく Pythonの「コルーチン」 Moriyoshi Koizumi

Slide 2

Slide 2 text

私とは • Moriyoshi Koizumi • Twitter @moriyoshit / GitHub @Moriyoshi • PHPのコミッター (ほぼ廃業) / Goのコントリビュータなど • オープンコレクター (https://open-c.jp/) という会社をやって います

Slide 3

Slide 3 text

Timeline (199x-2020) 1998 2018 2000 2002 2004 2006 2008 2010 2012 2014 2016 2020 XMLHTTPRequest Medusa Safari (2003) The C10K problem (1999) Twisted Tornado C# 5.0 Python 1.5.2 Twitter ES7 async/await async / await (PEP 492) Tulip aka asyncio (PEP 3156) greenlet Stackless Python (PEP 219) meinheld gevent Enhanced Generator yield from Flickr Hack

Slide 4

Slide 4 text

Pythonの非同期I/Oの歴史

Slide 5

Slide 5 text

黎明期 • selectモジュール • おそらくPython 0.9.8 (1993/1) で入った https://hg.python.org/cpython-fullhistory/rev/a4793ae26682 • (参考) threadモジュール • おそらくPython 0.9.8 (1993/1) で入った https://hg.python.org/cpython-fullhistory/rev/72aceed365d4

Slide 6

Slide 6 text

asyncore (1996-) • Python 1.4? • Sam Rushingによる • コールバックベースのAPI • 3.6 でdeprecated、PEP 594でdead battery宣告

Slide 7

Slide 7 text

Medusa (1996-) • http://www.nightmare.com/medusa/ • Sam Rushingによる • Python 1.5 (1997/12) 時代の非同期I/Oライブラリ • asyncoreベース • スタンフォードの研究プロジェクト時代のGoogleで使われてい た “google.stanford.edu” • 他にはZopeなど

Slide 8

Slide 8 text

Twisted (2002-) • Glyph Lefkowitzによる • コールバックベースのAPI • asyncoreの設計を見直し、幅広いプロトコルのサポート • 商用オンラインゲーム、Buildbotなどに採用

Slide 9

Slide 9 text

Tornado (2009-) • Ben Darnell / Bret Taylorによる • 当初はコールバック / 継続渡しベース • のちにジェネレータを使った疑似コルーチンを導入 (2011-)

Slide 10

Slide 10 text

eventlet (2008-) / gevent (2009-) • eventlet • Second Lifeを運営するLinden Labによる • greenletベースの非同期I/Oライブラリ • gevent • Denis Bilenkoによる • Greenletベースの非同期I/Oライブラリ • eventletの改善版として開発

Slide 11

Slide 11 text

Tulip a.k.a. asyncio (2011-) • PEP 3156 • async SIGで開発 • Twistedのプロトコル抽象化の設計を参考に、 PEP 380で導入されたジェネレータの移譲を用いて近代的な設 計にすることを主旨 • Python 3.4で標準モジュール化

Slide 12

Slide 12 text

Pythonのコルーチンの歴史

Slide 13

Slide 13 text

Stackless Python (PEP 219; 2000) Sam Rushingの1999年のメール: Subject: ‘stackless’ python? Date: Thu May 13, 1999 I'm not sure if this has been brought up before in other forums, but has there been discussion of separating the Python and C invocationstacks, (i.e., removing recursive calls to the intepreter) tofacilitate coroutines or first-class continuations?

Slide 14

Slide 14 text

Stackless Python (PEP 219; 2000) • このスレッドではコルーチンの対称性についてや、Schemeの call/ccとコルーチンの比較も議論される • Christian Tismerが議論を進めながら いきなり実装を作ってしまったのがStackless Python • ランタイム自体に変更を加え、コルーチンの実行を可能にした もの (ABIの互換性がない)

Slide 15

Slide 15 text

Stackless Python (PEP 219; 2000) • Tasklet • 協調的スレッドで、コルーチンではない (暗黙のスケジューラが存在) • Channel • Tasklet間のコミュニケーションに使えるキュー

Slide 16

Slide 16 text

Stackless Pythonのコード例 import stackless @stackless.tasklet def ping(c1, c2): v = c2.receive() print("ping received {}".format(v)) c1.send(v + 1) @stackless.tasklet def pong(c1, c2): v = c1.receive() print("pong received {}".format(v)) c2.send(v + 1) c1 = stackless.channel() c2 = stackless.channel() ping(c1, c2) pong(c1, c2) c1.send(1) stackless.run()

Slide 17

Slide 17 text

Simple Generator (PEP 255, 2001) • ジェネレータの基本文法 • Python 2.2で導入 • Neil Schemenauer / Tim Peters / Magnus Lie Hetlandらによ る • Sather / CLU / Iconの影響に言及

Slide 18

Slide 18 text

Simple Generatorのコード例 def gen(): for i in range(0, 10): yield i * 2 for i in gen(): print(i)

Slide 19

Slide 19 text

Coroutines via Enhanced Generator (PEP 342; 2005) • PEP 288 (Generators Attributes and Exceptions) / PEP 325 (Generators Attributes and Exceptions) の提案を元に、 • yieldを式の中で扱えるように • 呼び出し元からジェネレータの中に値を送れるように • 呼び出し先に例外を返せるように • try-finally節の中でyieldできるように したもの

Slide 20

Slide 20 text

Coroutines via Enhanced Generator (PEP 342; 2005) def ping(): v = yield pong_g print("ping received {}".format(v)) pong_g.send(v + 1) def pong(): v = yield ping_g print("pong received {}".format(v)) pong_g.send(v + 1) ping_g = ping() pong_g = pong() next(ping_g) next(pong_g) ping_g.send(1) ping received 1 pong received 2 Traceback (most recent call last): File "test.py", line 17, in ping_g.send(1) File "test.py", line 4, in ping pong_g.send(v + 1) File "test.py", line 10, in pong pong_g.send(v + 1) ValueError: generator already executing

Slide 21

Slide 21 text

greenlet (2006) • Alexey Borzenkovによる • Stackless Pythonは処理系自体を置き換えたもの • greenletは標準のPythonの一パッケージとして扱える • Stackless Pythonは本物のコルーチンを実装できるにもかかわ らず、コルーチンとしてtaskletを扱えるAPIを提供していな かった →greenletの開発のモチベーションの一つ A “greenlet”, on the other hand, is a still more primitive notion of micro-thread with no implicit scheduling; coroutines, in other words. This is useful when you want to control exactly when your code runs.

Slide 22

Slide 22 text

greenlet (2006) import greenlet @greenlet.greenlet def ping(v): while True: print("ping received {}".format(v)) v = pong.switch(v + 1) @greenlet.greenlet def pong(v): while True: print("pong received {}".format(v)) v = ping.switch(v + 1) ping.switch(0)

Slide 23

Slide 23 text

Syntax for Delegating to Subgenerator (PEP 380; 2011) • yield from構文の導入 • 2009年に提案され、asyncioの開発に伴ってGuidoにより承認

Slide 24

Slide 24 text

Syntax for Delegating to Subgenerator (PEP 380; 2011) def m(): yield from sub(1) yield from sub(2) def sub(c): for i in range(3): yield c * I print(list(m())) [0, 1, 2, 0, 2, 4]

Slide 25

Slide 25 text

asyncio with async / await syntax (PEP 492; 2015) • PEP 492 • Yury Selivanovによる • C# / ECMAScript 7 stage 3の提案の影響についての言及 • Python 3.5

Slide 26

Slide 26 text

asyncio with async / await syntax (PEP 492; 2015) import asyncio c = 0 async def foo(): global c c += 1 asyncio.sleep(1) return c async def bar(): print(await foo()) print(await foo()) print(await foo()) asyncio.get_event_loop().run_until_complete(bar())

Slide 27

Slide 27 text

asyncio with async / await syntax (PEP 492; 2015) await foo() i += 1 asyncio.sleep() await foo() i += 1 asyncio.sleep() • メソッドをawaitの呼び出しの単位で細 切りにする • 細切りの単位でジェネレータを進めて いく • async関数の呼び出しはジェネレータ の移譲と同じ イベントループ

Slide 28

Slide 28 text

まとめ • Pythonにおけるコルーチンの議論は、ジェネレータの導入以前 から存在していた!!!! • コルーチンの議論を先に推し進めていたのは、 非同期I/Oをコールバックや継続渡しではなく、外部イテレー タ的に実現するという現実的な欲求だった!!!! • 本物*のコルーチンを実装しているのはgreenletだけ!!!! *先に紹介したStackless PythonのスレッドでTim Petersが言った言葉