Lightweight thread performance benchmark using async / await implemented in Python 3.5

Since async / await was added in Python 3.5, I benchmarked it in terms of memory consumption and context switch cost.

What is the async / await syntax?

This is the most advanced syntax in the asynchronous processing paradigm that allows you to write asynchronous processing and non-blocking I / O processing in a nice way. After being implemented in C #, it was implemented in C ++, VB, Node.js and finally came to Python! It's like that. The feature is that asynchronous processing, which has been written hard in Threading, can be written more concisely and more powerfully. Lightweight threads are also called microthreads or fibers, and are a solution to the problem that hardware performance cannot be fully utilized when the number of clients is large due to I / O waiting called "C10K problem" (problem of 10,000 clients). It is one. By switching the context at high speed when waiting for I / O and handling other clients, you will be able to write programs that bring out the performance of the hardware to the limit. In other words, if you master it, you will be able to write a server that operates at high speed and light weight like nginx.

Benchmark results

Benchmark results show that coroutines using Python's async / await started up faster, consumed less memory, and context-switched faster. Isn't it an implementation that can be called a lightweight thread? According to the Wow Erlang book, Erlamg's lightweight thread starts up in microseconds with 4KByte of memory consumption. The Python version consumes 3.44KByte of memory and starts up in 60 microseconds, which is comparable to Erlang.

item value
Start and end times for 100,000 coroutines 6.081sec
Start and end times per coroutine 0.0608ms
Execution time of 1 million context switches 51.435sec
Execution time per context switch 51.435μs
Memory consumption when starting 100,000 coroutines 146.86MByte
Memory consumption per coroutine 1.50KByte
Memory consumption when 100,000 coroutines are started and context switch is executed 1 million times 336.30MByte
Memory consumption per context switch 1 million times 3.44KByte

Processing that Python's async / await is suitable for

Processing to perform multiple network I / O and file I / O asynchronously

Try async / await

Write a regular FizzBuzz

Ordinary FizzBuzz


def fizzbuzz(i):
    if i == 15:
        return 'FizzBuzz'
    if i % 5 == 0:
        return 'Buzz'
    if i % 3 == 0:
        return 'Fizz'
    return str(i)


for x in range(1, 10):
    print(fizzbuzz(x))

1 coroutine FizzBuzz with async / await

The result is the same as it is only running in a coroutine. スクリーンショット 2015-11-03 18.59.47.png

1 coroutine


import asyncio
async def main():
    await task_fizzbuzz()


async def task_fizzbuzz():
    for x in range(1, 10):
        print(fizzbuzz(x))
    return None

loop = asyncio.get_event_loop()
#Call the main function in a coroutine
loop.run_until_complete(main())
loop.close()

Async execution of 100,000 coroutines FizzBuzz with async / await

Generate 100,000 coroutines and execute them at the same time. When one coroutine finishes executing, the context switches to execute the next coroutine. スクリーンショット 2015-11-03 18.52.02.png

Coroutine 100,000 simultaneous execution


# -*- coding: utf-8 -*-
# from asyncio import Queue
# from queue import Queue
import asyncio

async def task_fizzbuzz(prefix):
    for x in range(1, 10):
        # await asyncio.sleep(1)
        print(prefix + "{}:".format(str(x)) + fizzbuzz(x))
    return None

loop = asyncio.get_event_loop()
#Generate 100,000 coroutines
tasks = asyncio.wait([task_fizzbuzz(str(i) + ":") for i in range(1, 100000)])
loop.run_until_complete(tasks)
loop.close()

Execution result of 100,000 coroutines


....
71798:7:7
71798:8:8
71798:9:Fizz
84034:1:1
84034:2:2
84034:3:Fizz
84034:4:4
84034:5:Buzz
84034:6:Fizz
84034:7:7
84034:8:8
84034:9:Fizz
17235:1:1
17235:2:2
....

Asynchronous execution of 100,000 coroutines with async / await with sleep

