O que faz diferença significativa de desempenho entre eventlet e gevent?
-
12-12-2019 - |
Pergunta
Essas duas bibliotecas compartilham uma filosofia semelhante e, como resultado, decisões de design semelhantes.Mas este popular benchmark WSGI diz eventlet
é muito mais lento do que gevent
.O que torna seu desempenho tão diferente?
Como sei, as principais diferenças entre eles são:
gevent
intencionalmente depende e está acoplado alibev
(libevent
, anteriormente) enquantoeventlet
define interface de reator independente e implementa adaptadores específicos usandoselect
,epoll
, e o reator Twisted atrás dele. A interface adicional do reator causa impactos críticos no desempenho?gevent
é escrito principalmente em Cython enquantoeventlet
é escrito em Python puro. O Cython compilado nativamente é tão mais rápido que o Python puro, para programas não tão computacionais, mas vinculados a IO?Primitivos de
gevent
emular interfaces de bibliotecas padrão enquantoeventlet
As primitivas do s diferem do padrão e fornecem uma camada adicional para emulá-lo. A camada de emulação adicional tornaeventlet
Mais devagar?É a implementação de
eventlet.wsgi
apenas pior do quegevent.pywsgi
?
Eu realmente me pergunto, porque no geral eles parecem muito semelhantes para mim.
Solução
Bem, gevent não é "principalmente" escrito em Cython, embora algumas seções críticas sejam.
Cython faz uma enorme diferença.As otimizações do processador funcionam muito melhor com código compilado.A previsão de ramificação, por exemplo, falha em sistemas baseados em VM porque a indireção da ramificação no nível de execução da VM é opaca para ela.A área ocupada pelo cache é menor.O código compilado faz uma grande diferença aqui, e o IO pode ser muito sensível à latência.
Na mesma linha, libev é muito rápido.Mesmas razões.
Não parece que o eventlet deveria estar usando o hub de seleção (o Python 2.6 geralmente usa como padrão o epoll).Se estivesse preso no select, isso faria com que realmente lento (porque o Python precisa converter o select fd_set para frente e para trás em uma lista do Python, então fica feio quando está no meio de um loop).
Não fiz nenhum perfil, mas aposto que libev/libevent mais Cython fazem uma grande diferença.Notavelmente, algumas das primitivas de threading estão no Cython em gevent.Isso é importante porque muito código os atinge indiretamente por meio de IO e até mesmo da biblioteca padrão em alguns pontos.
Quanto à camada adicional de emulação do eventlet, parece haver muito mais elasticidade.No gevent, o caminho do código parece construir retornos de chamada e permitir que o hub os chame.eventlet parece fazer mais contabilidade do que o hub está fazendo em gevent.Novamente, porém, eu não fiz um perfil dele.Quanto ao monkeypatching em si, eles são bastante semelhantes.
O servidor WSGI é outro difícil.Notavelmente, a análise do cabeçalho no gevent é adiada para a biblioteca padrão, enquanto eles próprios a implementam no eventlet.Não tenho certeza se isso é um grande impacto ou não, mas não seria surpresa se houvesse algo escondido ali.O mais revelador é que o servidor do eventlet é baseado em uma versão com patch de macaco da biblioteca padrão BaseHTTPServer.Não consigo imaginar que isso seja ideal.Gevent implementa um servidor que reconhece a emulação.
Outras dicas
Desculpe pela resposta tardia.
Existem duas razões principais para a grande diferença de desempenho naquela referência:
- como afirmado antes, os caminhos críticos do gevent são fortemente otimizados
- esse benchmark faz testes de estresse.Não está mais vinculado ao IO, porque tenta fazer com que a máquina execute o maior número possível de solicitações.E é aí que o código Cythonized brilha.
"No mundo real", isso só acontece durante picos de tráfego "slashdot".O que é importante e deve-se estar pronto, mas quando isso acontece, você reage adicionando mais servidores ou desativando recursos com muitos recursos.Não vi um benchmark que realmente adicione mais servidores quando a carga aumenta.
Se, por outro lado, o benchmark simulasse uma carga de “dia normal” (que variaria de um site para outro), mas geralmente poderia ser aproximado para solicitação, pausa aleatória, repetição.Quanto menos pausa, mais tráfego simulamos.Além disso, o lado do cliente do benchmark teria que simular a latência.No Linux isso poderia ser feito usando o incrível netem[1], caso contrário, colocando pequenos atrasos antes das chamadas recv/send (o que seria muito difícil porque os benchmarks geralmente usam bibliotecas de nível superior).
Agora, se essas condições forem atendidas, na verdade compararíamos problemas vinculados a IO.Mas os resultados não seriam tão impressionantes:todos os candidatos atenderam com êxito cargas de 10, 50 e até 200 qps.Chato, certo?Assim, poderíamos medir a distribuição de latência, tempo para atender 99% das solicitações, etc.Gevent ainda apresentaria melhores resultados.Mas a diferença dificilmente seria impressionante.