Продуктивність Python: рідні цикли NumPy, зворотні виклики та витрати на перетворення
Керований числовий тест лише для ЦП відстежує ідентичні значення через цикли CPython, арифметику NumPy, оболонку зворотного виклику Python і скомпільований цикл Numba.
Керований числовий тест лише для ЦП відстежує ідентичні значення через цикли CPython, арифметику NumPy, оболонку зворотного виклику Python і скомпільований цикл Numba. NumPy з готовим вводом мав велику перевагу, тоді як перетворення з і назад у списки Python зменшило цю перевагу майже до нічиєї. Епізод пояснює межу виконання та як виміряти результат, який потрібен програмі.
Читати письмову версію (англійською) ↗
Що охоплює це відео
- CPython компілює вихідний код у байт-код. Повторна робота інтерпретатора/об'єкта та типізований рідний числовий цикл є різними механізмами виконання.
- Числова арифметика NumPy може передавати типізовані пакети скомпільованим операціям; арифметичний цикл все ще існує, а подання можуть мати нерозривні макети.
- Виміряйте перетворення та необхідне вихідне представлення. У цьому запуску перетворення списків Python в масиви та назад знищило велику перевагу готового масиву.
- Малі робочі навантаження можуть віддавати перевагу звичайному Python, оскільки фіксовані витрати на виклик масиву мають значення. Розуміння восьми значень виграло це виміряне порівняння.
- np.vectorize – це зручна оболонка, яка викликає Python для послідовних елементів; сам пакетний інтерфейс не забезпечує рідне числове виконання.
- Скомпільовані цикли синтаксису Python також можуть бути швидкими. Включіть витрати на холодну компіляцію/налаштування, коли вони мають значення для викликаючої сторони.
- Рідне виконання, CPU SIMD, багатопотоковість та бекенди GPU є окремими ідеями. Доступні функції ЦП не відстежують інструкції, використані в конкретному запуску.
Перекладена стенограма
Перекладено з оригінальної англійської розповіді. Доступне аудіо та субтитри контролюються YouTube.
Чому той самий ЦП може закінчити раніше?
0:00 Ви вважаєте, що Python потрібна нова мова, щоб стати швидким. Той самий ЦП швидше виконав наш пакет після переміщення циклу. Куди поділася робота? Коли зникає прискорення? Який ярлик повертає Python? Це The Daily Diff, за лаштунками. Слідкуйте за цим ярликом. CPython – це стандартна реалізація Python.
0:17 Він компілює ваш вихідний код у байт-код, потім виконує ці інструкції в інтерпретаторі. NumPy – це числова бібліотека, масиви якої зберігають значення з оголошеним типом даних. Його вбудована арифметика може передати цілий пакет скомпільованим операціям. Візьміть мільйон значень з плаваючою комою, піднесіть кожне до квадрату, потім додайте вихідне значення. Кожна версія отримує ідентичні значення та видає відповідні відповіді. Значення досить малі, щоб уникнути відмінностей в округленні.
0:40 На цій машині розуміння зайняло близько тридцяти трьох мілісекунд. NumPy зайняв менше однієї. Обидва працювали на ЦП. Це корисний доказ для цього робочого навантаження і жахливий некролог для мови програмування. Почніть зі звичайного циклу.
Яку роботу повторює цикл Python?
0:53 Python отримує наступне значення, виконує арифметику і додає відповідь. Потім він робить це знову. Множення дешеве. Організація наступного множення та обробка об'єктів Python навколо нього додає роботу щоразу. Це фактична дизасембльована функція, що тестується. Ітерація, множення та додавання відображаються як інструкції байт-коду. Версії та інтерпретатори Python роблять різні вибори.
1:14 Ці вимірювання належать до версії на екрані, яка є Python 3.10. Список Python зберігає посилання на об'єкти, тому різні види речей можуть ділити один список. Наше арифметичне завдання повністю використовує числа. Воно платить за загальну організацію, вимагаючи повторюваної послуги. Уявіть, що ви замовляєте ту саму каву мільйон разів і заповнюєте нову форму для кожної чашки. Кавомашина в порядку.
1:36 Паперова робота розвинула власну кар'єру.
Куди NumPy переміщує цикл?
1:38 NumPy змінює контракт. Цей масив містить значення з плаваючою комою фіксованого розміру з одним оголошеним типом. Наш масив є неперервним, тому його значення знаходяться разом у пам'яті. Представлення можуть мати інші макети. Арифметична операція знає, який тип даних вона отримує. Виклик передає пакет нативній операції. Цикл все ще відвідує значення всередині скомпільованого коду. Він може повторно використовувати знання про тип у цій роботі.
2:00 Python залишається особою, яка передає завдання. Ітамар Тернер Траурінг пояснює відмінність у Python Speed. Пакетні інтерфейси, рідні реалізації та векторні інструкції процесора – це окремі речі. Назва функції надає дуже мало доказів.
Чи означає "швидко" GPU або багато ядер ЦП?
2:13 Цей результат використовував NumPy на ЦП. Бекенд GPU, такий як CuPy, вибирає інше місце для масивів та операцій. Придбання GPU залишає цей звичайний цикл Python працювати там, де він вже працював. Векторні інструкції ЦП можуть обробляти кілька значень разом. NumPy має механізми для вибору підтримуваних ядер. Наш час виконання повідомляє про доступні функції, але не відстежує інструкції цього виразу. Приписування всього виміряного приросту векторним інструкціям перебільшило б докази.
2:40 Кошик для покупок GPU може залишатися закритим. Є щось глибоко задовільне у виправленні продуктивності, яке також скасовує покупку. На жаль, бенчмарк продовжується.
Коли зникає вражаюче прискорення?
2:49 З вісьмома значеннями виграло розуміння. Виклик механізму масиву коштував дорожче, ніж крихітне завдання. Більші готові масиви вирвалися вперед. Ми використовували повторні випробування та розігрітий процес. Новіші збірки Python можуть змінити точку переходу. Потім змініть те, що потрібно програмі. Почніть зі списку Python і закінчіть свіжим списком Python. NumPy повинен перетворити вхідні дані в масив і повернути відповідь назад в об'єкти Python.
3:11 Ці кроки належать до рахунку, якщо ваша програма їх потребує. За мільйон значень цей повний шлях зайняв близько двадцяти восьми мілісекунд. Розуміння також зайняло близько двадцяти восьми. Наше драматичне лідерство перетворилося на майже нічию. Швидка частина була реальною. Перевага залежала від того, щоб дані залишалися в числовому вигляді.
Які межі належать до бенчмарку?
3:28 Слідкуйте за даними. Якщо наступна операція приймає масив, збережіть масив. Якщо ви постійно конвертуєте між списками та масивами, виміряйте ці перетини. Зупинка перед дорогим переданням дає точні числа для неправильного питання. Множення цього масиву створює тимчасовий результат. Додавання вихідних значень створює вихідні дані. Рідне виконання все ще потребує пам'яті та переміщує дані.
3:47 Деякі операції приймають вихідний буфер, зменшуючи розподіл, коли ваш код може безпечно його повторно використовувати. Одна гарна лінія може замовити кілька великих буферів. Python Speed також вказує на цю проблему. Алокатор надзвичайно байдужий до вашої естетики.
Чи може скомпільований цикл Python також бути швидким?
4:00 Ви також можете зберегти синтаксис циклу та змінити його виконання. Numba може компілювати підтримувані числові функції Python у рідний код. Ось цикл з нашого тесту з його декоратором компілятора. Він все ще каже for. Дорогий шлях інтерпретатора змінився. Після компіляції цей цикл зайняв менше половини мілісекунди з готовими масивами. Сама компіляція зайняла близько секунди в цьому запуску. Ці витрати на налаштування були виключені з повторних вимірювань часу,
4:22 тому крихітна одноразова робота заслуговує на інше порівняння. Власний посібник Numba із задоволенням показує явні цикли. Початок міграції мови, не знайшовши гарячого шляху, — це складний спосіб уникнути відкриття профілювальника. Спочатку знайдіть, що споживає час. Очікування мережі, розбір об'єктів та масові арифметичні операції вимагають різних виправлень. Рідні операції з масивами допомагають, коли обчислення та дані підходять.
4:43 Виміряйте результат, який насправді потрібен вашому викликаючому. Тепер про той ярлик. У вас є функція Python, і ви хочете, щоб вона приймала масив. Обгортка з назвою vectorize звучить як відсутня кнопка продуктивності. Її документація містить важливе речення, яке чекає.
Що насправді виконує np.vectorize?
4:56 NumPy каже, що його обгортка vectorize надається головним чином для зручності. Її реалізація, по суті, є циклом for. Обгортка приймає пакети, але продовжує викликати вашу функцію Python для послідовних елементів. Інтерфейс став коротшим. Межа виконання залишалася зайнятою. Спостерігайте, як ті самі значення повертаються через зворотний виклик Python. З готовими масивами ця версія зайняла близько вісімдесяти семи мілісекунд. Вона була повільнішою за наше розуміння.
5:18 Корисна обгортка для розширення може зберегти саме ту роботу для кожного елемента, яку ви намагалися видалити. Перевірте реалізацію за пакетним інтерфейсом, зберігайте сумісні дані у їхній корисній формі та включіть перетворення до порівняння. Рідні, зворотні та скомпільовані цикли можуть виглядати схожими з точки зору викликаючого. Назва vectorize дала обіцянку, яку ваш секундомір ніколи не підписував. Збережіть його, коли вам потрібна зручність. Для продуктивності перевірте, який цикл виконується.
Що слід змінити у вашому коді?
5:41 Мій вердикт — SHIP IT для пакетної обробки виміряного числового гарячого шляху. Зберігайте звичайні цикли, коли вони вже добре працюють. Перевірте, де відбувається робота та що потрібно наступному викликаючому. Підпишіться, натисніть дзвіночок і розкажіть мені в коментарях, чи ви б поставили іншу оцінку. І це The Daily Diff на сьогодні. Я Ніко з Axrisi. Зливайте відповідально.
Джерела
- Universal functions (ufunc) basics — NumPy 2.2NumPy
- The N-dimensional array — NumPy 2.2NumPy
- numpy.vectorize — convenience and performance notesNumPy
- CPU/SIMD optimizations — NumPy 2.2NumPy
- dis — Disassembler for Python bytecodePython Software Foundation
- CPython 3.10.12 list representationCPython contributors
- The limits of Python vectorization as a performance techniqueItamar Turner-Trauring / Python⇒Speed
- Performance TipsNumba
- Basics of CuPyCuPy