This is a test to see if context switching works well when sleeping during fizzbuzz logic.

100,000 simultaneous executions of coroutines with sleep


# -*- coding: utf-8 -*-
# from asyncio import Queue
# from queue import Queue
import asyncio

async def task_fizzbuzz(prefix):
    for x in range(1, 10):
        await asyncio.sleep(1) #Newly added code
        print(prefix + "{}:".format(str(x)) + fizzbuzz(x))
    return None

loop = asyncio.get_event_loop()
#Generate 100,000 coroutines
tasks = asyncio.wait([task_fizzbuzz(str(i) + ":") for i in range(1, 100000)])
#Parallel execution
loop.run_until_complete(tasks)
loop.close()

Execution result of 100,000 coroutines with sleep


....
75219:6:Fizz
8282:6:Fizz
57464:6:Fizz
75220:6:Fizz
8283:6:Fizz
57465:6:Fizz
75221:6:Fizz
8284:6:Fizz
57466:6:Fizz
75222:6:Fizz
8285:6:Fizz
57467:6:Fizz
75223:6:Fizz
....

When sleeping with ʻawait asyncio.sleep (1)` during coroutine execution, the result is that it is running at the same time because it switches to another coroutine. スクリーンショット 2015-11-03 19.25.56.png

Benchmark

Now that the verification is complete, we will benchmark with the following three programs.

  1. A program that runs FizzBuzz 10 times x 100,000 times without using coroutines
  2. A program that launches 100,000 coroutines and runs FizzBuzz 10 times x 100,000 times
  3. A program that executes context switches a total of 1 million times by launching 100,000 coroutines and executing Sleep for each FizzBuzz 10 times x 100,000 times.

スクリーンショット 2015-11-03 19.46.41.png

1. No coroutine

# -*- coding: utf-8 -*-
import time

def fizzbuzz(i):
    if i == 15:
        return 'FizzBuzz'
    if i % 5 == 0:
        return 'Buzz'
    if i % 3 == 0:
        return 'Fizz'
    return str(i)

COUNT = 100000
FIZZBUZZ_COUNT = 10

#Start measurement
ts = time.time()

#10 times FizzBuzz x 100,000 times
for x in range(COUNT):
    for x in range(FIZZBUZZ_COUNT):
        print(fizzbuzz(x))
#Wait 10 seconds to make the conditions the same as the others
for x in range(FIZZBUZZ_COUNT):
    time.sleep(1)

#End of measurement
te = time.time()

#Result output
print("{}sec".format(te-ts))


2. Launch 100,000 coroutines

# -*- coding: utf-8 -*-
import time
import asyncio

def fizzbuzz(i):
    if i == 15:
        return 'FizzBuzz'
    if i % 5 == 0:
        return 'Buzz'
    if i % 3 == 0:
        return 'Fizz'
    return str(i)

COUNT = 100000
FIZZBUZZ_COUNT = 10

#Start measurement
ts = time.time()

#10 times FizzBuzz x 100,000 times
async def task_fizzbuzz(prefix):
    for x in range(FIZZBUZZ_COUNT):
        # await asyncio.sleep(1)
        print(prefix + "{}:".format(str(x)) + fizzbuzz(x))
    return None

loop = asyncio.get_event_loop()
tasks = asyncio.wait([task_fizzbuzz(str(i) + ":") for i in range(COUNT)])
loop.run_until_complete(tasks)
loop.close()

#Wait 10 seconds to make the conditions the same as the others
for x in range(FIZZBUZZ_COUNT):
    time.sleep(1)

#End of measurement
te = time.time()

#Result output
print("{}sec".format(te-ts))

3. Launch 100,000 coroutines Context switch 1 million times

# -*- coding: utf-8 -*-
import time
import asyncio

def fizzbuzz(i):
    if i == 15:
        return 'FizzBuzz'
    if i % 5 == 0:
        return 'Buzz'
    if i % 3 == 0:
        return 'Fizz'
    return str(i)

COUNT = 100000
FIZZBUZZ_COUNT = 10

#Start measurement
ts = time.time()

#10 times FizzBuzz x 100,000 times
async def task_fizzbuzz(prefix):
    for x in range(FIZZBUZZ_COUNT):
        await asyncio.sleep(1)
        print(prefix + "{}:".format(str(x)) + fizzbuzz(x))
    return None

loop = asyncio.get_event_loop()
tasks = asyncio.wait([task_fizzbuzz(str(i) + ":") for i in range(COUNT)])
loop.run_until_complete(tasks)
loop.close()

#End of measurement
te = time.time()

#Result output
print("{}sec".format(te-ts))

Benchmark results

I only do it 3 times each. Context switch takes too much time (›´ω`‹) I haven't written it to keep the results simple, but Benchmark 2 seems to have 100,000 context switches.

