Esto es esperado.
Ejecuta este punto de referencia en una máquina virtual, en la que el costo de las llamadas al sistema es mayor que en el hardware físico. Cuando se activa gevent, tiende a generar más llamadas al sistema (para manejar el dispositivo epoll), por lo que terminas con un menor rendimiento.
Puede verificar fácilmente este punto utilizando strace en el script.
Sin GEvent, el bucle 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, tendrá apariciones de:
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
Cuando la llamada recvfrom está bloqueando (EAGAIN), GEvent se remonta a la ciclo de eventos, por lo que se realizan llamadas adicionales para esperar los eventos de descriptores de archivos (epoll_wait).
Tenga en cuenta que este tipo de punto de referencia es el peor caso para cualquier sistema de bucle de eventos, porque solo tiene un descriptor de archivo, por lo que las operaciones de espera no se pueden factorizar en varios descriptores. Además, las E/S asincrónicas no pueden mejorar nada aquí ya que todo es sincrónico.
Es también un peor caso para Redis porque:
En realidad el punto de referencia no prueba GEvent, Redis o Redis-py: ejerce la capacidad de una máquina virtual para sostener un juego de ping-pong entre 2 procesos.
Si desea aumentar el rendimiento, es necesario:
Por ejemplo, considere con la siguiente secuencia de comandos:
#!/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 este script, obtengo un rendimiento 3 veces mejor y casi sin gastos generales con gevent.
Gracias por la respuesta detallada. Si entiendo el problema más profundo, básicamente lo que he hecho es que solo hay un "objeto" que se puede esperar: si, por ejemplo, tuviera un conjunto de conexiones de Redis y utilizara gevent entonces me daría un mejor rendimiento (asumiendo redis puede mantenerse al día). Por cierto, la máquina virtual (y el conector Ux) era solo para pruebas. La producción será en instancias diferentes, etc., – vivekv
si se utiliza tubería, luego cómo usar "bloqueo de redis" – Tallmad