Percebi que meu aplicativo Python é muito mais lento ao executá-lo do python:2-alpine3.6
que executá-lo sem o Docker no Ubuntu. Eu vim com dois pequenos comandos de benchmark e há uma enorme diferença visível entre os dois sistemas operacionais, tanto quando os estou executando em um servidor Ubuntu quanto quando estou usando o Docker para Mac.
$ BENCHMARK="import timeit; print(timeit.timeit('import json; json.dumps(list(range(10000)))', number=5000))"
$ docker run python:2-alpine3.6 python -c $BENCHMARK
7.6094589233
$ docker run python:2-slim python -c $BENCHMARK
4.3410820961
$ docker run python:3-alpine3.6 python -c $BENCHMARK
7.0276606959
$ docker run python:3-slim python -c $BENCHMARK
5.6621271420
Eu também tentei o seguinte 'benchmark', que não usa Python:
$ docker run -ti ubuntu bash
root@6b633e9197cc:/# time $(i=0; while (( i < 9999999 )); do (( i ++
)); done)
real 0m39.053s
user 0m39.050s
sys 0m0.000s
$ docker run -ti alpine sh
/ # apk add --no-cache bash > /dev/null
/ # bash
bash-4.3# time $(i=0; while (( i < 9999999 )); do (( i ++ )); done)
real 1m4.277s
user 1m4.290s
sys 0m0.000s
O que poderia estar causando essa diferença?
ubuntu
performance
docker
alpine-linux
Underyx
fonte
fonte
Respostas:
Eu executei o mesmo benchmark que você, usando apenas o Python 3:
resultando em mais de 2 segundos de diferença:
A Alpine está usando uma implementação diferente de
libc
(biblioteca do sistema base) do projeto musl ( URL de espelho ). Existem muitas diferenças entre essas bibliotecas . Como resultado, cada biblioteca pode ter um desempenho melhor em determinados casos de uso.Aqui está uma diferença entre os comandos acima . A saída começa a diferir da linha 269. É claro que existem endereços diferentes na memória, mas, caso contrário, é muito semelhante. Obviamente, a maior parte do tempo é gasta esperando o
python
comando terminar.Após a instalação
strace
nos dois contêineres, podemos obter um rastreamento mais interessante (reduzi o número de iterações no benchmark para 10).Por exemplo,
glibc
está carregando bibliotecas da seguinte maneira (linha 182):O mesmo código em
musl
:Não estou dizendo que essa é a principal diferença, mas reduzir o número de operações de E / S nas bibliotecas principais pode contribuir para um melhor desempenho. Pelo diff, você pode ver que a execução do mesmo código Python pode levar a chamadas de sistema ligeiramente diferentes. Provavelmente o mais importante poderia ser feito na otimização do desempenho do loop. Não estou qualificado o suficiente para julgar se o problema de desempenho é causado pela alocação de memória ou por alguma outra instrução.
glibc
com 10 iterações:musl
com 10 iterações:musl
é mais lento em 0,0028254222124814987 segundos. À medida que a diferença aumenta com o número de iterações, eu presumo que a diferença esteja na alocação de memória dos objetos JSON.Se reduzirmos o benchmark para a importação exclusiva
json
, notamos que a diferença não é tão grande:Carregar bibliotecas Python parece comparável. Gerar
list()
produz uma diferença maior:Obviamente, a operação mais cara é
json.dumps()
, o que pode apontar para diferenças na alocação de memória entre essas bibliotecas.Olhando novamente para o benchmark ,
musl
é realmente um pouco mais lento na alocação de memória:Não sei ao certo o que significa "grande alocação", mas
musl
é quase duas vezes mais lento, o que pode se tornar significativo quando você repete essas operações milhares ou milhões de vezes.fonte