スクリーンショット 2015-11-03 19.58.15.png

スクリーンショット 2015-11-03 19.50.43.png

reference

PEP 0492 -- Coroutines with async and await syntax 18.5.3. Tasks and coroutines 18.5.9. Develop with asyncio What's new in Python 3.5 How to use async/await in Python 3.5? Asynchronous programming with Async and Await (C # and Visual Basic) (https://msdn.microsoft.com/en-us/library/hh191443.aspx) AsyncIO vs Gevent? : Python - Reddit

Recommended Posts

Lightweight thread performance benchmark using async / await implemented in Python 3.5
Scraping using Python 3.5 async / await
Hide websockets async / await in Python3
Implemented SimRank in Python
python async / await curio
Implemented Shiritori in Python
Convert callback-style asynchronous API to async / await in Python
Scraping using Python 3.5 Async syntax
Run LINE Bot implemented in Python (Flask) "without using Heroku"
Sudoku solver implemented in Python 3
Translate using googletrans in Python
6 Ball puzzle implemented in python
Using Python mode in Processing
GUI programming in Python using Appjar
Precautions when using pit in Python
Implemented image segmentation in python (Union-Find)
Try using LevelDB in Python (plyvel)
Widrow-Hoff learning rules implemented in Python
Implemented label propagation method in Python
[Python] Asynchronous request with async / await
Using global variables in python functions
Let's see using input in python
Infinite product in Python (using functools)
Edit videos in Python using MoviePy
Implemented Perceptron learning rules in Python
Handwriting recognition using KNN in Python
Try using Leap Motion in Python
Implemented in 1 minute! LINE Notify in Python
Depth-first search using stack in Python
When using regular expressions in Python
GUI creation in python using tkinter 2
Mouse operation using Windows API in Python
Notes using cChardet and python3-chardet in Python 3.3.1.
Try using the Wunderlist API in Python
GUI creation in python using tkinter part 1
A simple HTTP client implemented in Python
Get Suica balance in Python (using libpafe)
(Bad) practice of using this in Python
Slowly hash passwords using bcrypt in Python
Try using the Kraken API in Python
Using venv in Windows + Docker environment [Python]
Big difference in ruby, python, httpd performance
Implemented in Python PRML Chapter 7 Nonlinear SVM
[FX] Hit oanda-API in Python using Docker
Tweet using the Twitter API in Python
[Python] [Windows] Serial communication in Python using DLL
I tried using Bayesian Optimization in Python
Log in to Slack using requests in Python
Get Youtube data in Python using Youtube Data API
I implemented Cousera's logistic regression in Python
Using physical constants in Python scipy.constants ~ constants e ~
Scraping a website using JavaScript in Python
Develop slack bot in python using chat.postMessage
Write python modules in fortran using f2py
Draw a tree in Python 3 using graphviz
Implemented in Python PRML Chapter 5 Neural Networks
Notes for using python (pydev) in eclipse
Disease classification in Random Forest using Python
Implemented Stooge sort in Python3 (Bubble sort & Quicksort)
Implemented in Python PRML Chapter 1 Bayesian Inference
Download files in any format using Python