python vs bc при оцінці 6 ^ 6 ^ 6


29

Я оцінюю вираз 6^6^6за допомогою pythonта bcокремо.

Вміст файлу python є print 6**6**6. Коли я виконую time python test.py, я отримую вихід як

real        0m0.067s
user        0m0.050s
sys         0m0.011s

А потім я запустив команду, time echo 6^6^6 | bcяка дала мені наступний вихід

real        0m0.205s
user        0m0.197s
sys         0m0.005s

З цих результатів видно, що час синхронізації python та bc становив відповідно 11 мс та 5 мс. Команда bc перевершила python на рівні часу sys, але коли мова йде про користувачів і в реальному часі, python був майже в 4 рази швидшим, ніж bc . Що, можливо, пішло туди. Я не віддав жодного пріоритету процесам як таким. Я намагаюся зрозуміти цю ситуацію.


Отже, ви маєте на увазі, що компонент sys приділяє лише час, необхідний для завантаження, і час виконання буде вказано в користувальницькому компоненті виводу?
ganessh

Я дійсно не впевнений, тому я опублікував коментар. Це просто здогад.
тердон

7
echo | bcпередбачає запуск нижньої оболонки через трубу - саме звідси, ймовірно, прийшов деякий ваш додатковий час користувача. Щоб зробити це справедливим тестом, сценарій python повинен читати зі stdin, щоб ви могли time echo 6**6**6 | whatever.py.
goldilocks

1
Я вважаю за краще ввести командний рядок be в сценарій і вчасно виконати його. Або використовувати echo 6^6^6 | time bc.
Даніель Куллманн

1
Бічна примітка: у python 6**6**6вираження фактично обчислюється під час компіляції . Однак оскільки ви запускаєте файл безпосередньо, а не імпортувати його з модуля, це не має значення. Щоб побачити різницю, поміщену 10**12345678у a.pyфайл, і спробуйте імпортувати його з інтерактивного перекладача. Потім закрийте перекладач, перезапустіть його та aзнову імпортуйте . Перший раз це повинно зайняти помітну кількість часу (тому що python збирає модуль), тоді як другий раз він завантажує той .pyc, який повинен бути миттєвим,
Bakuriu

Відповіді:


25

Python імпортує велику кількість файлів при запуску:

% python -c 'import sys; print len(sys.modules)'
39

Кожен з них потребує ще більшої кількості спроб відкрити файл Python, оскільки існує багато способів визначення модуля:

% python -vv -c 'pass'
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
# trying site.so
# trying sitemodule.so
# trying site.py
# trying site.pyc
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/sitemodule.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.py
# /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.pyc matches /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.py
import site # precompiled from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.pyc
# trying os.so
# trying osmodule.so
# trying os.py
# trying os.pyc
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/osmodule.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py
# /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc matches /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py
import os # precompiled from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc
    ...

Кожна «спроба», за винятком вбудованих, потребує викликів на рівні ОС / системи, і кожен «імпорт», здається, запускає близько 8 «пробних» повідомлень. (Існували способи зменшити це за допомогою zipimport, і кожен шлях у вашому PYTHONPATH може потребувати іншого дзвінка.)

Це означає, що перед запуском Python на моїй машині є майже 200 статичних системних дзвінків, і "time" призначає це "sys", а не "user", тому що користувацька програма чекає системи, щоб зробити щось.

Для порівняння, і, як сказав тердон, "bc" не має такої високої вартості запуску. Дивлячись на вихід dtruss (у мене Mac; "strace" для ОС на базі Linux), я бачу, що bc не робить жодних відкритих () або stat () системних викликів, за винятком завантаження кількох спільних бібліотеки - це початок, що, звичайно, і Python. Крім того, у Python є більше файлів для читання, перш ніж він буде готовий обробити що-небудь.

Очікування диска відбувається повільно.

Ви можете зрозуміти вартість запуску Python, виконавши:

time python -c pass

На моїй машині це 0,032 секунди, тоді як "друк 6 ** 6 ** 6" - 0,072 секунди, тому вартість запуску становить 1/2 загального часу, а розрахунок + переведення в десятковий - друга половина. Поки:

time echo 1 | bc

займає 0,005s, а "6 ^ 6 ^ 6" займає 0,184s, тому експонація bc в 4 рази повільніше, ніж у Python, хоча це на 7 разів швидше, щоб почати.


4
Ви ніби поховали свинець там. Ви можете перенести кінцевий біт до вершини.
Рікінг

