Redis
 sql >> Base de données >  >> NoSQL >> Redis

redis + gevent - Mauvaises performances - qu'est-ce que je fais de mal ?

C'est normal.

Vous exécutez ce benchmark sur une VM, sur laquelle le coût des appels système est plus élevé que sur 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), vous vous retrouvez donc avec moins de performances.

Vous pouvez facilement vérifier ce point en utilisant 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 est bloquant (EAGAIN), gevent retourne à la boucle d'événements, donc des appels supplémentaires sont effectués pour attendre les événements de descripteurs de fichiers (epoll_wait).

Veuillez noter que ce type de benchmark est le pire des cas pour tout système de boucle d'événements, car vous n'avez qu'un seul descripteur de fichier, donc les opérations d'attente ne peuvent pas être factorisées sur plusieurs descripteurs. De plus, les E/S asynchrones ne peuvent rien améliorer ici puisque tout est synchrone.

C'est aussi le pire des cas pour Redis car :

  • il génère de nombreux allers-retours vers le serveur

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

En fait, votre benchmark ne teste pas gevent, redis ou redis-py :il exerce la capacité d'une VM à soutenir un jeu de ping-pong entre 2 processus.

Si vous souhaitez augmenter les performances, vous devez :

  • utiliser le pipeline pour réduire le nombre d'allers-retours

  • rendre le pool persistant sur l'ensemble du benchmark

Par exemple, considérez 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, j'obtiens des performances environ 3 fois meilleures et presque aucune surcharge avec gevent.