Zauważyłem, że moja aplikacja Python działa o wiele wolniej python:2-alpine3.6
niż na niej bez Dockera na Ubuntu. Wymyśliłem dwa małe polecenia testu porównawczego i między tymi dwoma systemami operacyjnymi widoczna jest ogromna różnica, zarówno podczas uruchamiania ich na serwerze Ubuntu, jak i podczas korzystania z Docker na komputery 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
Próbowałem także następującego „testu porównawczego”, który nie używa Pythona:
$ 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
Co może być przyczyną tej różnicy?
ubuntu
performance
docker
alpine-linux
Underyx
źródło
źródło
Odpowiedzi:
Uruchomiłem ten sam test co ty, używając tylko Pythona 3:
co powoduje różnicę przekraczającą 2 sekundy:
Alpine używa innej implementacji
libc
(podstawowej biblioteki systemowej) niż projekt musl ( lustrzany URL ). Istnieje wiele różnic między tymi bibliotekami . W rezultacie każda biblioteka może działać lepiej w niektórych przypadkach użycia.Oto różnica między tymi poleceniami powyżej . Wyjście zaczyna się różnić od wiersza 269. Oczywiście w pamięci są różne adresy, ale poza tym jest bardzo podobne. Większość czasu jest oczywiście poświęcana na oczekiwanie na zakończenie
python
polecenia.Po zainstalowaniu
strace
w obu kontenerach możemy uzyskać bardziej interesujący ślad (zmniejszyłem liczbę iteracji w teście do 10).Na przykład
glibc
ładuje biblioteki w następujący sposób (wiersz 182):Ten sam kod w
musl
:Nie twierdzę, że jest to kluczowa różnica, ale zmniejszenie liczby operacji we / wy w bibliotekach podstawowych może przyczynić się do lepszej wydajności. Z porównania widać, że wykonanie tego samego kodu Pythona może prowadzić do nieco innych wywołań systemowych. Prawdopodobnie najważniejszą rzeczą może być optymalizacja wydajności pętli. Nie mam wystarczających kwalifikacji, aby ocenić, czy problem z wydajnością jest spowodowany alokacją pamięci lub innymi instrukcjami.
glibc
z 10 iteracjami:musl
z 10 iteracjami:musl
jest wolniejszy o 0,0028254222124814987 sekund. Ponieważ różnica rośnie wraz z liczbą iteracji, zakładam, że różnica polega na alokacji pamięci obiektów JSON.Jeśli ograniczymy test do samego importu
json
, zauważymy, że różnica nie jest aż tak duża:Ładowanie bibliotek Pythona wygląda porównywalnie. Generowanie
list()
powoduje większą różnicę:Oczywiście jest to najdroższa operacja
json.dumps()
, która może wskazywać na różnice w alokacji pamięci między tymi bibliotekami.Patrząc znów na benchmarku ,
musl
jest naprawdę nieznacznie wolniejszy w alokacji pamięci:Nie jestem pewien, co należy rozumieć przez „duży przydział”, ale
musl
jest prawie 2 razy wolniejszy, co może stać się znaczące, gdy powtórzy się takie operacje tysiące lub miliony razy.źródło