Просто з інтересу на моїй машині: час пітон -c «пройти» 0m0.025s, час пітон -c «надрукувати 6 6 6" 0m0.087s але час пітона -c «х = 6 6 6" 0m0.028s Так більшість з час виводить велику кількість.
Стів Барнс

Так, перетворення на базу 10 займає квадратичний час у кількості цифр. Як крайній випадок, спробуйте надрукувати один із великих праймерів Мерсенна. Це дуже швидко обчислити, але для друку в базі 10. потрібно багато часу.
Ендрю Далке

11

Я знайшов гарну відповідь на ТАК, пояснюючи різні поля:

  • Справжнім є настінний годинник - час від початку до кінця розмови. Це весь минулий час, включаючи часові відрізки, використовувані іншими процесами, і час, який процес витрачає заблоковано (наприклад, якщо він чекає завершення вводу / виводу).

  • Користувач - це кількість часу процесора, проведеного в коді в режимі користувача (за межами ядра) в процесі. Це лише фактичний час процесора, який використовується для виконання процесу. Інші процеси та час, який процес витрачає заблоковано, не враховуються до цієї цифри.

  • Sys - це кількість часу процесора, проведеного в ядрі в процесі. Це означає виконання часу процесора, витраченого на системні виклики в межах ядра, на відміну від коду бібліотеки, який все ще працює в просторі користувача. Як і "користувач", це лише час процесора, який використовується процесом. Нижче див. Короткий опис режиму ядра (також відомий як режим "супервізор") та механізму системного виклику.

Отже, у вашому конкретному прикладі версія python є швидшою щодо фактичного часу, необхідного для її завершення. Однак підхід python проводить більше часу в просторі ядра, здійснюючи виклики до функцій ядра. bcКоманда не витрачає по суті немає часу в просторі ядра , і весь цей час , проведене в просторі користувача, імовірно , працює внутрішній bcкод.

Це не має для вас ніякої різниці. Єдина інформація, яка вас дійсно хвилює, це те, realякий фактичний час минув між запуском команди та отриманням її виводу.

Ви також повинні знати, що ці крихітні відмінності не є стабільними, вони також залежатимуть від навантаження вашої системи і будуть змінюватися щоразу, коли ви запускаєте команду:

$ for i in {1..10}; do ( time python test.py > /dev/null ) 2>&1; done | grep user
user    0m0.056s
user    0m0.052s
user    0m0.052s
user    0m0.052s
user    0m0.060s
user    0m0.052s
user    0m0.052s
user    0m0.056s
user    0m0.048s
user    0m0.056s

$ for i in {1..10}; do ( time echo 6^6^6 | bc > /dev/null ) 2>&1; done | grep user
user    0m0.188s
user    0m0.188s
user    0m0.176s
user    0m0.176s
user    0m0.172s
user    0m0.176s
user    0m0.180s
user    0m0.172s
user    0m0.172s
user    0m0.172s

10

Я поясню це з іншої точки зору.

Якщо чесно, це bcмає перевагу, оскільки йому не потрібно нічого читати з диска, а потрібні лише його blob / binaries, тоді як python повинен імпортувати серію модулів + читання файлу. Тож ваш тест може бути упередженим bc. Щоб фактично його перевірити, слід використовувати bc -q fileтам, де fileміститься:

6^6^6
quit

Змінення саме цього змінило час використання echo:

bc  0.33s user 0.00s system 80% cpu 0.414 total

Щоб використовувати файл:

bc -q some  0.33s user 0.00s system 86% cpu 0.385 total

(вам доведеться використовувати метод тердона, щоб помітити більші відмінності, але принаймні ми знаємо, що вони є)

Тепер, з точки зору python, python потребує читання з диска, компілювання та виконання кожного разу, а також завантаження модулів як Ендрю вказує , що робить час виконання повільнішим. Якщо ви компілюєте байт-код сценарію python, ви помітите, що для виконання коду потрібно 50% менше всього часу:

python some.py > /dev/null  0.25s user 0.01s system 63% cpu 0.413 total

складено:

./some.pyc  0.22s user 0.00s system 77% cpu 0.282 total

Як бачите, існує кілька факторів, які можуть впливати на час виконання між різними інструментами.


3

Я мав перевагу прочитати інші відповіді. Для початківців такі люди, як я, повинні знати причину, чому ми маємо справу з таким величезним цілим числом, це те, що обидва Pythonі bcробимо розширення прямо-асоціативної експоненції, а це означає, що це 6^36ми не оцінюємо, а скоріше 6^46656значно більше. 1

Використовуючи варіанти в наступних командах, ми можемо отримати середнє значення для конкретного елемента виводу як timeзарезервованого слова, так і команди:

