Warum ist das alpine Docker-Image über 50% langsamer als das Ubuntu-Image?

8493
Underyx

Mir ist aufgefallen, dass meine Python-Anwendung viel langsamer läuft python:2-alpine3.6als die Ausführung ohne Docker unter Ubuntu. Ich habe zwei kleine Benchmark-Befehle gefunden, und es gibt einen großen Unterschied zwischen den beiden Betriebssystemen, sowohl wenn ich sie auf einem Ubuntu-Server als auch wenn ich Docker für Mac verwende.

$ 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 

Ich habe auch den folgenden "Benchmark" ausprobiert, der Python nicht verwendet:

$ 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 

Was könnte diesen Unterschied verursachen?

22
@Seth Blick noch einmal: Das Timing beginnt nach der Installation von Bash in der gestarteten Bash-Shell Underyx vor 7 Jahren 1

1 Antwort auf die Frage

27
Tombart

Ich habe den gleichen Benchmark wie Sie mit Python 3 ausgeführt:

$ docker run python:3-alpine3.6 python --version Python 3.6.2 $ docker run python:3-slim python --version Python 3.6.2 

was zu mehr als 2 Sekunden Unterschied führt:

$ docker run python:3-slim python -c "$BENCHMARK" 3.6475560404360294 $ docker run python:3-alpine3.6 python -c "$BENCHMARK" 5.834922112524509 

Alpine verwendet eine andere Implementierung libc( Basissystembibliothek ) als das Musl- Projekt . Es gibt viele Unterschiede zwischen diesen Bibliotheken . Daher kann jede Bibliothek in bestimmten Anwendungsfällen bessere Ergebnisse erzielen.

Hier ist ein Unterschied zwischen diesen Befehlen . Die Ausgabe beginnt sich von Zeile 269 zu unterscheiden. Natürlich gibt es unterschiedliche Adressen im Speicher, ansonsten ist sie jedoch sehr ähnlich. Die meiste Zeit wird offensichtlich auf das pythonEnde des Befehls gewartet .

Nach der Installation stracein beiden Containern können wir eine interessantere Spur erhalten (ich habe die Anzahl der Iterationen im Benchmark auf 10 reduziert).

Zum Beispiel glibcwird das Laden von Bibliotheken in der folgenden Weise (Leitung 182):

openat(AT_FDCWD, "/usr/local/lib/python3.6", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3 getdents(3, /* 205 entries */, 32768) = 6824 getdents(3, /* 0 entries */, 32768) = 0 

gleicher Code in musl:

open("/usr/local/lib/python3.6", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 3 fcntl(3, F_SETFD, FD_CLOEXEC) = 0 getdents64(3, /* 62 entries */, 2048) = 2040 getdents64(3, /* 61 entries */, 2048) = 2024 getdents64(3, /* 60 entries */, 2048) = 2032 getdents64(3, /* 22 entries */, 2048) = 728 getdents64(3, /* 0 entries */, 2048) = 0 

Ich sage nicht, dass dies der entscheidende Unterschied ist, aber die Reduzierung der E / A-Anzahl in Core-Bibliotheken könnte zu einer besseren Leistung beitragen. Aus dem Vergleich können Sie erkennen, dass die Ausführung desselben Python-Codes zu leicht unterschiedlichen Systemaufrufen führen kann. Das Wichtigste könnte wohl bei der Optimierung der Schleifenleistung gemacht werden. Ich bin nicht qualifiziert, um zu beurteilen, ob das Leistungsproblem durch Speicherzuordnung oder andere Anweisungen verursacht wird.

  • glibc mit 10 Iterationen:

    write(1, "0.032388824969530106\n", 210.032388824969530106) 
  • musl mit 10 Iterationen:

    write(1, "0.035214247182011604\n", 210.035214247182011604) 

muslist 0,0028254222124814987 Sekunden langsamer. Da der Unterschied mit der Anzahl der Iterationen zunimmt, gehe ich davon aus, dass der Unterschied in der Speicherzuordnung von JSON-Objekten liegt.

Wenn wir den Benchmark auf das reine Importieren reduzieren, stellen jsonwir fest, dass der Unterschied nicht so groß ist:

$ BENCHMARK="import timeit; print(timeit.timeit('import json;', number=5000))" $ docker run python:3-slim python -c "$BENCHMARK" 0.03683806210756302 $ docker run python:3-alpine3.6 python -c "$BENCHMARK" 0.038280246779322624 

Das Laden von Python-Bibliotheken sieht vergleichbar aus. Generieren list()erzeugt größere Unterschiede:

$ BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=5000))" $ docker run python:3-slim python -c "$BENCHMARK" 0.5666235145181417 $ docker run python:3-alpine3.6 python -c "$BENCHMARK" 0.6885563563555479 

Offensichtlich ist der teuerste Vorgang json.dumps(), der auf Unterschiede in der Speicherzuordnung zwischen diesen Bibliotheken hinweisen könnte.

Wenn man den Benchmark noch einmal betrachtet, muslist die Speicherzuordnung wirklich etwas langsamer:

 musl | glibc -----------------------+--------+--------+ Tiny allocation & free | 0.005 | 0.002 | -----------------------+--------+--------+ Big allocation & free | 0.027 | 0.016 | -----------------------+--------+--------+ 

Ich bin mir nicht sicher, was mit "großer Zuteilung" gemeint ist, aber es muslist fast um das 2-fache langsamer, was bedeutsam werden kann, wenn Sie solche Operationen tausend- oder millionenfach wiederholen.

Nur wenige Korrekturen. musl ist nicht die _own_-Implementierung von glibc von alpine. 1st musl ist keine (Neu-) Implementierung von glibc, sondern eine unterschiedliche Implementierung von ** libc ** pro POSIX-Standard. Die 2. Musl ist nicht die alte Sache von Alpine, es ist ein eigenständiges, nicht verwandtes Projekt, und Musl wird nicht nur in Alpine verwendet. Jakub Jirutka vor 7 Jahren 5
@ JakubJirutka danke für Korrekturen Tombart vor 7 Jahren 1
Angesichts der Tatsache, dass musl libc wie ein besseres auf Standards basierendes * scheint, ganz zu schweigen von einer neueren Implementierung, warum scheint die glibc in diesen Fällen unterzugehen? * vgl. https://wiki.musl-libc.org/funktionsdifferenzen-von-glibc.html Forest vor 6 Jahren 0