.__skip_array_function__
function, чтобы можно было пропустить отправку __array_function__
. (https://github.com/numpy/numpy/pull/13389)numpy/core/overrides.py
в C для повышения скорости (https://github.com/numpy/numpy/issues/12028):get_overloaded_types_and_args
array_function_implementation_or_override
ndarray.__array_function__
?array_function_dispatch
?numpy.core
np.core.defchararray
(# 12154)np.einsum
и np.block
(https://github.com/numpy/numpy/pull/12163)numpy.lib
numpy.fft
/ numpy.linalg
(https://github.com/numpy/numpy/pull/12117)arange?
] (https://github.com/numpy/numpy/issues/12379)ndarray.__repr__
не должны полагаться на __array_function__
(https://github.com/numpy/numpy/pull/12212)stacklevel
следует увеличить на 1 для обернутых функций, чтобы трассировки указывали в нужное место (gh-13329)Было бы неплохо объединить предварительный «Украсьте все общедоступные функции NumPy с помощью @array_function_dispatch» для некоторых важных функций и попросить нижестоящих потребителей протокола опробовать его.
Как только мы объединим https://github.com/numpy/numpy/pull/12099, у меня будет готов еще один PR, который добавит декораторы отправки для большей части numpy.core
. Доделать будет довольно легко - на сборку ушло меньше часа.
коп @ eric-wieser @mrocklin @mhvk @hameerabbasi
См. Https://github.com/shoyer/numpy/tree/array-function-easy-impl для моей ветки, реализующей все «простые» переопределения функций с оболочками Python. Остальные части - это np.block
, np.einsum
и несколько многомассивных функций, полностью написанных на C (например, np.concatenate
). Я разделю это на кучу PR, когда мы закончим с # 12099.
Обратите внимание, что я не писал тестов для переопределения каждой отдельной функции. Я хотел бы добавить несколько интеграционных тестов, когда мы закончим (например, массив утки, который регистрирует все примененные операции), но я не думаю, что было бы продуктивно писать тесты диспетчеризации для каждой отдельной функции. Проверки в # 12099 должны выявлять наиболее распространенные ошибки диспетчеров, и каждая строка кода в функциях диспетчера должна выполняться существующими тестами.
@shoyer - по тестам, согласен, писать тесты для каждого не особо полезно; вместо этого в numpy может иметь смысл относительно быстро начать использовать переопределения в MaskedArray
.
Для меня
См. Https://github.com/numpy/numpy/pull/12115 , https://github.com/numpy/numpy/pull/12116 , # 12119 и https://github.com/numpy/numpy/pull/ 12117 для __array_function__
для функций, определенных в Python.
@shoyer -
reshape
, исходная функциональность уже предоставила способ переопределить ее, определив метод reshape
. Мы фактически осуждаем это для любого класса, который определяет __array_function__
.np.median
, осторожное использование np.asanyarray
и ufuncs гарантирует, что подклассы уже могут их использовать. Но к этим функциям больше нельзя получить доступ напрямую.Я думаю, что в целом эти две вещи, вероятно, являются преимуществами, поскольку мы упрощаем интерфейс и можем оптимизировать реализации для чистого ndarray
- хотя последнее предполагает, что ndarray.__array_function__
должен взять на себя преобразование списков и т. Д. в ndarray
, чтобы реализации могли пропустить эту часть). Тем не менее, я подумал, что отмечу это, потому что это заставляет меня бояться реализовать это за Quantity
немного больше, чем я думал - как с точки зрения объема работы, так и с точки зрения возможного снижения производительности.
хотя последний предполагает, что ndarray .__ array_function__ должен взять на себя преобразование списков и т.д. в ndarray, чтобы реализации могли пропустить эту часть).
Я не уверен, что слежу за этим.
Мы действительно эффективно отказываемся от старого способа переопределения таких функций, как reshape
и mean
, хотя старый способ по-прежнему поддерживает неполные реализации API NumPy.
Я не уверен, что слежу за этим.
Я думаю, проблема в том, что если мы реализуем __array_function__
даже для одной функции, предыдущие механизмы полностью сломаются и не будет возможности переключения при отказе. Вот почему я предлагаю вернуться к моему предложению NotImplementedButCoercible
.
@hameerabbasi - да, вот в чем проблема. Хотя здесь нам нужно быть осторожными, насколько легко мы можем полагаться на решения из клейкой ленты, от которых мы действительно предпочли бы избавиться ... (вот почему я написал выше, что мои "проблемы" на самом деле могут быть преимуществами ...) . Может быть, есть случай попробовать, как в 1.16, а затем решить на собственном опыте, хотим ли мы предоставить запасной вариант «игнорировать мой __array_function__
для этого случая».
Re: стиль диспетчера: Мои предпочтения по стилю основаны на соображениях памяти / времени импорта и многословности. Проще говоря, объедините диспетчеров, где подпись, скорее всего, останется прежней. Таким образом, мы создаем наименьшее количество объектов, и попадания в кеш будут также выше.
Тем не менее, я не слишком против лямбда-стиля.
Стиль написания диспетчерских функций теперь появился в нескольких PR. Было бы хорошо сделать последовательный выбор через NumPy.
У нас есть несколько вариантов:
Вариант 1. Напишите отдельный диспетчер для каждой функции, например,
def _sin_dispatcher(a):
return (a,)
@array_function_dispatch(_sin_dispatcher)
def sin(a):
...
def _cos_dispatcher(a):
return (a,)
@array_function_dispatch(_cos_dispatcher)
def cos(a):
...
Преимущества:
sin(x=1)
-> TypeError: _sin_dispatcher() got an unexpected keyword argument 'x'
.Недостатки:
Вариант 2 : повторно использовать функции диспетчера в модуле, например,
def _unary_dispatcher(a):
return (a,)
@array_function_dispatch(_unary_dispatcher)
def sin(a):
...
@array_function_dispatch(_unary_dispatcher)
def cos(a):
...
Преимущества:
Недостатки:
sin(x=1)
-> TypeError: _unary_dispatcher() got an unexpected keyword argument 'x'
Вариант 3. Используйте функции lambda
если определение диспетчера помещается в одну строку, например,
# inline style (shorter)
@array_function_dispatch(lambda a: (a,))
def sin(a):
...
@array_function_dispatch(lambda a, n=None, axis=None, norm=None: (a,))
def fft(a, n=None, axis=-1, norm=None):
...
# multiline style (more readable?)
@array_function_dispatch(
lambda a: (a,)
)
def sin(a):
...
@array_function_dispatch(
lambda a, n=None, axis=None, norm=None: (a,)
)
def fft(a, n=None, axis=-1, norm=None):
...
Преимущества:
Недостатки:
TypeError: <lambda>() got an unexpected keyword argument 'x'
)@shoyer : отредактировано, чтобы добавить двухстрочный интервал PEP8, чтобы сделать аспект "строк кода" более реалистичным.
Обратите внимание, что проблемы с сообщениями об ошибках могут быть устранены путем реконструкции объекта кода , хотя это требует некоторых затрат времени импорта. Возможно, стоит изучить тунца @nschloe , чтобы сравнить некоторые варианты.
Да, модуль декоратора также можно использовать для генерации определения функции (он использует несколько иной подход для генерации кода, немного больше похожий на namedtuple, поскольку он использует exec()
).
Пока ошибка не решена, я думаю, нам нужно придерживаться параметров с диспетчером с понятным именем. Я бы немного связал диспетчеров вместе (2) по соображениям памяти, хотя тогда бы очень хорошо помнил сообщение об ошибке, поэтому предлагал бы вызвать диспетчера что-то вроде _dispatch_on_x
.
Хотя, если мы сможем исправить ошибку, все изменится. Например, это может быть так же просто, как перехват исключений, замена <lambda>
на имя функции в тексте исключения, а затем повторное повышение. (Или эта цепочка в наши дни?)
Я согласен с тем, что сообщение об ошибке должно быть четким, в идеале вообще не должно меняться.
Хорошо, на данный момент я думаю, что лучше воздержаться от использования lambda
, если мы не получим какую-то работу по генерации кода.
https://github.com/numpy/numpy/pull/12175 добавляет черновик того, как могут выглядеть переопределения для функций с несколькими массивами (написанные на C), если мы воспользуемся подходом оболочки Python.
@mattip Где мы находимся в реализации matmul
как ufunc? Как только мы закончим все эти __array_function__
переопределения, я думаю, это последнее, что нам нужно для того, чтобы сделать публичный API NumPy полностью перегружаемым. Было бы хорошо, если бы все это было готово для NumPy 1.16!
PR № 11175, который реализует NEP 20, продвигается медленно. Это блокировщик для PR # 11133, который имеет код цикла matmul. Этот код еще нужно обновить, а затем проверить с помощью тестов, что новый код не медленнее старого.
У меня есть четыре PR для проверки, которые должны выполнить полный набор переопределений. Мы будем благодарны за окончательные обзоры / подписи / слияния, так что мы можем начать всерьез тестировать __array_function__
! https://github.com/numpy/numpy/pull/12154 , https://github.com/numpy/numpy/pull/12163 , https://github.com/numpy/numpy/pull/12119 , https: //github.com/numpy/numpy/pull/12175
Добавление переопределений в np.core
привело к сбою нескольких тестов pandas (https://github.com/pandas-dev/pandas/issues/23172). Мы еще не совсем уверены в том, что происходит, но мы обязательно должны разобраться в этом перед выпуском.
См. Https://github.com/numpy/numpy/issues/12225, чтобы узнать, почему это вызывает сбои тестов в dask / pandas.
Некоторые тесты времени импорта (на моем MacBook Pro с твердотельным накопителем):
decorator.decorate
(# 12226): 183,694 мсМой тестовый скрипт
import numpy as np
import subprocess
times = []
for _ in range(100):
result = subprocess.run("python -X importtime -c 'import numpy'",
shell=True, capture_output=True)
last_line = result.stderr.rstrip().split(b'\n')[-1]
time = float(last_line.decode('ascii')[-15:-7].strip().rstrip())
times.append(time)
print(np.median(times) / 1e3)
Есть идеи по использованию памяти (до / после)? Это тоже полезно, особенно для приложений Интернета вещей.
Вы знаете, как надежно измерить использование памяти для модуля?
В субботу, 20 октября 2018 г., в 6:56 Хамир Аббаси [email protected]
написал:
Есть идеи по использованию памяти (до / после)? Это полезно как
ну особенно для приложений IoT.-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/12028#issuecomment-431584123 или отключить звук
нить
https://github.com/notifications/unsubscribe-auth/ABKS1k_IkrJ2YmYReaDrnkNvcH2X0-ZCks5umyuogaJpZM4W3kSC
.
Я думаю, что написание сценария, содержащего import numpy as np
, добавление оператора сна и отслеживание памяти процесса должно быть достаточно хорошим. https://superuser.com/questions/581108/how-can-i-track-and-log-cpu-and-memory-usage-on-a-mac
Любые другие разработчики ядра хотят быстро взглянуть (на самом деле, он включает только две функции!) На https://github.com/numpy/numpy/pull/12163? Это последний PR, добавляющий array_function_dispatch
к внутренним функциям numpy.
Для справки, вот разница в производительности, которую я вижу при отключении __array_function__
:
before after ratio
[45718fd7] [4e5aa2cd]
<master> <disable-array-function>
+ 72.5±2ms 132±20ms 1.82 bench_io.LoadtxtCSVdtypes.time_loadtxt_dtypes_csv('complex128', 10000)
- 44.9±2μs 40.8±0.6μs 0.91 bench_ma.Concatenate.time_it('ndarray', 2)
- 15.3±0.3μs 13.3±0.7μs 0.87 bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'object'>)
- 38.4±1μs 32.7±2μs 0.85 bench_linalg.Linalg.time_op('norm', 'longfloat')
- 68.7±3μs 56.5±3μs 0.82 bench_linalg.Linalg.time_op('norm', 'complex256')
- 80.6±4μs 65.9±1μs 0.82 bench_function_base.Median.time_even
- 82.4±2μs 66.8±3μs 0.81 bench_shape_base.Block.time_no_lists(100)
- 73.5±3μs 59.3±3μs 0.81 bench_function_base.Median.time_even_inplace
- 15.2±0.3μs 12.2±0.6μs 0.80 bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'str'>)
- 2.20±0.1ms 1.76±0.04ms 0.80 bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint64', (4, 4))
- 388±20μs 310±10μs 0.80 bench_lib.Pad.time_pad((10, 10, 10), 3, 'linear_ramp')
- 659±20μs 524±20μs 0.80 bench_linalg.Linalg.time_op('det', 'float32')
- 22.9±0.7μs 18.2±0.8μs 0.79 bench_function_base.Where.time_1
- 980±50μs 775±20μs 0.79 bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint32', (4, 4))
- 36.6±1μs 29.0±1μs 0.79 bench_ma.Concatenate.time_it('unmasked', 2)
- 16.4±0.7μs 12.9±0.6μs 0.79 bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'str'>)
- 16.4±0.5μs 12.9±0.4μs 0.79 bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'object'>)
- 141±5μs 110±4μs 0.78 bench_lib.Pad.time_pad((10, 100), (0, 5), 'linear_ramp')
- 18.0±0.6μs 14.1±0.6μs 0.78 bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'object'>)
- 11.9±0.6μs 9.28±0.5μs 0.78 bench_core.CountNonzero.time_count_nonzero_axis(1, 100, <type 'int'>)
- 54.6±3μs 42.4±2μs 0.78 bench_function_base.Median.time_odd_small
- 317±10μs 246±7μs 0.78 bench_lib.Pad.time_pad((10, 10, 10), 1, 'linear_ramp')
- 13.8±0.5μs 10.7±0.7μs 0.77 bench_reduce.MinMax.time_min(<type 'numpy.float64'>)
- 73.3±6μs 56.6±4μs 0.77 bench_lib.Pad.time_pad((1000,), (0, 5), 'mean')
- 14.7±0.7μs 11.4±0.3μs 0.77 bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'str'>)
- 21.5±2μs 16.5±0.6μs 0.77 bench_reduce.MinMax.time_min(<type 'numpy.int64'>)
- 117±4μs 89.2±3μs 0.76 bench_lib.Pad.time_pad((1000,), 3, 'linear_ramp')
- 43.7±1μs 33.4±1μs 0.76 bench_linalg.Linalg.time_op('norm', 'complex128')
- 12.6±0.6μs 9.55±0.2μs 0.76 bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'int'>)
- 636±20μs 482±20μs 0.76 bench_ma.MA.time_masked_array_l100
- 86.6±4μs 65.6±4μs 0.76 bench_lib.Pad.time_pad((1000,), (0, 5), 'linear_ramp')
- 120±4μs 90.4±2μs 0.75 bench_lib.Pad.time_pad((1000,), 1, 'linear_ramp')
- 160±5μs 119±8μs 0.74 bench_ma.Concatenate.time_it('ndarray+masked', 100)
- 14.4±0.6μs 10.7±0.3μs 0.74 bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'str'>)
- 15.7±0.4μs 11.7±0.6μs 0.74 bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'str'>)
- 21.8±2μs 16.1±0.7μs 0.74 bench_reduce.MinMax.time_max(<type 'numpy.int64'>)
- 11.9±0.6μs 8.79±0.3μs 0.74 bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'bool'>)
- 53.8±3μs 39.4±2μs 0.73 bench_function_base.Median.time_even_small
- 106±20μs 76.7±4μs 0.73 bench_function_base.Select.time_select
- 168±10μs 122±4μs 0.72 bench_shape_base.Block2D.time_block2d((512, 512), 'uint32', (2, 2))
- 12.5±0.5μs 8.96±0.4μs 0.72 bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'int'>)
- 162±10μs 115±5μs 0.71 bench_function_base.Percentile.time_percentile
- 12.9±1μs 9.12±0.4μs 0.71 bench_random.Random.time_rng('normal')
- 9.71±0.4μs 6.88±0.3μs 0.71 bench_core.CorrConv.time_convolve(1000, 10, 'full')
- 15.1±0.8μs 10.7±0.4μs 0.71 bench_reduce.MinMax.time_max(<type 'numpy.float64'>)
- 153±9μs 108±7μs 0.71 bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint8', (2, 2))
- 109±5μs 76.9±5μs 0.71 bench_ma.Concatenate.time_it('ndarray+masked', 2)
- 34.3±1μs 24.2±0.6μs 0.71 bench_linalg.Linalg.time_op('norm', 'complex64')
- 9.80±0.2μs 6.84±0.5μs 0.70 bench_core.CorrConv.time_convolve(1000, 10, 'same')
- 27.4±6μs 19.1±2μs 0.70 bench_core.CountNonzero.time_count_nonzero_axis(1, 10000, <type 'bool'>)
- 9.35±0.4μs 6.50±0.3μs 0.70 bench_core.CorrConv.time_convolve(50, 100, 'full')
- 65.2±4μs 45.2±1μs 0.69 bench_shape_base.Block.time_block_simple_row_wise(100)
- 12.9±1μs 8.89±0.3μs 0.69 bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'bool'>)
- 19.6±3μs 13.5±0.4μs 0.69 bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'object'>)
- 75.6±2μs 52.1±3μs 0.69 bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'reflect')
- 12.4±1μs 8.51±0.4μs 0.69 bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'bool'>)
- 172±30μs 117±4μs 0.68 bench_ma.Concatenate.time_it('unmasked+masked', 100)
- 23.1±0.5μs 15.8±0.9μs 0.68 bench_linalg.Linalg.time_op('norm', 'int16')
- 8.18±0.9μs 5.57±0.1μs 0.68 bench_core.CorrConv.time_correlate(1000, 10, 'full')
- 153±5μs 103±3μs 0.68 bench_function_base.Percentile.time_quartile
- 758±100μs 512±20μs 0.68 bench_linalg.Linalg.time_op('det', 'int16')
- 55.4±6μs 37.4±1μs 0.68 bench_ma.Concatenate.time_it('masked', 2)
- 234±30μs 157±5μs 0.67 bench_shape_base.Block.time_nested(100)
- 103±4μs 69.3±3μs 0.67 bench_linalg.Eindot.time_dot_d_dot_b_c
- 19.2±0.4μs 12.9±0.6μs 0.67 bench_core.Core.time_tril_l10x10
- 122±7μs 81.7±4μs 0.67 bench_lib.Pad.time_pad((10, 10, 10), 3, 'edge')
- 22.9±1μs 15.3±0.5μs 0.67 bench_linalg.Linalg.time_op('norm', 'int32')
- 16.6±2μs 11.0±0.3μs 0.66 bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'object'>)
- 9.98±0.3μs 6.58±0.1μs 0.66 bench_core.CorrConv.time_convolve(1000, 10, 'valid')
- 118±6μs 77.9±4μs 0.66 bench_shape_base.Block2D.time_block2d((512, 512), 'uint16', (2, 2))
- 212±50μs 140±8μs 0.66 bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'mean')
- 21.9±0.7μs 14.4±0.5μs 0.66 bench_linalg.Linalg.time_op('norm', 'int64')
- 131±5μs 85.9±5μs 0.65 bench_lib.Pad.time_pad((10, 10, 10), 3, 'constant')
- 56.8±2μs 37.0±3μs 0.65 bench_lib.Pad.time_pad((1000,), (0, 5), 'constant')
- 58.9±3μs 38.1±1μs 0.65 bench_lib.Pad.time_pad((10, 100), (0, 5), 'reflect')
- 72.1±2μs 46.5±3μs 0.64 bench_lib.Pad.time_pad((10, 100), (0, 5), 'constant')
- 8.66±0.3μs 5.58±0.2μs 0.64 bench_core.CorrConv.time_correlate(50, 100, 'full')
- 300±30μs 193±10μs 0.64 bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint8', (4, 4))
- 15.9±5μs 10.2±0.3μs 0.64 bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'int'>)
- 13.7±0.5μs 8.80±0.1μs 0.64 bench_random.Random.time_rng('uniform')
- 8.60±0.5μs 5.50±0.2μs 0.64 bench_core.CorrConv.time_correlate(1000, 10, 'same')
- 44.7±2μs 28.5±0.7μs 0.64 bench_lib.Pad.time_pad((1000,), 1, 'reflect')
- 72.7±3μs 46.2±2μs 0.64 bench_lib.Pad.time_pad((10, 10, 10), 3, 'wrap')
- 567±50μs 360±40μs 0.63 bench_shape_base.Block2D.time_block2d((512, 512), 'uint64', (2, 2))
- 58.0±3μs 36.7±2μs 0.63 bench_lib.Pad.time_pad((10, 100), 3, 'reflect')
- 219±30μs 138±7μs 0.63 bench_lib.Pad.time_pad((10, 100), 1, 'mean')
- 261±60μs 164±10μs 0.63 bench_lib.Pad.time_pad((10, 100), 1, 'linear_ramp')
- 825±100μs 519±30μs 0.63 bench_shape_base.Block2D.time_block2d((512, 512), 'uint64', (4, 4))
- 121±5μs 75.7±2μs 0.63 bench_lib.Pad.time_pad((10, 10, 10), 1, 'constant')
- 8.16±0.2μs 5.08±0.4μs 0.62 bench_core.CorrConv.time_convolve(50, 100, 'same')
- 66.6±3μs 41.3±2μs 0.62 bench_lib.Pad.time_pad((1000,), 3, 'constant')
- 53.1±3μs 32.9±0.8μs 0.62 bench_lib.Pad.time_pad((10, 100), 3, 'wrap')
- 285±60μs 177±10μs 0.62 bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'linear_ramp')
- 8.30±0.9μs 5.14±0.1μs 0.62 bench_core.CorrConv.time_correlate(1000, 10, 'valid')
- 115±3μs 71.2±3μs 0.62 bench_shape_base.Block2D.time_block2d((256, 256), 'uint64', (2, 2))
- 19.1±0.5μs 11.8±0.6μs 0.62 bench_linalg.Linalg.time_op('norm', 'float64')
- 95.3±5μs 58.6±2μs 0.62 bench_lib.Pad.time_pad((10, 100), 1, 'constant')
- 44.6±1μs 27.2±0.9μs 0.61 bench_lib.Pad.time_pad((1000,), (0, 5), 'edge')
- 447±20μs 270±10μs 0.61 bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint16', (4, 4))
- 53.9±2μs 32.6±2μs 0.60 bench_lib.Pad.time_pad((10, 100), 1, 'wrap')
- 11.6±1μs 6.97±0.4μs 0.60 bench_reduce.MinMax.time_max(<type 'numpy.float32'>)
- 95.9±5μs 57.7±2μs 0.60 bench_lib.Pad.time_pad((10, 100), 3, 'constant')
- 47.2±2μs 28.2±2μs 0.60 bench_lib.Pad.time_pad((1000,), (0, 5), 'reflect')
- 5.51±0.2μs 3.27±0.07μs 0.59 bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'object'>)
- 74.3±3μs 44.0±2μs 0.59 bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'wrap')
- 76.2±3μs 45.0±0.8μs 0.59 bench_lib.Pad.time_pad((10, 10, 10), 1, 'reflect')
- 57.1±1μs 33.5±2μs 0.59 bench_lib.Pad.time_pad((10, 100), (0, 5), 'wrap')
- 52.0±2μs 30.4±1μs 0.58 bench_lib.Pad.time_pad((1000,), 1, 'edge')
- 42.6±2μs 24.9±0.9μs 0.58 bench_lib.Pad.time_pad((1000,), 3, 'wrap')
- 15.0±3μs 8.73±0.3μs 0.58 bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'bool'>)
- 16.0±3μs 9.29±0.3μs 0.58 bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'int'>)
- 53.1±1μs 30.9±2μs 0.58 bench_lib.Pad.time_pad((1000,), 3, 'edge')
- 88.0±8μs 51.1±3μs 0.58 bench_lib.Pad.time_pad((10, 10, 10), 3, 'reflect')
- 44.6±2μs 25.9±1μs 0.58 bench_lib.Pad.time_pad((1000,), (0, 5), 'wrap')
- 90.3±5μs 51.9±1μs 0.57 bench_shape_base.Block2D.time_block2d((512, 512), 'uint8', (2, 2))
- 15.6±0.5μs 8.93±0.3μs 0.57 bench_linalg.Linalg.time_op('norm', 'float32')
- 102±6μs 58.3±0.9μs 0.57 bench_lib.Pad.time_pad((10, 10, 10), 1, 'edge')
- 80.1±4μs 45.6±3μs 0.57 bench_lib.Pad.time_pad((10, 100), 3, 'edge')
- 44.2±2μs 24.9±1μs 0.56 bench_lib.Pad.time_pad((1000,), 1, 'wrap')
- 71.6±8μs 39.5±1μs 0.55 bench_lib.Pad.time_pad((10, 10, 10), 1, 'wrap')
- 81.7±10μs 44.8±2μs 0.55 bench_lib.Pad.time_pad((10, 100), 1, 'edge')
- 420±90μs 230±10μs 0.55 bench_shape_base.Block.time_3d(10, 'block')
- 114±20μs 62.3±2μs 0.55 bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'constant')
- 5.76±0.1μs 3.13±0.08μs 0.54 bench_core.CorrConv.time_convolve(50, 10, 'same')
- 5.30±0.1μs 2.84±0.08μs 0.54 bench_core.CorrConv.time_correlate(50, 100, 'valid')
- 92.5±4μs 49.3±1μs 0.53 bench_shape_base.Block2D.time_block2d((256, 256), 'uint32', (2, 2))
- 13.5±3μs 7.07±0.2μs 0.52 bench_reduce.MinMax.time_min(<type 'numpy.float32'>)
- 7.66±1μs 3.88±0.2μs 0.51 bench_core.CorrConv.time_convolve(50, 100, 'valid')
- 29.0±3μs 14.5±0.8μs 0.50 bench_shape_base.Block.time_no_lists(10)
- 6.62±0.3μs 3.30±0.2μs 0.50 bench_core.CorrConv.time_convolve(1000, 1000, 'valid')
- 74.2±7μs 36.2±0.9μs 0.49 bench_shape_base.Block2D.time_block2d((256, 256), 'uint16', (2, 2))
- 5.55±0.3μs 2.70±0.2μs 0.49 bench_core.CorrConv.time_convolve(50, 10, 'valid')
- 73.9±20μs 35.8±2μs 0.48 bench_lib.Pad.time_pad((10, 100), 1, 'reflect')
- 224±20μs 107±7μs 0.48 bench_shape_base.Block2D.time_block2d((256, 256), 'uint64', (4, 4))
- 3.87±0.1μs 1.83±0.06μs 0.47 bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'str'>)
- 109±30μs 51.5±3μs 0.47 bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'edge')
- 240±20μs 112±4μs 0.47 bench_shape_base.Block2D.time_block2d((512, 512), 'uint16', (4, 4))
- 337±40μs 158±7μs 0.47 bench_shape_base.Block2D.time_block2d((512, 512), 'uint32', (4, 4))
- 188±8μs 88.0±2μs 0.47 bench_shape_base.Block2D.time_block2d((512, 512), 'uint8', (4, 4))
- 4.39±0.2μs 2.04±0.09μs 0.47 bench_core.CountNonzero.time_count_nonzero(3, 10000, <type 'bool'>)
- 73.2±4μs 33.9±0.5μs 0.46 bench_shape_base.Block2D.time_block2d((128, 128), 'uint64', (2, 2))
- 5.48±1μs 2.44±0.1μs 0.45 bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'object'>)
- 4.46±0.1μs 1.97±0.08μs 0.44 bench_core.CorrConv.time_correlate(50, 10, 'full')
- 30.4±9μs 13.3±0.3μs 0.44 bench_shape_base.Block.time_no_lists(1)
- 7.05±0.2μs 3.05±0.06μs 0.43 bench_reduce.SmallReduction.time_small
- 7.35±1μs 3.12±0.2μs 0.42 bench_core.CorrConv.time_convolve(50, 10, 'full')
- 4.36±0.1μs 1.84±0.07μs 0.42 bench_core.CorrConv.time_correlate(50, 10, 'same')
- 3.51±0.2μs 1.46±0.05μs 0.42 bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'object'>)
- 4.03±0.05μs 1.66±0.1μs 0.41 bench_core.CorrConv.time_correlate(1000, 1000, 'valid')
- 199±10μs 80.1±3μs 0.40 bench_shape_base.Block2D.time_block2d((256, 256), 'uint32', (4, 4))
- 3.98±0.2μs 1.60±0.08μs 0.40 bench_core.CountNonzero.time_count_nonzero(2, 10000, <type 'bool'>)
- 61.8±2μs 24.8±1μs 0.40 bench_shape_base.Block2D.time_block2d((256, 256), 'uint8', (2, 2))
- 4.13±0.1μs 1.62±0.05μs 0.39 bench_core.CorrConv.time_correlate(50, 10, 'valid')
- 61.6±2μs 23.9±1μs 0.39 bench_shape_base.Block2D.time_block2d((128, 128), 'uint32', (2, 2))
- 184±10μs 70.5±3μs 0.38 bench_shape_base.Block2D.time_block2d((256, 256), 'uint16', (4, 4))
- 56.1±4μs 21.0±0.9μs 0.38 bench_shape_base.Block2D.time_block2d((64, 64), 'uint64', (2, 2))
- 40.0±2μs 15.0±0.6μs 0.37 bench_shape_base.Block.time_block_simple_column_wise(10)
- 121±2μs 45.1±2μs 0.37 bench_shape_base.Block.time_nested(1)
- 179±4μs 66.1±4μs 0.37 bench_shape_base.Block2D.time_block2d((128, 128), 'uint64', (4, 4))
- 59.8±2μs 22.0±1μs 0.37 bench_shape_base.Block2D.time_block2d((128, 128), 'uint16', (2, 2))
- 3.19±0.05μs 1.17±0.02μs 0.37 bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'str'>)
- 54.0±3μs 19.7±1μs 0.37 bench_shape_base.Block2D.time_block2d((32, 32), 'uint64', (2, 2))
- 56.9±1μs 20.7±0.7μs 0.36 bench_shape_base.Block2D.time_block2d((64, 64), 'uint32', (2, 2))
- 3.14±0.1μs 1.14±0.04μs 0.36 bench_core.CountNonzero.time_count_nonzero(1, 10000, <type 'bool'>)
- 92.7±2μs 33.7±2μs 0.36 bench_shape_base.Block.time_block_complicated(1)
- 104±4μs 37.8±1μs 0.36 bench_shape_base.Block.time_block_complicated(10)
- 128±5μs 45.5±2μs 0.36 bench_shape_base.Block.time_nested(10)
- 196±100μs 69.4±3μs 0.35 bench_ma.Concatenate.time_it('unmasked+masked', 2)
- 153±5μs 53.9±2μs 0.35 bench_shape_base.Block2D.time_block2d((128, 128), 'uint16', (4, 4))
- 39.4±2μs 13.8±0.5μs 0.35 bench_shape_base.Block.time_block_simple_column_wise(1)
- 53.5±2μs 18.7±1μs 0.35 bench_shape_base.Block2D.time_block2d((32, 32), 'uint8', (2, 2))
- 55.2±2μs 19.3±0.6μs 0.35 bench_shape_base.Block2D.time_block2d((32, 32), 'uint16', (2, 2))
- 16.9±1μs 5.89±0.5μs 0.35 bench_core.Core.time_dstack_l
- 60.6±3μs 21.1±0.6μs 0.35 bench_shape_base.Block2D.time_block2d((128, 128), 'uint8', (2, 2))
- 25.5±0.2μs 8.88±0.3μs 0.35 bench_shape_base.Block.time_block_simple_row_wise(10)
- 54.6±3μs 19.0±0.6μs 0.35 bench_shape_base.Block2D.time_block2d((16, 16), 'uint64', (2, 2))
- 52.6±2μs 18.2±0.7μs 0.35 bench_shape_base.Block2D.time_block2d((16, 16), 'uint16', (2, 2))
- 6.57±2μs 2.25±0.08μs 0.34 bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'str'>)
- 24.3±1μs 8.30±0.6μs 0.34 bench_shape_base.Block.time_block_simple_row_wise(1)
- 148±3μs 50.0±3μs 0.34 bench_shape_base.Block2D.time_block2d((16, 16), 'uint32', (4, 4))
- 171±8μs 57.9±4μs 0.34 bench_shape_base.Block2D.time_block2d((256, 256), 'uint8', (4, 4))
- 159±5μs 53.8±1μs 0.34 bench_shape_base.Block2D.time_block2d((64, 64), 'uint64', (4, 4))
- 171±20μs 57.7±2μs 0.34 bench_shape_base.Block2D.time_block2d((128, 128), 'uint32', (4, 4))
- 3.15±0.3μs 1.06±0.03μs 0.34 bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'int'>)
- 55.7±5μs 18.7±0.2μs 0.34 bench_shape_base.Block2D.time_block2d((16, 16), 'uint8', (2, 2))
- 158±7μs 52.6±3μs 0.33 bench_shape_base.Block2D.time_block2d((128, 128), 'uint8', (4, 4))
- 153±4μs 50.7±1μs 0.33 bench_shape_base.Block2D.time_block2d((32, 32), 'uint64', (4, 4))
- 152±7μs 50.3±1μs 0.33 bench_shape_base.Block2D.time_block2d((16, 16), 'uint8', (4, 4))
- 53.6±3μs 17.7±0.4μs 0.33 bench_shape_base.Block2D.time_block2d((16, 16), 'uint32', (2, 2))
- 156±4μs 51.4±3μs 0.33 bench_shape_base.Block2D.time_block2d((64, 64), 'uint8', (4, 4))
- 148±3μs 48.2±2μs 0.33 bench_shape_base.Block2D.time_block2d((16, 16), 'uint16', (4, 4))
- 160±10μs 52.0±1μs 0.33 bench_shape_base.Block2D.time_block2d((64, 64), 'uint32', (4, 4))
- 159±8μs 51.4±3μs 0.32 bench_shape_base.Block2D.time_block2d((64, 64), 'uint16', (4, 4))
- 59.8±3μs 19.3±1μs 0.32 bench_shape_base.Block2D.time_block2d((32, 32), 'uint32', (2, 2))
- 153±4μs 49.4±2μs 0.32 bench_shape_base.Block2D.time_block2d((32, 32), 'uint32', (4, 4))
- 15.6±0.6μs 5.03±0.3μs 0.32 bench_core.Core.time_vstack_l
- 154±7μs 49.7±2μs 0.32 bench_shape_base.Block2D.time_block2d((32, 32), 'uint8', (4, 4))
- 59.6±6μs 19.1±0.8μs 0.32 bench_shape_base.Block2D.time_block2d((64, 64), 'uint8', (2, 2))
- 3.03±0.4μs 969±30ns 0.32 bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'int'>)
- 120±10μs 38.4±2μs 0.32 bench_shape_base.Block.time_3d(1, 'block')
- 156±5μs 49.3±1μs 0.32 bench_shape_base.Block2D.time_block2d((16, 16), 'uint64', (4, 4))
- 164±10μs 49.3±2μs 0.30 bench_shape_base.Block2D.time_block2d((32, 32), 'uint16', (4, 4))
- 65.7±10μs 19.6±0.7μs 0.30 bench_shape_base.Block2D.time_block2d((64, 64), 'uint16', (2, 2))
- 2.82±0.08μs 732±30ns 0.26 bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'int'>)
- 2.77±0.07μs 664±30ns 0.24 bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'bool'>)
- 2.61±0.1μs 624±20ns 0.24 bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'bool'>)
- 16.8±3μs 3.97±0.2μs 0.24 bench_core.Core.time_hstack_l
- 2.78±0.1μs 637±20ns 0.23 bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'bool'>)
- 2.36±0.2μs 207±5ns 0.09 bench_overrides.ArrayFunction.time_mock_broadcast_to_numpy
- 2.68±0.1μs 221±7ns 0.08 bench_overrides.ArrayFunction.time_mock_concatenate_numpy
- 2.58±0.1μs 201±10ns 0.08 bench_overrides.ArrayFunction.time_mock_broadcast_to_duck
- 3.02±0.2μs 222±6ns 0.07 bench_overrides.ArrayFunction.time_mock_concatenate_duck
- 4.29±0.3μs 216±6ns 0.05 bench_overrides.ArrayFunction.time_mock_concatenate_mixed
- 142±20μs 213±8ns 0.00 bench_overrides.ArrayFunction.time_mock_concatenate_many
SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
см. также https://docs.google.com/spreadsheets/d/15-AFI_cmZqfkU6mo2p1znsQF2E52PEXpF68QqYqEar4/edit#gid = 0 для электронной таблицы.
Неудивительно, что наибольшая разница в производительности наблюдается для функций, которые вызывают другие функции numpy внутри много раз, например, для np.block()
.
@shoyer - меня немного нужна реализация на C, но тем временем я сделал PR с некоторыми небольшими изменениями, которые сэкономили время для общего случая только одного type, а для случая, когда единственный тип - ndarray
. См. № 12321.
@shoyer - Я поднял два вопроса в списке рассылки, которые, вероятно, тоже стоит изложить здесь:
types
? (а не только те аргументы, которые обеспечивают переопределение.) Было бы полезно знать об реализациях. (см. №12327).ndarray.__array_function__
принимать подклассы, даже если они переопределяют __array_function__
? Это было бы разумно, учитывая принцип замены Лискова и учитывая, что у подкласса уже был шанс выйти под залог. Это будет означать вызов реализации, а не публичной функции внутри ndarray.__array_function__
. (И нечто подобное в __array_ufunc__
...) См. # 12328 для ознакомления только с __array_function__
.@shoyer - см. # 12327 для быстрой реализации (1) - если мы действительно пойдем по этому пути, я думаю, нам также следует скорректировать нэп.
И # 12328 для пробной версии (2), в основном, чтобы посмотреть, как это выглядит.
Я здесь +1 к обеим модификациям.
Название диспетчерских функций в сообщениях об ошибках снова появилось в https://github.com/numpy/numpy/pull/12789 , где кто-то был удивлен, увидев TypeError: _pad_dispatcher missing 1 required positional argument
В дополнение к альтернативам, описанным выше https://github.com/numpy/numpy/issues/12028#issuecomment -429377396 (сейчас мы используем 2), я добавлю четвертый вариант:
Вариант 4 : Напишите отдельный диспетчер для каждой функции с тем же именем, что и функция:
def sin(a):
return (a,)
@array_function_dispatch(sin)
def sin(a):
...
def cos(a):
return (a,)
@array_function_dispatch(cos)
def cos(a):
...
Преимущества:
Недостатки:
pad
получило неправильные аргументы (но у нас есть тесты, чтобы убедиться, что они синхронизированы).Я думаю, что для того, чтобы текущий код работал, фактическая функция должна появиться _после_ диспетчера.
Верно, но мы можем дать ему то же имя, что и диспетчер. Имя диспетчера будет перезаписано.
Было бы здорово иметь возможность определять настраиваемую отправку для таких функций, как np.arange или np.empty.
Я предполагаю, что одним из вариантов было бы, чтобы NumPy отправлял скаляры, а также массивы. Это несовместимо с нэпом? Что-нибудь сломается с этим изменением?
Для обсуждения np.arange
см. Https://github.com/numpy/numpy/issues/12379.
Я не понимаю, как np.empty()
может выполнять диспетчеризацию - здесь не на что отправлять, только форма и dtype. Но, безусловно, np.empty_like()
может выполнять отправку с перезаписанной формой - это именно то, что поддерживает https://github.com/numpy/numpy/pull/13046 .
Вариант 4 : Напишите отдельный диспетчер для каждой функции с тем же именем, что и функция:
Есть возражения против принятия этого варианта? Я думаю, что это, пожалуй, самый удобный выбор с точки зрения пользователя.
Я не понимаю, как np.empty () может выполнять диспетчеризацию - здесь не на что отправлять, только фигура и dtype
Возможно, вы захотите отправить любой из них. Например, вот объект нестандартной формы, который мы можем отправить по-другому.
Этот пример не очень полезен, но идея состоит в том, что у меня есть ленивый объект, который ведет себя как shape, но не возвращает целые числа, он возвращает выражения. Например, было бы неплохо сделать что-то вроде этого:
class ExprShape:
def __getitem__(self, i):
return ('getitem', self, i)
def __len__(self):
return ('len', self)
numpy.empty(ExprShape())
Что бы я хотел переопределить, чтобы вернуть что-то вроде ExprArray('empty', ExprShape())
.
Да, в принципе, по форме тоже можно отправить. Это добавило бы протоколу дополнительной сложности / накладных расходов. Есть ли у вас варианты использования, когда использование массива в качестве шаблона (например, empty_like
с shape
) будет недостаточно?
В других случаях я могу думать это size
аргумент np.random.RandomState
методы, но обратите внимание , что в настоящее время мы не поддерживаем тех , кто вообще - см http://www.numpy.org/ neps / nep-0018-array-function-protocol.html # callable -objects-generated-at-runtime
Есть ли у вас варианты использования, когда использование массива в качестве шаблона (например, empty_like с shape) недостаточно?
Если мы берем существующий API, который зависит от NumPy, и хотели бы, чтобы он прозрачно работал на другом сервере, без изменения существующего исходного кода.
Например, предположим, что мы пытались вызвать scipy.optimize.differential_evolution
с NP-подобными массивами, которые создают граф вызовов вместо немедленного выполнения.
Вы можете увидеть здесь , было бы полезно , если бы мы могли изменить np.full
, чтобы создать символическую массив вместо массива в Numpy по умолчанию, если вход перешел в это тоже символично.
Если мы берем существующий API, который зависит от NumPy, и хотели бы, чтобы он прозрачно работал на другом сервере, без изменения существующего исходного кода.
В общем, это невозможно. Явную конструкцию массива, такую как np.array()
, определенно нужно будет переписать, чтобы она была совместима с утиной типизацией.
В этом случае переключение energies = np.full(num_members, np.inf)
на energies = np.full_like(population, np.inf, shape=num_members)
кажется простым и понятным изменением.
В общем, это невозможно. Явное построение массива, такое как np.array (), определенно нужно будет переписать, чтобы оно было совместимо с утиной типизацией.
Есть ли предложение относительно внесения такого рода изменений или вы говорите, что поддержка отправки np.array
будет действительно сложной задачей, и поэтому мы никогда не сможем получить 100% поддержку?
В этом случае переключение энергий = np.full (num_members, np.inf) на energy = np.full_like (Population, np.inf, shape = num_members) кажется простым и читаемым изменением.
Определенно. Но есть много случаев, когда либо вы не контролируете исходный код, либо хотите поддержать пользователей в использовании функций, которые они знают и любят, насколько это возможно.
Есть и другие способы предоставить пользователям такой опыт, например:
Оба эти варианта могут потребоваться в определенных случаях (например, разрешить пользователям вызывать np.full
и в настоящее время возвращать символический результат), но если я правильно понимаю, цель NEP-18 - попытаться ограничить, когда они необходимы и позволить людям использовать оригинальный NumPy в большем количестве случаев.
Я понимаю, что здесь существует компромисс между производительностью и сложностью, и это может быть хорошей причиной не реализовывать их. Но это может заставить пользователей искать другие средства для достижения желаемой гибкости.
Есть ли предложение относительно внесения такого рода изменений или вы говорите, что поддержка отправки
np.array
будет действительно сложной задачей, и поэтому мы никогда не сможем получить 100% поддержку?
В NEP 22 варианты обсуждаются здесь. Я не думаю, что мы можем безопасно изменить семантику np.asarray()
чтобы возвращать что-либо, кроме объекта numpy.ndarray
- для этого нам понадобится новый протокол.
Проблема в том, что np.asarray()
в настоящее время является идиоматическим способом преобразования в объект массива numpy, который использует can и ожидает точного соответствия numpy.ndarray
, например, вплоть до макета памяти.
Безусловно, существует множество вариантов использования, в которых это не так, но переключение этого поведения приведет к поломке большого количества нисходящего кода, так что это не начало. Проекты, находящиеся ниже по течению, должны будут согласиться хотя бы на этот аспект набора утиных массивов.
Я понимаю, что здесь существует компромисс между производительностью и сложностью, и это может быть хорошей причиной не реализовывать их. Но это может заставить пользователей искать другие средства для достижения желаемой гибкости.
Да. NEP 18 не предназначен для того, чтобы быть полным решением для альтернативных вариантов NumPy, но это шаг в этом направлении.
Я подготовил пересмотр NEP-18 для добавления атрибута __numpy_implementation__
:
https://github.com/numpy/numpy/pull/13305
Мне приходит в голову, что мы забываем деформировать функции в numpy.testing
: https://github.com/numpy/numpy/issues/13588
Я сделаю это в ближайшее время ...
Есть одна версия, которую я хотел бы внести в NEP, в частности, чтобы прояснить, какие гарантии NEP-18 предлагает авторам подклассов: https://github.com/numpy/numpy/pull/13633
Я отметил, что задачи по удобству использования выполнены, так как gh-13329 был исправлен. Мы решили, что # 13588 может подождать до выпуска 1.17. Это оставляет улучшения документации и arange
gh-12379 по-прежнему открытыми для включения в 1.17.
Там же # 13728 - ошибка в диспетчере histogram[2d]d
Это оставляет улучшения документации и arange gh-12379 для включения в 1.17.
Проблема с документацией отсутствовала, поэтому я открыл gh-13844. Я думаю, что документы намного важнее, чем открытый вопрос arange
.
@shoyer мы можем закрыть это?
Самый полезный комментарий
В NEP 22 варианты обсуждаются здесь. Я не думаю, что мы можем безопасно изменить семантику
np.asarray()
чтобы возвращать что-либо, кроме объектаnumpy.ndarray
- для этого нам понадобится новый протокол.Проблема в том, что
np.asarray()
в настоящее время является идиоматическим способом преобразования в объект массива numpy, который использует can и ожидает точного соответствияnumpy.ndarray
, например, вплоть до макета памяти.Безусловно, существует множество вариантов использования, в которых это не так, но переключение этого поведения приведет к поломке большого количества нисходящего кода, так что это не начало. Проекты, находящиеся ниже по течению, должны будут согласиться хотя бы на этот аспект набора утиных массивов.
Да. NEP 18 не предназначен для того, чтобы быть полным решением для альтернативных вариантов NumPy, но это шаг в этом направлении.