Detta förväntas.
Du kör detta riktmärke på en virtuell dator, där kostnaden för systemsamtal är högre än på fysisk hårdvara. När gevent är aktiverat tenderar det att generera fler systemanrop (för att hantera epoll-enheten), så att du får mindre prestanda.
Du kan enkelt kontrollera denna punkt genom att använda strace på skriptet.
Utan gavt genererar den inre slingan:
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
Med gevent kommer du att ha förekomster av:
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
När recvfrom-anropet blockerar (EAGAIN), går gevent tillbaka till händelseslingan, så ytterligare anrop görs för att vänta på filbeskrivningshändelser (epoll_wait).
Observera att den här typen av riktmärke är ett värsta fall för alla händelseloopsystem, eftersom du bara har en filbeskrivning, så väntan operationer kan inte faktoriseras på flera deskriptorer. Dessutom kan asynkrona I/O inte förbättra någonting här eftersom allt är synkront.
Det är också ett värsta fall för Redis eftersom:
-
det genererar många rundresor till servern
-
den ansluter/kopplar från systematiskt (1000 gånger) eftersom poolen deklareras i UxDomainSocket-funktionen.
Egentligen testar ditt riktmärke inte gevent, redis eller redis-py:det utövar en virtuell dators förmåga att upprätthålla ett pingisspel mellan två processer.
Om du vill öka prestandan måste du:
-
använd pipelining för att minska antalet tur och retur
-
göra poolen beständig över hela riktmärket
Överväg till exempel med följande skript:
#!/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)
Med det här skriptet får jag ungefär 3 gånger bättre prestanda och nästan ingen overhead med gavt.