Domanda

Ho appena scritto un semplice pezzo di codice a Perf Test Redis + Gevent per vedere come ASYNC aiuta la performance ed io siamo rimasti sorpresi di trovare cattive prestazioni.Ecco il mio codice.Se ti sbarazzi delle prime due righe a Monkey Patch questo codice, vedrai il tempismo "Esecuzione normale".

Su un Ubuntu 12.04 LTS VM, sto vedendo un tempismo di

senza patch monkey - 54 secondi Con patch di scimmia - 61 secondi

C'è qualcosa di sbagliato nel mio codice / approccio?C'è un problema perf qui?

#!/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)
.

È stato utile?

Soluzione

Questo è previsto.

Esegui questo benchmark su una VM, su cui il costo delle chiamate di sistema è superiore a quello su hardware fisico. Quando Gevent è attivato, tende a generare più chiamate di sistema (per gestire il dispositivo EPOLL), in modo da finire con meno prestazioni.

È possibile controllare facilmente questo punto usando Strace sullo script.

Senza Gevent, il loop 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 iscritti 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 è bloccata (EAGAIN), Gevent torna al loop dell'evento, quindi vengono eseguite ulteriori chiamate per attendere gli eventi dei descrittori di file (EPOLL_WAIT).

Si prega di notare che questo tipo di benchmark è un caso peggiore per qualsiasi sistema di loop evento, poiché hai solo un descrittore di file, quindi le operazioni di attesa non possono essere fattilizzate su diversi descrittori. Inoltre, Async I / O non può migliorare nulla qui poiché tutto è sincrono.

È anche un caso peggiore per Redis perché:

    .
  • Genera molte antaintropie al server

  • Si collega sistematicamente / disconnetti (1000 volte) perché il pool è dichiarato in funzione UXDomainSocket.

    In realtà il tuo benchmark non testerà Gevent, Redis o Redis-Py: esercita la capacità di una VM per sostenere un gioco Ping-Pong tra 2 processi.

    Se si desidera aumentare le prestazioni, è necessario:

      .
    • Utilizzare tubazioni per diminuire il numero di antaintrici

    • Rendi la piscina persistenti attraverso l'intero benchmark

      Ad esempio, considerare 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 circa 3 volte prestazioni migliori e quasi nessun sovraccarico con Gevent.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top