Da async / await in Python 3.5 hinzugefügt wurde, habe ich versucht, es hinsichtlich des Speicherverbrauchs und der Kosten für den Kontextwechsel zu vergleichen.
Dies ist die fortschrittlichste Syntax im Paradigma der asynchronen Verarbeitung, mit der Sie die asynchrone Verarbeitung und die nicht blockierende E / A-Verarbeitung auf nette Weise schreiben können. Nach der Implementierung in C # wurde es in C ++, VB, Node.js implementiert und kam schließlich zu Python! Es ist wie es ist. Das Merkmal ist, dass die asynchrone Verarbeitung, die in Threading hart geschrieben wurde, präziser und leistungsfähiger geschrieben werden kann. Leichte Threads werden auch als Mikrothreads oder Fasern bezeichnet und sind eine Lösung für das Problem, dass die Hardwareleistung bei vielen Clients aufgrund von E / A-Wartezeiten, die als "C10K-Problem" bezeichnet werden (Problem von 10.000 Clients), nicht vollständig genutzt werden kann. Es ist eins. Wenn Sie den Kontext mit hoher Geschwindigkeit wechseln, während Sie auf E / A warten und andere Clients verarbeiten, können Sie Programme schreiben, die die Leistung der Hardware an ihre Grenzen bringen. Mit anderen Worten, wenn Sie es beherrschen, können Sie einen Server schreiben, der mit hoher Geschwindigkeit und geringem Gewicht wie Nginx arbeitet.
Das Ergebnis des Benchmarks war, dass das Collout mit Pythons async / await schnell gestartet wurde, weniger Speicher verbrauchte und einen schnellen Kontextwechsel hatte. Ist es nicht eine Implementierung, die als Lightweight-Thread bezeichnet werden kann? Laut dem "Wow Erlang Book" beginnt Erlamgs leichter Thread mit einem Speicherverbrauch von 4 KByte in Mikrosekunden. Die Python-Version verbraucht 3,44 KByte Speicher und beginnt bei 60 Mikrosekunden, was mit Erlang vergleichbar ist.
Artikel | Wert |
---|---|
Start- und Endzeiten für 100.000 Collouts | 6.081sec |
Start- und Endzeit pro Collout | 0.0608ms |
Ausführungszeit von 1 Million Kontextwechseln | 51.435sec |
Ausführungszeit pro Kontextwechsel | 51.435μs |
Speicherverbrauch beim Starten von 100.000 Collouts | 146.86MByte |
Speicherverbrauch pro Collout | 1.50KByte |
Speicherverbrauch, wenn 100.000 Collouts gestartet und der Kontextwechsel 1 Million Mal ausgeführt wird | 336.30MByte |
Speicherverbrauch pro Person, wenn der Kontextwechsel 1 Million Mal ausgeführt wird | 3.44KByte |
Verarbeitung zur asynchronen Ausführung mehrerer Netzwerk-E / A und Datei-E / A.
Gewöhnlicher Fizz Buzz
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))
Das Ergebnis ist das gleiche, da es nur in der Corroutine ausgeführt wird.
1 Kollout
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()
#Rufen Sie die Hauptfunktion im Collout auf
loop.run_until_complete(main())
loop.close()
Generieren Sie 100.000 Collouts und führen Sie sie gleichzeitig aus. Wenn eine Coroutine die Ausführung beendet hat, wechselt der Kontext und die nächste Coroutine wird ausgeführt.
Corroutine 100.000 gleichzeitige Ausführung
# -*- 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()
#Generieren Sie 100.000 Collouts
tasks = asyncio.wait([task_fizzbuzz(str(i) + ":") for i in range(1, 100000)])
loop.run_until_complete(tasks)
loop.close()
Ausführungsergebnis von 100.000 Collouts
....
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
....
Dies ist ein Test, um festzustellen, ob der Kontext beim Schlafen während der Fizzbuzz-Logik erfolgreich wechselt.
100.000 gleichzeitige Hinrichtungen von Kollouts mit Schlaf
# -*- 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) #Neu hinzugefügter Code
print(prefix + "{}:".format(str(x)) + fizzbuzz(x))
return None
loop = asyncio.get_event_loop()
#Generieren Sie 100.000 Collouts
tasks = asyncio.wait([task_fizzbuzz(str(i) + ":") for i in range(1, 100000)])
#Parallele Ausführung
loop.run_until_complete(tasks)
loop.close()
Ausführungsergebnis von 100.000 Coroutinen mit Schlaf
....
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
....
Wenn Sie mit "wait asyncio.sleep (1)" schlafen, während die Coroutine ausgeführt wird, erhalten Sie das Ergebnis, dass sie gleichzeitig ausgeführt wird, da sie zu einer anderen Coroutine wechselt.
Nachdem die Überprüfung abgeschlossen ist, werden wir den Benchmark mit den folgenden drei Programmen durchführen.
# -*- 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
#Messung starten
ts = time.time()
#10 mal FizzBuzz x 100.000 mal
for x in range(COUNT):
for x in range(FIZZBUZZ_COUNT):
print(fizzbuzz(x))
#Warten Sie 10 Sekunden, bis der Zustand mit den anderen übereinstimmt
for x in range(FIZZBUZZ_COUNT):
time.sleep(1)
#Ende der Messung
te = time.time()
#Ergebnisausgabe
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
#Messung starten
ts = time.time()
#10 mal FizzBuzz x 100.000 mal
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()
#Warten Sie 10 Sekunden, bis der Zustand mit den anderen übereinstimmt
for x in range(FIZZBUZZ_COUNT):
time.sleep(1)
#Ende der Messung
te = time.time()
#Ergebnisausgabe
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
#Messung starten
ts = time.time()
#10 mal FizzBuzz x 100.000 mal
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()
#Ende der Messung
te = time.time()
#Ergebnisausgabe
print("{}sec".format(te-ts))
Ich mache es jeweils nur dreimal. Der Kontextwechsel dauert zu lange (›´ω`‹) Ich habe es nicht geschrieben, um die Ergebnisse einfach zu halten, aber Benchmark 2 scheint 100.000 Kontextwechsel stattgefunden zu haben.
PEP 0492 -- Coroutines with async and await syntax 18.5.3. Aufgaben und Collouts 18.5.9. Develop with asyncio Was ist neu in Python 3.5 How to use async/await in Python 3.5? Asynchrone Programmierung mit Async und Await (C # und Visual Basic) (https://msdn.microsoft.com/en-us/library/hh191443.aspx) AsyncIO vs Gevent? : Python - Reddit)
Recommended Posts