Dit wordt verwacht.
Je draait deze benchmark op een VM, waarop de kosten van systeemoproepen hoger zijn dan op fysieke hardware. Wanneer gevent is geactiveerd, heeft het de neiging om meer systeemoproepen te genereren (om het epoll-apparaat af te handelen), zodat u uiteindelijk minder presteert.
U kunt dit punt gemakkelijk controleren door streep in het script te gebruiken.
Zonder gevent genereert de binnenste lus:
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
Met gevent heb je de volgende woorden:
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
Wanneer de recvfrom-oproep blokkeert (EAGAIN), gaat gevent terug naar de gebeurtenislus, dus er worden extra oproepen gedaan om te wachten op bestandsdescriptorsgebeurtenissen (epoll_wait).
Houd er rekening mee dat dit soort benchmark het slechtste geval is voor elk event-loop-systeem, omdat je maar één bestandsdescriptor hebt, dus de wachtbewerkingen kunnen niet worden gefactoriseerd op verschillende descriptors. Verder kunnen asynchrone I/O's hier niets verbeteren omdat alles synchroon is.
Het is ook een worst case voor Redis omdat:
-
het genereert veel retourvluchten naar de server
-
het maakt systematisch verbinding / verbreekt de verbinding (1000 keer) omdat de pool wordt gedeclareerd in de UxDomainSocket-functie.
Eigenlijk test je benchmark gevent, redis of redis-py niet:het oefent het vermogen van een VM uit om een pingpongspel tussen 2 processen te ondersteunen.
Als u de prestaties wilt verbeteren, moet u:
-
gebruik pipelining om het aantal retourvluchten te verminderen
-
maak de pool persistent over de hele benchmark
Overweeg bijvoorbeeld met het volgende 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)
Met dit script krijg ik ongeveer 3x betere prestaties en bijna geen overhead met gevent.