Depuis que async / await a été ajouté dans Python 3.5, j'ai essayé de le comparer en termes de consommation de mémoire et de coût de changement de contexte.
Il s'agit de la syntaxe la plus avancée du paradigme de traitement asynchrone qui vous permet d'écrire de manière agréable un traitement asynchrone et un traitement d'E / S non bloquant. Après avoir été implémenté en C #, il a été implémenté en C ++, VB, Node.js et est finalement arrivé à Python! C'est comme ça. La fonctionnalité est que le traitement asynchrone, qui a été écrit en dur dans Threading, peut être écrit de manière plus concise et plus puissante. Les threads légers sont également appelés microthreads ou fibres, et sont une solution au problème que les performances matérielles ne peuvent pas être pleinement utilisées lorsqu'il y a de nombreux clients en raison d'une attente d'E / S appelée «problème C10K» (problème de 10 000 clients). C'est un. En changeant le contexte à grande vitesse en attendant les E / S et en manipulant d'autres clients, vous serez en mesure d'écrire des programmes qui font ressortir les performances du matériel à la limite. En d'autres termes, si vous le maîtrisez, vous pourrez écrire un serveur qui fonctionne à haute vitesse et léger comme nginx.
Les résultats du benchmark sont que le collout utilisant async / await de Python a démarré rapidement, consommé moins de mémoire et a eu un changement de contexte rapide. N'est-ce pas une implémentation que l'on peut appeler un thread léger? Selon le «livre Wow Erlang», le fil léger d'Erlamg commence par 4 Ko de consommation de mémoire en microsecondes. La version Python consomme 3,44 Ko de mémoire et démarre à 60 microsecondes, ce qui est comparable à Erlang.
article | valeur |
---|---|
Heures de début et de fin pour 100 000 collouts | 6.081sec |
Heure de début et de fin par collout | 0.0608ms |
Temps d'exécution de 1 million de commutateurs de contexte | 51.435sec |
Temps d'exécution par changement de contexte | 51.435μs |
Consommation de mémoire lors du démarrage de 100 000 collouts | 146.86MByte |
Consommation de mémoire par collout | 1.50KByte |
Consommation de mémoire lorsque 100000 collouts sont démarrés et que le changement de contexte est exécuté 1 million de fois | 336.30MByte |
Consommation de mémoire par unité lorsque le changement de contexte est exécuté 1 million de fois | 3.44KByte |
Traitement pour effectuer plusieurs E / S réseau et E / S fichier de manière asynchrone
Fizz Buzz ordinaire
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))
Le résultat sera le même car il ne fonctionne que dans la corroutine.
1 collout
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()
#Appelez la fonction principale dans le collout
loop.run_until_complete(main())
loop.close()
Générez 100 000 collouts et exécutez-les en même temps. Lorsqu'une coroutine termine son exécution, le contexte change et la coroutine suivante est exécutée.
Corroutine 100 000 exécutions simultanées
# -*- 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()
#Générer 100 000 collouts
tasks = asyncio.wait([task_fizzbuzz(str(i) + ":") for i in range(1, 100000)])
loop.run_until_complete(tasks)
loop.close()
100000 résultats d'exécution corroutine
....
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
....
Ceci est un test pour voir si le contexte bascule avec succès lors de la mise en veille pendant la logique fizzbuzz.
100000 exécutions simultanées de collouts avec sommeil
# -*- 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) #Code nouvellement ajouté
print(prefix + "{}:".format(str(x)) + fizzbuzz(x))
return None
loop = asyncio.get_event_loop()
#Générer 100 000 collouts
tasks = asyncio.wait([task_fizzbuzz(str(i) + ":") for i in range(1, 100000)])
#Exécution parallèle
loop.run_until_complete(tasks)
loop.close()
Résultat d'exécution de 100000 coroutines avec sommeil
....
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
....
Si vous dormez avec ʻawait asyncio.sleep (1) `pendant que la coroutine est en cours d'exécution, vous obtenez le résultat qu'elle s'exécute en même temps car elle passe à une autre coroutine.
Maintenant que la vérification est terminée, nous allons prendre le benchmark avec les trois programmes suivants.
# -*- 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
#Commencer la mesure
ts = time.time()
#10 fois FizzBuzz x 100000 fois
for x in range(COUNT):
for x in range(FIZZBUZZ_COUNT):
print(fizzbuzz(x))
#Attendez 10 secondes pour que la condition soit la même que les autres
for x in range(FIZZBUZZ_COUNT):
time.sleep(1)
#Fin de la mesure
te = time.time()
#Sortie de résultat
print("{}sec".format(te-ts))
# -*- 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
#Commencer la mesure
ts = time.time()
#10 fois FizzBuzz x 100000 fois
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()
#Attendez 10 secondes pour que la condition soit la même que les autres
for x in range(FIZZBUZZ_COUNT):
time.sleep(1)
#Fin de la mesure
te = time.time()
#Sortie de résultat
print("{}sec".format(te-ts))
# -*- 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
#Commencer la mesure
ts = time.time()
#10 fois FizzBuzz x 100000 fois
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()
#Fin de la mesure
te = time.time()
#Sortie de résultat
print("{}sec".format(te-ts))
Je ne le fais que 3 fois chacun. Le changement de contexte prend trop de temps (›´ω`‹) Je ne l'ai pas écrit pour garder les résultats simples, mais Benchmark 2 semble avoir eu lieu 100 000 changements de contexte.
PEP 0492 -- Coroutines with async and await syntax 18.5.3. Tâches et collaborateurs 18.5.9. Develop with asyncio Nouveautés de Python 3.5 How to use async/await in Python 3.5? Programmation asynchrone avec Async et Await (C # et Visual Basic) (https://msdn.microsoft.com/en-us/library/hh191443.aspx) AsyncIO vs Gevent? : Python - Reddit)
Recommended Posts