Questo è previsto.
Esegui questo benchmark su una macchina virtuale, in cui il costo delle chiamate di sistema è maggiore rispetto all'hardware fisico. Quando gevent è attivato, tende a generare più chiamate di sistema (per gestire il dispositivo epoll), quindi si finisce con meno prestazioni.
Puoi facilmente verificare questo punto usando strace sullo script.
Senza gevent, il ciclo interno genera:
recvfrom(3, ":931\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
recvfrom(3, ":941\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
Con gevent, avrai occorrenze di:
recvfrom(3, ":221\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
recvfrom(3, 0x7b0f04, 4096, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(5, EPOLL_CTL_ADD, 3, {EPOLLIN, {u32=3, u64=3}}) = 0
epoll_wait(5, {{EPOLLIN, {u32=3, u64=3}}}, 32, 4294967295) = 1
clock_gettime(CLOCK_MONOTONIC, {2469, 779710323}) = 0
epoll_ctl(5, EPOLL_CTL_DEL, 3, {EPOLLIN, {u32=3, u64=3}}) = 0
recvfrom(3, ":231\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
Quando la chiamata recvfrom sta bloccando (EAGAIN), gevent torna al ciclo degli eventi, quindi vengono eseguite chiamate aggiuntive per attendere gli eventi dei descrittori di file (epoll_wait).
Tieni presente che questo tipo di benchmark è il caso peggiore per qualsiasi sistema di loop di eventi, perché hai un solo descrittore di file, quindi le operazioni di attesa non possono essere fattorizzate su più descrittori. Inoltre, gli I/O asincroni non possono migliorare nulla qui poiché tutto è sincrono.
È anche il caso peggiore per Redis perché:
-
genera molti viaggi di andata e ritorno al server
-
si connette/disconnette sistematicamente (1000 volte) perché il pool è dichiarato nella funzione UxDomainSocket.
In realtà il tuo benchmark non testa gevent, redis o redis-py:esercita la capacità di una VM di sostenere una partita a ping-pong tra 2 processi.
Se vuoi aumentare le prestazioni, devi:
-
usa la pipeline per ridurre il numero di viaggi di andata e ritorno
-
rendere il pool persistente nell'intero benchmark
Ad esempio, considera con il seguente script:
#!/usr/bin/python
from gevent import monkey
monkey.patch_all()
import timeit
import redis
from redis.connection import UnixDomainSocketConnection
pool = redis.ConnectionPool(connection_class=UnixDomainSocketConnection, path = '/tmp/redis.sock')
def UxDomainSocket():
r = redis.Redis(connection_pool = pool)
p = r.pipeline(transaction=False)
p.set("testsocket", 1)
for i in range(100):
p.incr('testsocket', 10)
p.get('testsocket')
p.delete('testsocket')
p.execute()
print timeit.Timer(stmt='UxDomainSocket()', setup='from __main__ import UxDomainSocket').timeit(number=1000)
Con questo script, ottengo prestazioni circa 3 volte migliori e quasi nessun sovraccarico con gevent.