Pergunta

Acabei de escrever um código simples para testar o desempenho do Redis + gevent para ver como o assíncrono ajuda no desempenho e fiquei surpreso ao encontrar um desempenho ruim.aqui está o meu código.Se você se livrar das duas primeiras linhas para corrigir esse código, verá o tempo de "execução normal".

Em uma VM Ubuntu 12.04 LTS, estou vendo um tempo de

sem patch de macaco - 54 seg Com patch de macaco - 61 segundos

Há algo errado com meu código/abordagem?Existe um problema de desempenho aqui?

#!/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)
Foi útil?

Solução

Isto é esperado.

Você executa esse benchmark em uma VM, na qual o custo das chamadas do sistema é maior do que no hardware físico.Quando gevent é ativado, ele tende a gerar mais chamadas de sistema (para lidar com o dispositivo epoll), então você acaba com menos desempenho.

Você pode verificar esse ponto facilmente usando strace no script.

Sem gevent, o loop interno gera:

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

Com gevent, você terá ocorrências 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

Quando a chamada recvfrom está bloqueando (EAGAIN), gevent volta para o loop de eventos, portanto chamadas adicionais são feitas para aguardar eventos de descritores de arquivo (epoll_wait).

Observe que esse tipo de benchmark é o pior caso para qualquer sistema de loop de eventos, porque você possui apenas um descritor de arquivo, portanto, as operações de espera não podem ser fatoradas em vários descritores.Além disso, as E/S assíncronas não podem melhorar nada aqui, pois tudo é síncrono.

É também o pior caso para o Redis porque:

  • gera muitas viagens de ida e volta para o servidor

  • ele conecta/desconecta sistematicamente (1000 vezes) porque o pool é declarado na função UxDomainSocket.

Na verdade, seu benchmark não testa gevent, redis ou redis-py:ele exercita a capacidade de uma VM de sustentar um jogo de pingue-pongue entre 2 processos.

Se quiser aumentar o desempenho, você precisa:

  • use pipeline para diminuir o número de viagens de ida e volta

  • tornar o pool persistente em todo o benchmark

Por exemplo, considere o seguinte 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)

Com esse script, obtenho desempenho cerca de 3x melhor e quase nenhuma sobrecarga com o gevent.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top