Использование Julia ifelse с массивом

Мы можем немного потрудиться, чтобы понять это:

>>> import numpy as np
>>> a = np.arange(32)
>>> a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])
>>> a.data
<read-write buffer for 0x107d01e40, size 256, offset 0 at 0x107d199b0>
>>> id(a.data)
4433424176
>>> id(a[0])
4424950096
>>> id(a[1])
4424950096
>>> for item in a:
...   print id(item)
... 
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120

Итак, что здесь происходит? Во-первых, я взглянул на расположение памяти буфера памяти массива. Это на 4433424176. Это само по себе не является также . Тем не менее, numpy хранит его данные как непрерывный массив C, поэтому первый элемент в массиве numpy должен соответствовать адресу памяти самого массива, но это не так:

>>> id(a[0])
4424950096

, и хорошо, что это не так, потому что это сломает инвариант в python, что у 2 объектов никогда не будет одинакового id в течение их жизни.

Итак, как numpy выполняет это? Ну, ответ заключается в том, что numpy должен обернуть возвращаемый объект типом python (например, numpy.float64 или numpy.int64 в этом случае), который требует времени, если вы повторяете item-by-item1. Дальнейшее доказательство этого демонстрируется при итерации. Мы видим, что мы чередуем между двумя отдельными идентификаторами, итерациями по массиву. Это означает, что распределитель памяти и сборщик мусора python работают сверхурочно для создания новых объектов, а затем освобождают их.

Список не имеет накладных расходов на распределитель памяти / сборщика мусора. Объекты в списке уже существуют как объекты python (и они все равно будут существовать после итерации), поэтому не играет никакой роли в итерации по списку.

Методология синхронизации:

Также обратите внимание: ваши тайминги немного отброшены по вашим предположениям. Вы предполагали, что в обоих случаях k + 1 должно занимать примерно такое же количество времени, но это не так. Обратите внимание, если я повторяю ваши тайминги без , делая какое-либо дополнение:

mgilson$ python -m timeit -s "import numpy" "for k in numpy.arange(5000): k"
1000 loops, best of 3: 233 usec per loop
mgilson$ python -m timeit "for k in range(5000): k"
10000 loops, best of 3: 114 usec per loop

, разница в коэффициенте разницы составляет всего 2. Однако выполнение добавления приводит к разнице в 5 раз:

mgilson$ python -m timeit "for k in range(5000): k+1"
10000 loops, best of 3: 179 usec per loop
mgilson$ python -m timeit -s "import numpy" "for k in numpy.arange(5000): k+1"
1000 loops, best of 3: 786 usec per loop

Для удовольствия просто добавьте:

$ python -m timeit -s "v = 1" "v + 1"
10000000 loops, best of 3: 0.0261 usec per loop
mgilson$ python -m timeit -s "import numpy; v = numpy.int64(1)" "v + 1"
10000000 loops, best of 3: 0.121 usec per loop

И, наконец, ваше время также включает время построения списка / массива, которое не является идеальным:

mgilson$ python -m timeit -s "v = range(5000)" "for k in v: k"
10000 loops, best of 3: 80.2 usec per loop
mgilson$ python -m timeit -s "import numpy; v = numpy.arange(5000)" "for k in v: k"
1000 loops, best of 3: 237 usec per loop

Обратите внимание, что в этом случае nump действительно удалился от решения списка. Это показывает, что итерация на самом деле меньше медленнее, и вы можете получить некоторые ускорения, если вы конвертируете типы numpy в стандартные типы python.

1Note, this doesn ' t занимает много времени, когда разрезает, потому что ему нужно только выделить O (1) новые объекты, поскольку numpy возвращает view в исходный массив.

0
задан Stephen G 15 January 2019 в 17:40
поделиться

2 ответа

Кроме того, что упоминали другие, ваш код страдает от проблемы нестабильности типов. Используйте @code_warntype, чтобы проверить это. x имеет элементы типа Float64, а внутри ifelse у вас есть одна ветвь с типом Float64, а другая - с типом Int64. Эту проблему можно решить, используя zero(eltype(x)) вместо 0.

Вместо этого странного условия 2 - 2*x >= 0, используйте это эквивалентное простое x <= 1. Кроме того, вместо вещания я предлагаю использовать map, который работает быстрее и не выделяет. Увидеть ниже.

x = 0:0.1:2
x2 = map(x -> ifelse(x <= 1, 2 - 2x, zero(eltype(x))), x)
0
ответ дан AboAmmar 15 January 2019 в 17:40
поделиться

Просто поставьте точку . после ifelse для трансляции. Многие функции, которые работают с элементами коллекций, требуют трансляции с точечным синтаксисом на Julia 1.x.

x = collect(0:0.1:2) 
x2 = ifelse.(2 .- 2*x.>=0, 2 .- 2*x, 0.)
0
ответ дан hckr 15 January 2019 в 17:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: