Alpine DockerイメージがUbuntuイメージよりも50%以上遅いのはなぜですか?


35

私のPythonアプリケーションは、UbuntuでDockerなしで実行するよりも、実行するほうがはるかに遅いことに気付きましたpython:2-alpine3.6。2つの小さなベンチマークコマンドを思いついたのですが、Ubuntuサーバーで実行する場合とDocker for Macを使用する場合の両方で、2つのオペレーティングシステムの間に大きな違いがあります。

$ 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

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

この違いの原因は何ですか?


1
@Sethの外観:bashがインストールされた後、起動されたbashシェル内でタイミングが開始される
Underyx

回答:


45

Python 3のみを使用して、同じベンチマークを実行しました。

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

2秒以上の差が生じる:

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

AlpineはlibcmuslプロジェクトミラーURL)とは異なる(ベースシステムライブラリ)の実装を使用していますこれらのライブラリには多くの違いがあります。その結果、特定のユースケースで各ライブラリのパフォーマンスが向上する場合があります。

ここだ上で、これらのコマンドの間にstraceのdiffが。出力は269行目とは異なります。もちろん、メモリには異なるアドレスがありますが、それ以外は非常に似ています。ほとんどの時間は、明らかにpythonコマンドが終了するのを待つことに費やされます。

strace両方のコンテナにインストールすると、より興味深いトレースを取得できます(ベンチマークの反復回数を10回に減らしました)。

たとえばglibc、次の方法でライブラリをロードしています(行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

の同じコード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

これが重要な違いだとは言いませんが、コアライブラリのI / O操作の数を減らすと、パフォーマンスが向上する可能性があります。diffから、まったく同じPythonコードを実行すると、システムコールがわずかに異なる可能性があることがわかります。おそらく、ループパフォーマンスの最適化で最も重要なことができます。パフォーマンスの問題がメモリ割り当てまたは他の命令によって引き起こされているかどうかを判断する資格がありません。

  • glibc 10回の反復:

    write(1, "0.032388824969530106\n", 210.032388824969530106)
    
  • musl 10回の反復:

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

musl0.0028254222124814987秒遅くなります。繰り返しの数とともに違いが大きくなるにつれて、JSONオブジェクトのメモリ割り当てに違いがあると思います。

ベンチマークをインポートのみに減らすと、jsonその差はそれほど大きくないことに気付きます。

$ 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

Pythonライブラリのロードは同等に見えます。生成list()により大きな違いが生じます。

$ 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

明らかに最も高価な操作はjson.dumps()であり、これらのライブラリ間のメモリ割り当ての違いを示している可能性があります。

再び見るとベンチマークmuslメモリ割り当てにわずかに遅い実際にあります:

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

「大きな割り当て」が何を意味するのかはわかりませんmuslが、ほぼ2倍遅くなります。


12
わずかな修正。muslは、Alpine 独自のglibc 実装ではありません。最初のmuslはglibcの(再)実装ではなく、POSIX標準ごとのlibcの異なる実装です。2番目のmuslはAlpine 独自のものではなく、独立した独立したプロジェクトであり、muslはAlpineだけで使用されるわけではありません。
ヤクブジルトカ

musl libcはより良い標準ベース*のように思えますが、新しい実装は言うまでもなく、なぜこれらの場合にglibcを下回るのでしょうか?* cf。wiki.musl-libc.org/functional-differences-from-glibc.html
フォレスト

0.0028秒の差は統計的に有意ですか?相対偏差はわずか0.0013%で、10個のサンプルを取得しています。これらの10回の実行の(推定)標準偏差(または最大と最小の差)は何でしたか?
ピーターモーテンセン

@PeterMortensenベンチマーク結果に関する質問については、Eta Labsコードを参照してください:etalabs.net/libc-bench.html Eg mallocストレステストは100k回繰り返されます。結果は、いくつかの側面を挙げると、ライブラリのバージョン、GCCのバージョン、使用されているCPUに大きく依存する可能性があります。
トムバート
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.