Skip to content

Instantly share code, notes, and snippets.

@sirex
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sirex/2b930806989e1a8483d7 to your computer and use it in GitHub Desktop.
Save sirex/2b930806989e1a8483d7 to your computer and use it in GitHub Desktop.
VilniusPy #1: asyncio
/VilniusPy-1-asyncio.slides.html
/reveal.js/
/.ipynb_checkpoints/
.prompt {
display: none;
}
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
Display the source blob
Display the rendered blob
Raw
{
"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