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

    View full-size slide

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

    View full-size slide

  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

    View full-size slide

  4. Pythonの非同期I/Oの歴史

    View full-size slide

  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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  12. Pythonのコルーチンの歴史

    View full-size slide

  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?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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()

    View full-size slide

  17. Simple Generator (PEP 255, 2001)
    • ジェネレータの基本文法
    • Python 2.2で導入
    • Neil Schemenauer / Tim Peters / Magnus Lie Hetlandらによ

    • Sather / CLU / Iconの影響に言及

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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
    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

    View full-size slide

  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.

    View full-size slide

  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)

    View full-size slide

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

    View full-size slide

  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]

    View full-size slide

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

    View full-size slide

  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())

    View full-size slide

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

    View full-size slide

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

    View full-size slide