Почему numy ndarray намного медленнее, чем списки для простых циклов? - PullRequest
0 голосов
/ 15 января 2019

Я новичок в Python, поэтому я решил решить некоторые общие проблемы, чтобы улучшить мои знания языка. Я узнал о Numpy и его эффективных ndarrays, поэтому я попытался следующий эксперимент:

Рассмотрим проблему 2 сумм (например, здесь ) и давайте решим ее наивным способом (для целей этого вопроса это не имеет значения). Вот решение со списками Python:

from  itertools import combinations

def twosum1(n_lst):
    pairs=list(combinations(n_lst,2))
    solutions=[]
    for pair in pairs:
        if sum(pair)==7: solutions.append(pair)
    return(solutions)

Затем я создал версию, используя np.arrays, ожидая, что она резко ускорит вычисления:

from  itertools import combinations
import numpy as np

def twosum2(n_lst):
    pairs=np.array(list(combinations(n_lst,2)),dtype=int)
    return pairs[pairs[:,1]+pairs[:,0]==7]

Однако после синхронизации двух функций twosum2 примерно в 2 раза медленнее, чем twosum1. Поэтому я подумал, что проблема может быть в динамическом выборе элементов, поэтому я написал точную копию twosum1, заменив списки на ndarrays ...

def twosum3(n_lst):
    pairs=np.array(list(combinations(n_lst,2)))
    solutions=np.empty((0,2))
    for pair in pairs:
        if np.sum(pair)==7: 
            solutions=np.append(solutions,[pair],axis=0)
    return(solutions)

... и результирующая функция была в 10 раз медленнее, чем оригинал!

Как это возможно? Что я тут не так делаю? Очевидно, что удаление циклов и замена списков на ndarrays недостаточно для ускорения (в отличие от того, что я изучил, прочитав this ).

Edit:

  • Я использую% timeit в jupyter для определения времени функций.
  • Я беру одинаковые тесты для всех функций, которые я синхронизирую.
  • Тот факт, что я вычисляю комбинации одним и тем же способом в 3 функциях, говорит мне, что замедление происходит из-за клева ... но не понимаю, как.

1 Ответ

0 голосов
/ 15 января 2019

Дорогая операция - np.array(list(combinations(n_lst,2)),dtype=int), потому что python должен сканировать каждого члена списка, проверить, является ли элемент 'int-совместимым', преобразовать его в целое число и сохранить его в массиве.

Чтобы достичь производительности numpy, вы должны представить весь алгоритм numpy. Например:

In [63]: n_lst=list(range(100))

In [64]: %timeit twosum1(n_lst)
11.2 ms ± 1.64 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)


In [65]: np.vstack(np.where(np.add.outer(n_lst,n_lst)==7)).T
Out[65]: 
array([[0, 7],
       [1, 6],
       [2, 5],
       [3, 4],
       [4, 3],
       [5, 2],
       [6, 1],
       [7, 0]], dtype=int64)

In [66]: %timeit np.vstack(np.where(np.add.outer(n_lst,n_lst)==7)).T
306 µs ± 19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Таким образом, вы выиграете фактор от 30 до 100, в зависимости от проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...