Оптимизация цикла с Numpy.tile: занимает больше времени? - PullRequest
0 голосов
/ 19 декабря 2018

У меня есть функция, которая принимает массив, позицию и число k.Функция создает все числа от нуля до k, а затем использует эти числа в качестве замены элемента в позиции pos для k копий входного массива.

Ваниль для версии цикла выглядит следующим образом:

def expand1(array,pos,k):
    res = []
    states = range(0,k)
    for state in states:
        aux = array.copy()
        aux[pos] = state
        res.append(aux)
    return(res) 

и это занимает около 770 нс ± 6,35 нс

Я пытаюсь оптимизировать это с помощью Numpy.Моя функция переписать

def expand2(array,pos,k):
    aux = np.tile(array,(k,1))
    aux[:,pos] = np.arange(k)
    return aux

, и это займет 6,26 мкс ± 65,7 нс , что значительно дольше.

Мне было интересно о опытах, которые могут иметь людипри оптимизации кода Python , который, например, несколько раз клонирует массив и выполняет разные действия с каждой копией.

Заранее извиняюсь за возможное отсутствие этикета.Это мой первый пост здесь.

Спасибо.

1 Ответ

0 голосов
/ 19 декабря 2018

При частом использовании numpy вы платите определенные фиксированные накладные расходы, чтобы получить ускорение за единицу.Поэтому при тестировании фрагментов кода важно учитывать различные размеры проблем.

Давайте сделаем это с вашими двумя функциями и с третьей, которую я написал:

enter image description here

ось x - это k, ось y - время

Сосредоточив внимание на расширениях 1 и 2, сначала мы видим типичную картинку.Цикл имеет более высокую стоимость на единицу (наклон), но превосходит np.tile при малых k из-за постоянных издержек (перехват y).

Обращаясь к expand3, мы можем далее видеть, что, избегая удобных функций, таких какnp.tile мы можем значительно сократить накладные расходы.

Код:

import numpy as np

def expand1():
    res = []
    states = range(0,k)
    for state in states:
        aux = array.copy()
        aux[pos] = state
        res.append(aux)
    return res

def expand2():
    aux = np.tile(array,(k,1))
    aux[:,pos] = np.arange(k)
    return aux

def expand3():
    aux = np.empty((k, *array.shape), array.dtype)
    aux[...] = array
    aux[:, pos] = np.arange(k)
    return aux

array = np.random.randint(0, 100, 1000)
pos = 493

from timeit import repeat
T = []
for k in range(100):
    T.append([min(repeat(f, number=100)) for f in (expand1, expand2, expand3)])

import pylab
pylab.plot(T)
pylab.legend('expand1 expand2 expand3'.split())
pylab.show()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...