Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

15.09.2024

З Pascal в NumPy

Натрапив на питання про те, як прискорити програму з обчисленнями на Pascal. Чудова задача на оптимізацію, подумав я, та вирішив спробувати NumPy, як топове рішення для обробки математичних даних… з яким я не мав практичного досвіду.

🐍 Перший очевидний результат: наївне переписування на Python уповільнює програму у 160 разів. Очевидний тому, що в Python повільний тип “масив” (насправді “список”) та й взагалі, все що я писав про повільність Ruby, стосується й пітону.

⛴️ Проте так само очевидно, що Python беруть для використання бібліотек на кшталт NumPy. Головною ідеєю тут є векторизація. Так називають два різних підходи. На високому рівні — це виклик функцій низькорівневої мови для обробки цілих масивів замість окремих значень. На низькому рівні — це використання спеціальних інструкцій процесора SIMD: єдина інструкція — багато даних. В разі NumPy обидва підходи мають місце.

🚤 Версія з NumPy за швидкістю близька до оригіналу на Pascal. Що досить непоганий результат, якщо врахувати, що програма написана на Python, де не потрібно оголошувати змінні та можна запускати програму по шматках в Jupyter.

↔️ Єдиним неясним місцем в перекладі було фільтрування масивів. Як виявилось, в NumPy для того є дві семантики: take вибирає з масиву за масивом індексів, а extract - за масивом-“маскою”. Але було неочевидно, що функції filter немає. Зате приємно здивувало, що можна прочитати файл з рядками чисел однією функцією readfile.

🐢 Чому вона не швидше? Для мене відкриттям стало, що NumPy не включає майже ніякого паралелізму. Тільки для окремих операцій — особливо над матрицями. А на цьому прикладі я бачив тільки завантаження одного ядра на 100%. Тобто, в цілому, як програма на Pascal, так і на Python/NumPy робила однакову кількість арифметичних операцій — не дивно, що швидкість приблизно однакова.

🐇 Можливо, десь SIMD чи інші оптимізації й дали прискорення, але з іншого боку, програма на Pascal виділяє памʼять тільки один раз — а на Python нові масиви створюються декілька разів на ітерацію. Що точно дає затримку.

🤔 Залишилося перевірити, якої оптимізації можна було досягнути низькорівневим рішенням.