Какие знаешь методы оптимизации CPU bound задач в Python

Ответ

Для оптимизации CPU-bound задач в Python можно использовать:

  1. Многопроцессорность (модуль multiprocessing), так как GIL блокирует потоки:
from multiprocessing import Pool

def heavy_computation(x):
    return x * x

with Pool(4) as p:
    result = p.map(heavy_computation, range(10))
  1. Cython - компиляция Python-кода в C для ускорения.

  2. Numba - JIT-компилятор для числовых операций:

from numba import jit

@jit(nopython=True)
def fast_func(x):
    # ускоренный код
    return x * x
  1. Использование C-расширений (ctypes, CFFI).

  2. Оптимизация алгоритмов - выбор более эффективных алгоритмов (O(n) вместо O(n²)).

  3. NumPy - для векторных операций вместо циклов.

Для I/O-bound задач лучше подходят асинхронные подходы (asyncio) или потоки.

Ответ 18+ 🔞

А, ну ты про то, как в Питоне эту ебучую GIL обойти, чтобы железо на полную выжимать? Да, история древняя, как говно мамонта. Сидит этот питонячий глобальный интерпретатор, как манда с ушами, и не даёт потокам нормально работать. Короче, слушай сюда, варианты есть.

Если у тебя задачи CPU-bound, то есть чистая математика, перемножение матриц до посинения, то потоки — это пиздец, они в очередь встанут. Тут надо процессы запускать, у каждого свой GIL будет. Модуль multiprocessing тебе в помощь. Смотри, как просто:

from multiprocessing import Pool

def heavy_computation(x):
    return x * x

with Pool(4) as p:
    result = p.map(heavy_computation, range(10))

Вот так, блядь, размазал работу по четырём ядрам и поехали. Но только передавать туда-сюда данные между процессами — это отдельная песня, овердохуища накладных расходов может быть.

Второй фокус — это Cython. Берёшь свой питонячий код, немного начиняешь статической типизацией, как колбасу салом, и компилируешь в C. Получается почти нативный код, летает. Но это уже, блядь, почти другой язык учить.

Третий вариант, особенно для числодробилки — Numba. Это просто волшебство, ёпта. Над функцией декоратор повесил, и она в машинный код компилируется прямо при первом запуске.

from numba import jit

@jit(nopython=True)
def fast_func(x):
    # ускоренный код
    return x * x

Выглядит как Python, а работает — огонь. Но поддерживает не все конструкции, это да.

Ну и классика жанра — писать критические куски на C/C++ и прикручивать через ctypes или CFFI. Это для настоящих мазохистов, которые любят прострелить себе оба колена, но зато потом хвастаться.

А ещё, чувак, самый главный пункт, который все забывают — оптимизация алгоритмов. Можно хоть на ассемблере писать, но если у тебя алгоритм квадратичной сложности на миллионе элементов, то ты просто мудак. Сначала мозги включи, потом уже процессы плоди.

И для операций с массивами — NumPy, конечно. Он всё векторизовано делает на низком уровне, циклы на питоне для этого — чистое самоубийство.

А вот если задачи I/O-bound (сеть, диск, база данных), то тут процессы — это из пушки по воробьям. Тут уже asyncio или хотя бы потоки рулят. GIL на операциях ввода-вывода отпускает, так что можно параллельно много чего ждать.

Короче, выбор инструмента зависит от задачи. Не будь, как Герасим, который всё одним методом — "утопить". Соображай, что ты делаешь.