Skip to content

Experiments with asynchronous programming techniques in Python.

Notifications You must be signed in to change notification settings

kwentine/pyasync

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Some experiments with asynchronous programming in Python, largely inspired by the following great resources:

The general idea is to take some very simple network application senarios, implement synchronous versions, and gradually make changes to turn them asynchronous exploring various techniques and tradeoffs along the way (threads, callback style, coroutines…) Finally, provide corresponding variants using asyncio’s event loop and APIs.

Fibonacci servers

The fibservers directory contains incrementals version of a simple TCP server computing Fibonacci numbers, gradully going from synchronous to async. Each version of the server comes with an equivalent implementation using asyncio’s event loop and APIs.

  • fibservsync.py

    Synchronous version. Handles one connexion at a time.

  • fibservthrd.py

    Introduce concurrency by handling each incoming connexion in a separate thread.

  • fibservcb.py

    First single-threaded async version, using callbacks and I/O polling with the standard library’s selectors module.

  • fibservcoro.py

    Use coroutines to implement tasks, driven by a simple event loop.

  • fibservcorothrd.py

    Asynchronous I/O handled by an event loop in the main thread, and computation of Fibonacci numbers delegated to a ThreadPoolExecutor.

Asyncio variants

Performance assessment

In addition to these, I adapted David Beazley’s client implementations to play around and test performance:

  • fibclientfast.py

    Repeatedly send short-time requests and monitor the requests-per-second rate.

  • fibclientslow.py

    Send comparatively CPU-demanding requests (computing Fib(33)) and monitor the time-per-request.

Using various combinations of these allows to get an idea of some tradeoffs involved when doing asynchronous programming in Python.

Here are the results of the tests performed on my machine:

Server version1 fast2 fast1 slow2 slowfast + slowfast + slow
req/sreq/ss/reqs/req(fast) req/sslow (s/rec)
Synchronous38000X1XXX
Threads340001700012.11001
Callbacks33000200001.12.111.1
Coroutines32000160001.12.211.1
Coroutines and threads800040001.12.2301.1
Asyncio
Callbacks30000180001211
Coroutines2000090001211
Coroutines and threads5000300012501
Streams4500260012401

Presentation

Some notes in French of a short presentation I gave on the topic, and the corresponding reveal.js export (generated by org-reveal) lie in the presentation directory, along with some drawings.

Simplified event loop

While reading asyncio code, I started out to implement a simplified version of the event loop, tasks and futures, while mimicking the API. It tries to capture the essential logic, while overlooking error handling and many other subtleties. This work in progress is in the eventloop directory.

Web clients

The webclients directory follows the same incremental approach, focusing on the simple task of concurrently fetching a list of URLs. Work in progress, also integrating some Tornado examples.

About

Experiments with asynchronous programming techniques in Python.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published