J'ai remarqué que mon application Python est beaucoup plus lente lors de son exécution python:2-alpine3.6
que de son exécution sans Docker sur Ubuntu. J'ai trouvé deux petites commandes de référence et il y a une énorme différence visible entre les deux systèmes d'exploitation, à la fois lorsque je les exécute sur un serveur Ubuntu et lorsque j'utilise Docker pour 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
J'ai également essayé le «benchmark» suivant, qui n'utilise pas 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
Qu'est-ce qui pourrait causer cette différence?
ubuntu
performance
docker
alpine-linux
Underyx
la source
la source
Réponses:
J'ai exécuté le même benchmark que vous, en utilisant uniquement Python 3:
entraînant une différence de plus de 2 secondes:
Alpine utilise une implémentation différente de
libc
(bibliothèque du système de base) du projet musl ( URL miroir ). Il existe de nombreuses différences entre ces bibliothèques . Par conséquent, chaque bibliothèque peut être plus performante dans certains cas d'utilisation.Voici une différence entre ces commandes ci-dessus . La sortie commence à différer de la ligne 269. Bien sûr, il y a différentes adresses en mémoire, mais sinon c'est très similaire. La plupart du temps est évidemment passé à attendre la fin de la
python
commande.Après l'installation
strace
dans les deux conteneurs, nous pouvons obtenir une trace plus intéressante (j'ai réduit le nombre d'itérations dans le benchmark à 10).Par exemple,
glibc
charge les bibliothèques de la manière suivante (ligne 182):Le même code dans
musl
:Je ne dis pas que c'est la principale différence, mais la réduction du nombre d'opérations d'E / S dans les bibliothèques principales pourrait contribuer à de meilleures performances. Du diff, vous pouvez voir que l'exécution du même code Python peut conduire à des appels système légèrement différents. Le plus important pourrait probablement être fait pour optimiser les performances de la boucle. Je ne suis pas suffisamment qualifié pour juger si le problème de performances est causé par l'allocation de mémoire ou une autre instruction.
glibc
avec 10 itérations:musl
avec 10 itérations:musl
est plus lent de 0,0028254222124814987 secondes. Comme la différence augmente avec le nombre d'itérations, je suppose que la différence réside dans l'allocation de mémoire des objets JSON.Si nous réduisons la référence à l'importation uniquement,
json
nous remarquons que la différence n'est pas si énorme:Le chargement des bibliothèques Python semble comparable. Générer
list()
produit une différence plus importante:Évidemment, l'opération la plus coûteuse est
json.dumps()
, ce qui pourrait indiquer des différences d'allocation de mémoire entre ces bibliothèques.En regardant à nouveau le benchmark , l'
musl
allocation de mémoire est vraiment légèrement plus lente:Je ne sais pas ce que l'on entend par "grosse allocation", mais elle
musl
est presque 2 fois plus lente, ce qui peut devenir significatif lorsque vous répétez de telles opérations des milliers ou des millions de fois.la source