Neden Alpine Docker resmi Ubuntu resminden% 50'nin üzerinde daha yavaş?


35

Çalıştırırken Python uygulamamın Ubuntu’da Docker olmadan çalıştırmaktan çok daha yavaş olduğunu fark ettim python:2-alpine3.6. İki küçük benchmark komutuyla karşılaştım ve iki işletim sistemi arasında hem Ubuntu sunucusunda çalıştırdığımda hem de Mac için Docker kullandığımda görülebilen çok büyük bir fark var.

$ 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'u kullanmayan şu 'benchmark'ı da denedim:

$ 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

Bu farklılığa ne sebep olabilir?


1
@Seth tekrar bakın: zamanlama bash kurulduktan sonra, bash bash kabuğunun içine başlar
Underyx

Yanıtlar:


45

Sadece Python 3 kullanarak aynı kriteri kullandım:

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

2 saniyeden fazla farkla sonuçlanır:

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

Alpine, musl projesinden farklı bir uygulama libc(temel sistem kütüphanesi) kullanıyor ( ayna URL'si ). Bu kütüphaneler arasında birçok farklılık var . Sonuç olarak, her kütüphane belirli kullanım durumlarında daha iyi performans gösterebilir.

İşte yukarıdaki komutlar arasında saplı bir fark var . Çıktı, satır 269'dan farklı olmaya başlar. Tabii ki, bellekte farklı adresler vardır, ancak aksi halde çok benzer. Çoğu zaman pythonkomutun bitmesini beklemekle harcanır .

Her straceiki kaba da kurduktan sonra daha ilginç bir iz elde edebiliriz (Ben kıyaslamadaki yineleme sayısını 10'a düşürdüm).

Örneğin, glibckütüphaneleri aşağıdaki şekilde yüklüyor (satır 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

Aynı kod 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

Bunun önemli bir fark olduğunu söylemiyorum, ancak çekirdek kütüphanelerdeki G / Ç işlemlerinin sayısının azaltılması daha iyi performansa katkıda bulunabilir. Farklılıktan, aynı Python kodunun çalıştırılmasının biraz farklı sistem çağrılarına neden olabileceğini görebilirsiniz. Muhtemelen en önemlisi döngü performansını optimize etmede yapılabilir. Performans sorununun bellek ayırma veya başka bir talimattan kaynaklanıp kaynaklanmadığını yargılayacak kadar nitelikli değilim.

  • glibc 10 yineleme ile:

    write(1, "0.032388824969530106\n", 210.032388824969530106)
    
  • musl 10 yineleme ile:

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

musldaha yavaş 0,0028254222124814987 saniye daha yavaş. Fark, yinelemelerin sayısıyla birlikte büyüdükçe, farkın JSON nesnelerinin hafıza tahsisinde olduğunu varsayarım.

Göstergeyi yalnızca ithal etmeye jsonindirirsek, farkın çok büyük olmadığını fark ediyoruz:

$ 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 kütüphanelerinin yüklenmesi karşılaştırılabilir görünüyor. Üretmek list()daha büyük farklar üretir:

$ 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

Açıkçası, en pahalı işlem, json.dumps()bu kütüphaneler arasında bellek paylaşımındaki farklılıklara işaret edebilir.

Tekrar bakıldığında kriter , muslbellek ayrılmasına biraz daha yavaş gerçekten:

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

"Büyük tahsisat" ile neyin kastedildiğinden emin değilim, ancak muslneredeyse 2 kat daha yavaş, bu tür işlemleri binlerce veya milyon kez tekrarladığınızda önemli olabilir.


12
Sadece birkaç düzeltme. musl, Alpine'nin kendi glibc uygulaması değildir . 1. musl (yeniden) bir glibc uygulaması değil, POSIX standardına göre farklı libc uygulamasıdır . 2. musl Alpine'ın kendi işi değil, bağımsız, ilgisiz bir proje ve musl sadece Alpine'de kullanılmıyor.
Jakub Jirutka 25:17

musl libc'nin daha iyi bir standart gibi göründüğü göz önüne alındığında *, daha yeni bir uygulamadan bahsetmiyorum bile, neden bu durumlarda glibc'den daha iyi bir performans sergilediğini düşünüyorsunuz? * cf. wiki.musl-libc.org/functional-differences-from-glibc.html
Orman

0.0028 saniye arasındaki fark istatistiksel olarak anlamlı mı? Bağıl sapma sadece% 0,0013'tür ve 10 örnek alıyorsunuz. Bu 10 çalışma için (tahmini) standart sapma neydi (ya da maksimum-dakika farkı)?
Peter Mortensen

@PeterMortensen Karşılaştırma sonuçlarıyla ilgili sorularınız için Eta Labs koduna başvurmalısınız: etalabs.net/libc-bench.html Örneğin, malloc stres testi 100k kez tekrarlandı. Sonuçlar büyük ölçüde sadece birkaç yönü belirtmek için kütüphane sürümüne, GCC sürümüne ve kullanılan CPU'ya bağlı olabilir.
Tombart
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.