Detailing Python's Coroutine out of History

Detailing Python's Coroutine out of History

0bafbcfcfb548a4ac20406692858f68b?s=128

Moriyoshi Koizumi

May 30, 2019
Tweet

Transcript

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

  2. 私とは • Moriyoshi Koizumi • Twitter @moriyoshit / GitHub @Moriyoshi

    • PHPのコミッター (ほぼ廃業) / Goのコントリビュータなど • オープンコレクター (https://open-c.jp/) という会社をやって います
  3. 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
  4. Pythonの非同期I/Oの歴史

  5. 黎明期 • 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
  6. asyncore (1996-) • Python 1.4? • Sam Rushingによる • コールバックベースのAPI

    • 3.6 でdeprecated、PEP 594でdead battery宣告
  7. Medusa (1996-) • http://www.nightmare.com/medusa/ • Sam Rushingによる • Python 1.5

    (1997/12) 時代の非同期I/Oライブラリ • asyncoreベース • スタンフォードの研究プロジェクト時代のGoogleで使われてい た “google.stanford.edu” • 他にはZopeなど
  8. Twisted (2002-) • Glyph Lefkowitzによる • コールバックベースのAPI • asyncoreの設計を見直し、幅広いプロトコルのサポート •

    商用オンラインゲーム、Buildbotなどに採用
  9. Tornado (2009-) • Ben Darnell / Bret Taylorによる • 当初はコールバック

    / 継続渡しベース • のちにジェネレータを使った疑似コルーチンを導入 (2011-)
  10. eventlet (2008-) / gevent (2009-) • eventlet • Second Lifeを運営するLinden

    Labによる • greenletベースの非同期I/Oライブラリ • gevent • Denis Bilenkoによる • Greenletベースの非同期I/Oライブラリ • eventletの改善版として開発
  11. Tulip a.k.a. asyncio (2011-) • PEP 3156 • async SIGで開発

    • Twistedのプロトコル抽象化の設計を参考に、 PEP 380で導入されたジェネレータの移譲を用いて近代的な設 計にすることを主旨 • Python 3.4で標準モジュール化
  12. Pythonのコルーチンの歴史

  13. 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?
  14. Stackless Python (PEP 219; 2000) • このスレッドではコルーチンの対称性についてや、Schemeの call/ccとコルーチンの比較も議論される • Christian

    Tismerが議論を進めながら いきなり実装を作ってしまったのがStackless Python • ランタイム自体に変更を加え、コルーチンの実行を可能にした もの (ABIの互換性がない)
  15. Stackless Python (PEP 219; 2000) • Tasklet • 協調的スレッドで、コルーチンではない (暗黙のスケジューラが存在)

    • Channel • Tasklet間のコミュニケーションに使えるキュー
  16. 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()
  17. Simple Generator (PEP 255, 2001) • ジェネレータの基本文法 • Python 2.2で導入

    • Neil Schemenauer / Tim Peters / Magnus Lie Hetlandらによ る • Sather / CLU / Iconの影響に言及
  18. Simple Generatorのコード例 def gen(): for i in range(0, 10): yield

    i * 2 for i in gen(): print(i)
  19. Coroutines via Enhanced Generator (PEP 342; 2005) • PEP 288

    (Generators Attributes and Exceptions) / PEP 325 (Generators Attributes and Exceptions) の提案を元に、 • yieldを式の中で扱えるように • 呼び出し元からジェネレータの中に値を送れるように • 呼び出し先に例外を返せるように • try-finally節の中でyieldできるように したもの
  20. 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 <module> 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
  21. 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.
  22. 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)
  23. Syntax for Delegating to Subgenerator (PEP 380; 2011) • yield

    from構文の導入 • 2009年に提案され、asyncioの開発に伴ってGuidoにより承認
  24. 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]
  25. asyncio with async / await syntax (PEP 492; 2015) •

    PEP 492 • Yury Selivanovによる • C# / ECMAScript 7 stage 3の提案の影響についての言及 • Python 3.5
  26. 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())
  27. asyncio with async / await syntax (PEP 492; 2015) await

    foo() i += 1 asyncio.sleep() await foo() i += 1 asyncio.sleep() • メソッドをawaitの呼び出しの単位で細 切りにする • 細切りの単位でジェネレータを進めて いく • async関数の呼び出しはジェネレータ の移譲と同じ イベントループ
  28. まとめ • Pythonにおけるコルーチンの議論は、ジェネレータの導入以前 から存在していた!!!! • コルーチンの議論を先に推し進めていたのは、 非同期I/Oをコールバックや継続渡しではなく、外部イテレー タ的に実現するという現実的な欲求だった!!!! • 本物*のコルーチンを実装しているのはgreenletだけ!!!!

    *先に紹介したStackless PythonのスレッドでTim Petersが言った言葉