Pregunta

Acabo de escribir una simple pieza de código a la prueba de la prueba Redis + GEVENT para ver cómo ASYNC ayuda a la perforamancia y me sorprendió encontrar un mal desempeño.Aquí está mi código.Si se deshace de las dos primeras líneas a Monkey Parche este código, entonces verá el tiempo de "ejecución normal".

en un Ubuntu 12.04 LTS VM, estoy viendo un momento de

sin parche de mono - 54 segundos Con mono parche - 61 segundos

¿Hay algo malo con mi código / enfoque?¿Hay un problema de rendimiento aquí?

#!/usr/bin/python

from gevent import monkey

monkey.patch_all()

import timeit
import redis
from redis.connection import UnixDomainSocketConnection

def UxDomainSocket():
    pool = redis.ConnectionPool(connection_class=UnixDomainSocketConnection, path =    '/var/redis/redis.sock')
    r = redis.Redis(connection_pool = pool)
    r.set("testsocket", 1)
    for i in range(100):
            r.incr('testsocket', 10)
    r.get('testsocket')
    r.delete('testsocket')


print timeit.Timer(stmt='UxDomainSocket()',
 setup='from __main__ import UxDomainSocket').timeit(number=1000)

¿Fue útil?

Solución

Esto se espera.

Usted realiza este punto de referencia en una máquina virtual, en la que el costo de las llamadas del sistema es más alto que en el hardware físico. Cuando se activa GEVENT, tiende a generar más llamadas del sistema (para manejar el dispositivo EPOLL), para que termine con menos rendimiento.

Puede verificar fácilmente este punto usando 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á ocurrencias 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 RECVDROM está bloqueando (EAGAIN), GEVENT vuelve al bucle de eventos, por lo que se realizan llamadas adicionales para esperar a los eventos de los descriptores de archivos (EPOLL_WAIT).

Tenga en cuenta que este tipo de referencia es el peor de los casos para cualquier sistema de bucle de eventos, ya que solo tiene un descriptor de archivo, por lo que las operaciones de espera no se pueden factorizar en varios descriptores. Además, Async I / OS no puede mejorar nada aquí, ya que todo es síncrono.

También es el peor de los casos para Redis porque:

  • Se genera muchos ondritas al servidor

  • Se conecta / se desconecta sistemáticamente (1000 veces) porque la piscina se declara en la función UXDomainsHocket.

    En realidad, su 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, necesita:

    • Use la tubería para disminuir el número de rondas

    • Haz que la piscina persistente en todo el punto de referencia

      Por ejemplo, considere con el siguiente 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 este script, obtengo un mejor rendimiento 3x y casi no hay gastos generales con gevent.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top