Чому зображення Alpine Docker на 50% повільніше, ніж зображення Ubuntu?


35

Я помітив, що мій додаток Python працює набагато повільніше, python:2-alpine3.6ніж його запускати без Docker в Ubuntu. Я придумав дві невеликі контрольні команди, і між двома операційними системами помітна величезна різниця, як при запуску їх на сервері Ubuntu, так і при використанні Docker для 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

Я також спробував наступний 'бенчмарк', який не використовує 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
@ Подивіться ще раз: терміни починаються після встановлення 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 використовує іншу реалізацію libc(базова бібліотека системи) від проекту musl ( дзеркальна URL-адреса ). Існує багато відмінностей між цими бібліотеками . Як результат, у певних випадках кожна бібліотека може працювати краще.

Ось ця різниця між цими командами вище . Вихід починає відрізнятися від рядка 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

Я не кажу, що це ключова відмінність, але зменшення кількості операцій вводу / виводу в основних бібліотеках може сприяти кращій роботі. З різниці видно, що виконання того самого коду Python може призвести до дещо інших системних викликів. Напевно, найважливіше можна зробити в оптимізації продуктивності циклу. Я недостатньо кваліфікований, щоб оцінити, чи викликана продуктивністю розподіл пам'яті чи інша інструкція.

  • glibc з 10 ітераціями:

    write(1, "0.032388824969530106\n", 210.032388824969530106)
    
  • musl з 10 ітераціями:

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

muslповільніше на 0,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 - це не власна реалізація glibc Alpine . 1-я musl - це не (повторна) реалізація glibc, а інша реалізація libc за стандартом POSIX. 2-й мусл - це не власна альпійська річ, це окремий, не пов'язаний між собою проект, і musl використовується не тільки в альпійській мові.
Якуб Жирутка

зважаючи на те, що musl libc здається кращим на основі більш стандартів *, не кажучи вже про новішу імплементацію, чому, здається, у цих випадках низька ефективність glibc? * пор. wiki.musl-libc.org/functional-differences-from-glibc.html
Ліс

Чи є різницею 0,0028 секунд статистично значущою? Відносне відхилення становить лише 0,0013%, і ви берете 10 проб. Яким було (розрахункове) стандартне відхилення для цих 10 пробіжок (або навіть максимальна хв різниця)?
Пітер Мортенсен

@PeterMortensen Для запитань щодо результативних показників слід звернутися до коду лабораторії Eta: etalabs.net/libc-bench.html Напружений тест на малоквороту повторюється в 100 тисяч разів. Результати можуть бути сильно залежними від бібліотечної версії, використовуваної версії GCC та процесора, щоб назвати лише декілька аспектів.
Томбарт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.