Question

Je viens d'écrire un code de code simple à Perf Test Reis + Gevent pour voir comment Async aide la perforation et j'ai été surpris de trouver de mauvaises performances.Voici mon code.Si vous vous débarrassez des deux premières lignes à la touche de singe, ce code puis vous verrez le timing "Exécution normale".

sur un Ubuntu 12.04 lts vm, je vois un moment de la synchronisation

sans patch de singe - 54 secs Avec patch de singe - 61 secondes

Y a-t-il quelque chose qui ne va pas avec mon code / mon approche?Y a-t-il un problème Perf ici?

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

Était-ce utile?

La solution

Ceci est attendu.

Vous exécutez cette référence sur une machine virtuelle, sur laquelle le coût des appels système est supérieur à celui du matériel physique. Lorsque Gevent est activé, il a tendance à générer plus d'appels système (pour gérer le périphérique EPOLL) afin de vous retrouver avec moins de performances.

Vous pouvez facilement vérifier ce point à l'aide de la strace sur le script.

sans gevent, la boucle interne génère:

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

avec Gevent, vous aurez des occurrences 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

Lorsque l'appel RECVFROM bloque (Eagain), Gevent retourne à la boucle d'événement, de sorte que des appels supplémentaires sont effectués pour attendre les événements descripteurs de fichiers (Epoll_wait).

Veuillez noter que ce type de référence est un pire des cas pour tout système de boucle d'événement, car vous n'avez qu'un seul descripteur de fichiers. Les opérations d'attente ne peuvent donc pas être factorisées sur plusieurs descripteurs. De plus, ASYNC I / OS ne peut améliorer quoi que ce soit ici puisque tout est synchrone.

C'est aussi un pire des cas pour REDIS parce que:

  • Il génère de nombreuses demi-arrivées au serveur

  • Il connecte systématiquement / déconnecte (1000 fois) car le pool est déclaré dans la fonction UXDomUnocket.

    En fait, votre référence ne teste pas Gevent, Redis ou Redis-Py: il exerce la capacité d'une machine virtuelle à soutenir un jeu de ping-pong entre 2 processus.

    Si vous souhaitez augmenter la performance, vous devez:

    • Utilisez de la pipeline pour diminuer le nombre de rondes

    • Faire la piscine persistante dans l'ensemble de la référence

      Par exemple, envisagez avec le script suivant:

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

      Avec ce script, je reçois environ 3 fois de meilleures performances et presque pas de frais généraux avec Gevent.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top