Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Detailing Python's Coroutine out of History

Detailing Python's Coroutine out of History

Moriyoshi Koizumi

May 30, 2019
Tweet

More Decks by Moriyoshi Koizumi

Other Decks in Technology

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が言った言葉