Last active
August 29, 2015 14:17
-
-
Save sirex/2b930806989e1a8483d7 to your computer and use it in GitHub Desktop.
VilniusPy #1: asyncio
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/VilniusPy-1-asyncio.slides.html | |
/reveal.js/ | |
/.ipynb_checkpoints/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.prompt { | |
display: none; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
REVEAL_VERSION = 2.6.2 | |
VilniusPy-1-asyncio.slides.html: reveal.js VilniusPy-1-asyncio.ipynb custom.css | |
ipython nbconvert --to slides VilniusPy-1-asyncio.ipynb | |
reveal.js: | |
wget https://github.com/hakimel/reveal.js/archive/$(REVEAL_VERSION).zip | |
unzip $(REVEAL_VERSION).zip | |
mv reveal.js-$(REVEAL_VERSION) reveal.js | |
rm $(REVEAL_VERSION).zip | |
run: | |
ipython nbconvert --to slides VilniusPy-1-asyncio.ipynb --post serve | |
.PHONY: run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "skip" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"import asyncio\n", | |
"import aiohttp\n", | |
"\n", | |
"loop = asyncio.get_event_loop()\n", | |
"\n", | |
"presenter = {'name': 'Mantas Zimnickas'}" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"import asyncio" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 66, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"{'name': 'Mantas Zimnickas'}\n" | |
] | |
} | |
], | |
"source": [ | |
"print(presenter)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Concurrent vs Parallel vs Asynchronous" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"- **Concurrency** is about *dealing with* lots of things at once [1].\n", | |
"- Possible ways to deal with *concurrency*:\n", | |
" - Parallelisation.\n", | |
" - Asynchronous IO." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Parallelisation" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"- **Parallelisation** is about *doing* lots of things at once[1].\n", | |
"- **Parallelisation** helps to solve CPU-bound performance issues.\n", | |
"- Parallelisation tools in Python's standard library:\n", | |
" \n", | |
"```python\n", | |
"import multiprocessing\n", | |
"import threading\n", | |
"import concurrent.futures\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Asynchronous IO" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"- **Asynchronous IO** is about *doing* IO processing in a non-blocking way [2].\n", | |
"- **Asynchronous IO** helps to solve IO bound performance issues.\n", | |
"- Asynchornous IO tools in Python's standart library:\n", | |
"\n", | |
"```python\n", | |
"import asyncio\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# What is IO-bound?\n", | |
"\n", | |
"Below is a list of possible reasons for IO-bound performance issues:\n", | |
"\n", | |
"* Reading/writing to a file.\n", | |
"* Sending/receiving data over the network.\n", | |
"* Executing database queries.\n", | |
"* Executing processes and comunicating with them over pipes.\n", | |
"* Waiting for user input from `sys.stdin`.\n", | |
"* Writing output to `sys.stdout` or `sys.stderr`." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# History of asynchronous IO in python\n", | |
"\n", | |
"* 2001 - PEP 255: Simple generators (`yield` statement)\n", | |
"* 2002 - Twisted\n", | |
"* 2009 - Gevent\n", | |
"* 2010 - Tornado\n", | |
"* 2010 - PEP 3153: Asynchronous IO support\n", | |
"* 2012 - PEP 3156: Asynchronous IO Support Rebooted: the \"asyncio\" Module\n", | |
"* 2014 - Tulip\n", | |
"* 2014 - asyncio" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Two types of asyncio users\n", | |
"\n", | |
"* Those who use high level asyncio coroutines.\n", | |
"* Those who create new coroutines using low level asyncio primitives." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Callbacks" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2 + 2 = 4\n" | |
] | |
} | |
], | |
"source": [ | |
"from functools import partial\n", | |
"from concurrent.futures import ThreadPoolExecutor\n", | |
"\n", | |
"def add(a, b):\n", | |
" return a + b\n", | |
"\n", | |
"def done(a, b, future):\n", | |
" print('%s + %s = %s' % (a, b, future.result()))\n", | |
" \n", | |
"with ThreadPoolExecutor(max_workers=1) as pool:\n", | |
" future = pool.submit(add, 2, 2)\n", | |
" future.add_done_callback(partial(done, 2, 2))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Using coroutines" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2 + 2 = 4\n" | |
] | |
} | |
], | |
"source": [ | |
"def add(a, b):\n", | |
" return a + b\n", | |
"\n", | |
"def main():\n", | |
" print('2 + 2 =', add(2, 2))\n", | |
" \n", | |
"main()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2 + 2 = 4\n" | |
] | |
} | |
], | |
"source": [ | |
"@asyncio.coroutine\n", | |
"def add(a, b):\n", | |
" return a + b\n", | |
"\n", | |
"@asyncio.coroutine\n", | |
"def main():\n", | |
" print('2 + 2 =', (yield from add(2, 2)))\n", | |
"\n", | |
"loop.run_until_complete(main())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Using coroutines: aiohttp" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"python.org size is: 46947\n" | |
] | |
} | |
], | |
"source": [ | |
"import aiohttp\n", | |
"\n", | |
"@asyncio.coroutine\n", | |
"def get_size(url):\n", | |
" response = yield from aiohttp.request('GET', url)\n", | |
" return len((yield from response.read()))\n", | |
"\n", | |
"@asyncio.coroutine\n", | |
"def main():\n", | |
" size = yield from get_size('https://python.org/')\n", | |
" print('python.org size is:', size)\n", | |
" \n", | |
"loop.run_until_complete(main())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"source": [ | |
"* This example works much faster without `asyncio`.\n", | |
"* `asyncio` shines with many concurent input/output." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Using coroutines: aiohttp" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 23, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"[46947]" | |
] | |
}, | |
"execution_count": 23, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"@asyncio.coroutine\n", | |
"def get_size(semaphore, url):\n", | |
" with (yield from semaphore):\n", | |
" response = yield from aiohttp.request('GET', url)\n", | |
" return len((yield from response.read()))\n", | |
" \n", | |
"@asyncio.coroutine\n", | |
"def main(urls):\n", | |
" semaphore = asyncio.Semaphore(3)\n", | |
" sizes = [get_size(semaphore, url) for url in urls]\n", | |
" return [(yield from size) for size in asyncio.as_completed(sizes)]\n", | |
"\n", | |
"urls = ['https://python.org/']\n", | |
"loop.run_until_complete(main(urls)) " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Using coroutines: aiohttp.web" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"<asyncio.base_events.Server at 0x7f485c0ad198>" | |
] | |
}, | |
"execution_count": 5, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"from aiohttp import web\n", | |
"\n", | |
"@asyncio.coroutine\n", | |
"def handle(request):\n", | |
" return web.Response(body=b'Hello world.')\n", | |
"\n", | |
"@asyncio.coroutine\n", | |
"def init(loop):\n", | |
" app = web.Application(loop=loop)\n", | |
" app.router.add_route('GET', '/', handle)\n", | |
" return (yield from loop.create_server(app.make_handler(), '127.0.0.1', 8080))\n", | |
"\n", | |
"loop.run_until_complete(init(loop))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Generator" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 131, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"1\n", | |
"2\n", | |
"3\n" | |
] | |
} | |
], | |
"source": [ | |
"def generator():\n", | |
" yield 1\n", | |
" yield 2\n", | |
" return 3\n", | |
" \n", | |
"def wrapper():\n", | |
" yield (yield from generator())\n", | |
"\n", | |
"for x in wrapper():\n", | |
" print(x)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Coroutine\n", | |
"\n", | |
"* In python a coroutine is same thing as generator.\n", | |
"* Coroutine is a function with multiple entry points for suspending and resuming execution at certain locations [4]." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Coroutine" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 123, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def coroutine():\n", | |
" print('b:', (yield 1))\n", | |
" print('b:', (yield 3))\n", | |
" print('b:', (yield 5))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 124, | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"a = coroutine()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 125, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"a: 1\n" | |
] | |
} | |
], | |
"source": [ | |
"print('a:', next(a))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 126, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"b: 2\n", | |
"a: 3\n" | |
] | |
} | |
], | |
"source": [ | |
"print('a:', a.send(2))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 127, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"b: 4\n", | |
"a: 5\n" | |
] | |
} | |
], | |
"source": [ | |
"print('a:', a.send(4))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# asyncio coroutine" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 17, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"@asyncio.coroutine\n", | |
"def my_coroutine():\n", | |
" print('hello')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"function" | |
] | |
}, | |
"execution_count": 18, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"type(my_coroutine)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 19, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"generator" | |
] | |
}, | |
"execution_count": 19, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"type(my_coroutine())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 21, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"(True, True)" | |
] | |
}, | |
"execution_count": 21, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"asyncio.iscoroutinefunction(my_coroutine), asyncio.iscoroutine(my_coroutine())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Future\n", | |
"\n", | |
"* A class used to represent a result that may not be available yet [3].\n", | |
"* Also know as `deffered` from Twisted or `promise` in other libraries." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 51, | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"future = asyncio.futures.Future()\n", | |
"future.add_done_callback(lambda future: print(\"Future's done callback here.\"))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 52, | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"future.set_result(\"My result\")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 53, | |
"metadata": { | |
"collapsed": false, | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"True" | |
] | |
}, | |
"execution_count": 53, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"future.done()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 54, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"future.exception()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 55, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"'My result'" | |
] | |
}, | |
"execution_count": 55, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"future.result()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Low level coroutines" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 29, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"3" | |
] | |
}, | |
"execution_count": 29, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"busy, ready = 0, 1\n", | |
"responses = [busy, busy, ready]\n", | |
"\n", | |
"def callback(future, n):\n", | |
" if responses.pop(0) == ready:\n", | |
" future.set_result(n)\n", | |
" else:\n", | |
" loop.call_later(1, callback, future, n+1)\n", | |
" \n", | |
"def coroutine():\n", | |
" future = asyncio.futures.Future()\n", | |
" callback(future, 1)\n", | |
" return future\n", | |
"\n", | |
"loop.run_until_complete(coroutine())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# References\n", | |
"\n", | |
"- [1] Andrew Gerrand. [Concurrency is not parallelism](http://blog.golang.org/concurrency-is-not-parallelism). The Go Blog. 16 January 2013.\n", | |
"- [2] [Asynchronous I/O](http://en.wikipedia.org/wiki/Asynchronous_I/O). Wikipedia.\n", | |
"- [3] Nicholas H. Tollervey. [Asynchronous Python](http://ntoll.org/article/asyncio). Personal blog. 28 April 2014.\n", | |
"- [4] [Coroutine](http://en.wikipedia.org/wiki/Coroutine). Wikipedia." | |
] | |
} | |
], | |
"metadata": { | |
"celltoolbar": "Slideshow", | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.4.0" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment