redis + gevent – Mau desempenho – o que estou fazendo de errado?
-
11-12-2019 - |
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)
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.