for i in {1..1000}; do (time echo 6^6^6 | bc > /dev/null) 2>&1; done | grep 'rea' | sed -e s/.*m// | awk '{sum += $1} END {print sum / NR}'

for i in {1..1000}; do (/usr/bin/time -v sh -c 'echo 6^6^6 | bc > /dev/null') 2>&1; done | grep 'Use' | sed -e s/.*:// | awk '{sum += $1} END {print sum / NR}'

Можна піти іншим маршрутом і повністю видалити файл із порівняння. Крім того, ми можемо порівняти час Bc з чимось на зразок dcкоманди, оскільки історично перший є "процесором на передньому кінці" до другого. Наступні команди були приурочені:

echo 6^6^6 | bc
echo 6 6 6 ^ ^ p | dc
echo print 6**6**6 | python2.7

Зверніть увагу, що dcкоманда ліво-асоціативна для експоненції. 2

У нас є кілька результатів з time(bash) за 1000 ітерацій (у секундах):

0.229678 real bc
0.228348 user bc
0.000569 sys bc
0.23306  real dc
0.231786 user dc
0.000395 sys dc
0.07 real python
0.065907 user python
0.003141 sys python

bcі dcпропонують порівнянні показники в цьому контексті.

Менш точні 3 результати, /usr/bin/timeтобто timeкоманда GNU (точність шкали тут не вірна, але результати схожі):

0.2224 user bc
0 sys bc
0.23 Elapsed bc
0.22998 user dc
0 sys dc
0.23 Elapsed dc
0.06008 user python
0 sys python
0.07 Elapsed python

Перевагою цього /usr/bin/timeє те, що він пропонує -vваріант, який дає набагато більше інформації, яка може бути корисною з часом.

Можна також оцінити це внутрішньо, так би мовити з timeitмодулем Python:

python2.7 -m timeit -n 1000 -r 1 'print 6**6**6' | grep 'loops'
1000 loops, best of 1: 55.4 msec per loop

Це трохи швидше, ніж те, що ми бачили раніше. Спробуємо сам перекладач:

>>> import timeit
>>> import sys
>>> import os
>>> T = timeit.Timer("print 6**6**6")
>>> n = int(1000)
>>> f = open(os.devnull, 'w')
>>> sys.stdout = f
>>> t = t.timeit(n)
>>> sys.stdout = sys.__stdout__
>>> print t/n
0.0553743481636

Це найшвидше, що я бачив.


Якщо ми оцінюємо як меншу експоненцію 6^6, то команда time дає дивовижні результати - використовуючи ті самі forкоманди циклу, які ми використовували, ми маємо:

0.001001 bc real
0.000304 user
0.000554 sys
0.014    python real i.e. 10x more than bc??
0.010432 user
0.002606 sys

Так що з меншим цілим числом bcраптом набагато швидше ?? Від перезавантаження системи до другого запуску значення не має. Але в той же час, якщо ми використовуємо timeitдля Python, ми отримуємо:

python2.7 -m timeit -n 100000 -r 1 'print 6**6' | grep loops  
100000 loops, best of 1: 0.468 usec per loop

Це мікросекунди , а не мілісекунди, тому це не відповідає набагато повільнішим результатам за допомогою forциклу. Можливо, для перевірки цього потрібні інші інструменти, і, як інші пояснили, тут більше, ніж очі. Здається, Python був швидшим у сценарії запитання, але не ясно, чи можна зробити висновки поза цим ...


1. Зайве говорити, що це виходить за межі чогось такого, як арифметичне розширення ехо, тобто echo $((6**6**6))- bashтакож буває правильним асоціативним для цього, тобто 6^6^6 = 6^(6^6).

2. Порівняйте з цим: 6 6 ^ 6 ^ p.

3. Можливо, команда часу GNU надає додаткову інформацію при запуску на BSD UNIX (GNU time info document): Більшість інформації, показаної 'time', походить із системного виклику 'wait3'. Цифри такі ж хороші, як і ті, які повернув "wait3". Багато систем не оцінюють усі ресурси, про які „час” може звітувати; ці ресурси повідомляються як нульові. Системи, які вимірюють більшість або всі ресурси, базуються на 4.2 або 4.3BSD. Пізніше випуски BSD використовують різний код управління пам'яттю, який вимірює менше ресурсів. - У системах, у яких немає виклику "wait3", який повертає інформацію про стан, замість цього використовується системний виклик "times". Він надає набагато менше інформації, ніж "wait3", тому в цих системах "час" повідомляє більшість ресурсів як нуль.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.