2015-04-07 22 views
5

Używam Asyncio i Żądań do testowania szeregu żądań HTTP.Asyncio sprawia, że ​​żądania HTTP są wolniejsze?

Z jakiegoś powodu jest nieco wolniej używać Asyncio niż prostych żądań. Każdy pomysł, dlaczego? Czy używam Asyncio niepoprawnie?

import asyncio 
import functools 
import requests 
import time 

ts = time.time() 
for i in range(10): 
    @asyncio.coroutine 
    def do_checks(): 
     loop = asyncio.get_event_loop() 
     req = loop.run_in_executor(None, functools.partial(requests.get, "http://google.com", timeout=3)) 
     resp = yield from req 
     print(resp.status_code) 

    loop = asyncio.get_event_loop() 
    loop.run_until_complete(do_checks()) 
te = time.time() 
print("Version A: " + str(te - ts)) 

ts = time.time() 
for i in range(10): 
    r = requests.get("http://google.com", timeout=3) 
    print(r.status_code) 
te = time.time() 

print("Version B: " + str(te - ts)) 

wyjściowa:

Wariant A = Asyncio; Wersja B = Żądania:

200 
200 
200 
200 
200 
200 
200 
200 
200 
200 
Version A: 5.7215821743011475 
200 
200 
200 
200 
200 
200 
200 
200 
200 
200 
Version B: 5.320340156555176 

Odpowiedz

12

Czekasz na zakończenie każdego wniosku przed rozpoczęciem następnego. Więc masz narzut pętli zdarzeń bez żadnych korzyści.

Spróbuj tego:

import asyncio 
import functools 
import requests 
import time 

ts = time.time() 
loop = asyncio.get_event_loop() 

@asyncio.coroutine 
def do_checks(): 
    futures = [] 
    for i in range(10): 
     futures.append(loop.run_in_executor(None, functools.partial(requests.get, "http://google.com", timeout=3))) 

    for req in asyncio.as_completed(futures): 
     resp = yield from req 
     print(resp.status_code) 

loop.run_until_complete(do_checks()) 
te = time.time() 
print("Version A: " + str(te - ts)) 

ts = time.time() 
for i in range(10): 
    r = requests.get("http://google.com", timeout=3) 
    print(r.status_code) 
te = time.time() 
print("Version B: " + str(te - ts)) 

To co dostaję gdy uruchamiam go:

$ python test.py 
200 
... 
Version A: 0.43438172340393066 
200 
... 
Version B: 1.6541109085083008 

Znacznie szybciej, ale tak naprawdę jest to po prostu tarła wątki i czeka biblioteka http do końca, do tego nie potrzebujesz asyncio.

Może chcesz wypróbować aiohttp, ponieważ został on zbudowany do użytku z asyncio. requests to wspaniała biblioteka, ale nie jest przeznaczona na asyncio.

+0

Wielką odpowiedź, że pomaga! Czy są jakieś zalety posiadania wątków odradzania 'asyncio' zamiast robić to bezpośrednio? – okoboko

+2

@okoboko Jeśli zamierzasz używać 'requests', naprawdę nie ma potrzeby używania' asyncio', chyba że masz inne komponenty w projekcie, które są faktycznie zaprojektowane do użycia z 'asyncio'. Jeśli tak jest, powinieneś faworyzować 'aiohttp' zamiast' requests', chyba że potrzebujesz funkcji 'request', której nie ma w' aiohttp'. – dano

+2

Jedna notatka z 'loop.run_in_executor' - kiedy używasz domyślnego executora (przekazując' None' jako pierwszy argument), używasz 'concurrent.futures.ThreadPoolExecutor' z pięcioma wątkami. Oznacza to, że można uruchamiać tylko pięć żądań jednocześnie, co jest dość niskie dla obciążenia związanego z operacjami we/wy. Prawdopodobnie uzyskasz lepszą wydajność, jeśli utworzysz własny ThreadPoolExecutor z większą liczbą wątków. – dano

6

Tylko dla kompletności, tutaj jest naprawdę szybka realizacja asyncio

import aiohttp 
import asyncio 
import time 

async def main(n): 
    ts = time.time() 
    session = aiohttp.ClientSession() 
    fs = [session.get('http://google.com') for _ in range(n)] 
    for f in asyncio.as_completed(fs): 
     resp = await f 
     print(resp.status) 
     await resp.release() 
    session.close() 
    te = time.time() 
    print("Aiohttp version: " + str(te - ts)) 

loop = asyncio.get_event_loop() 
loop.run_until_complete(main(10)) 
loop.close() 

Kod jest python 3.5 i wyżej.

~> python asyncioreq.py 
200 
... 
Aiohttp version: 0.15974688529968262 

nadzieję, że ktoś może go używać;)

Powiązane problemy