Ich habe den gleichen Benchmark wie Sie ausgeführt und nur Python 3 verwendet:
$ 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 von libc
( Basissystembibliothek ) als das musl- Projekt ( Spiegel-URL ). Es gibt viele Unterschiede zwischen diesen Bibliotheken . Infolgedessen kann jede Bibliothek in bestimmten Anwendungsfällen eine bessere Leistung erbringen.
Hier ist ein Unterschied zwischen diesen Befehlen oben . Die Ausgabe beginnt sich von Zeile 269 zu unterscheiden. Natürlich gibt es verschiedene Adressen im Speicher, aber ansonsten ist es sehr ähnlich. Die meiste Zeit wird offensichtlich damit verbracht, auf das python
Ende des Befehls zu warten .
Nach der Installation strace
in beiden Containern erhalten wir eine interessantere Ablaufverfolgung (ich habe die Anzahl der Iterationen im Benchmark auf 10 reduziert).
Beispielsweise glibc
ist 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
Der gleiche 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 Hauptunterschied ist, aber die Reduzierung der Anzahl der E / A-Vorgänge in Kernbibliotheken könnte zu einer besseren Leistung beitragen. Aus dem Diff können Sie ersehen, dass das Ausführen des gleichen Python-Codes zu geringfügig unterschiedlichen Systemaufrufen führen kann. Wahrscheinlich könnte das Wichtigste bei der Optimierung der Schleifenleistung getan werden. Ich bin nicht qualifiziert genug, um beurteilen zu können, ob das Leistungsproblem durch Speicherzuweisung oder eine andere Anweisung verursacht wird.
glibc
mit 10 Iterationen:
write(1, "0.032388824969530106\n", 210.032388824969530106)
musl
mit 10 Iterationen:
write(1, "0.035214247182011604\n", 210.035214247182011604)
musl
ist um 0.0028254222124814987 Sekunden langsamer. Da der Unterschied mit der Anzahl der Iterationen zunimmt, würde ich annehmen, dass der Unterschied in der Speicherzuordnung von JSON-Objekten liegt.
Wenn wir den Benchmark auf den reinen Import reduzieren, stellen json
wir 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 die teuerste Operation json.dumps()
, die auf Unterschiede in der Speicherzuordnung zwischen diesen Bibliotheken hindeuten könnte.
Wenn Sie sich den Benchmark noch einmal ansehen ,
musl
ist die Speicherzuweisung 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ße Zuweisung" gemeint ist, aber es musl
ist fast 2 × langsamer, was bedeutsam werden kann, wenn Sie solche Vorgänge Tausende oder Millionen Mal wiederholen.