.__skip_array_function__
函数属性以允许跳过__array_function__
调度。 (https://github.com/numpy/numpy/pull/13389)numpy/core/overrides.py
以提高速度(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)对于一些引人注目的功能,可以合并一个初步的“用@array_function_dispatch装饰所有公共NumPy函数”的初步建议,并请协议的下游使用者尝试一下
一旦我们合并https://github.com/numpy/numpy/pull/12099,我就会准备好另一个PR,它将为numpy.core
大部分添加调度装饰器。 完成事情将非常容易-这个过程花了不到一个小时的时间。
cc @ eric-wieser @mrocklin @mhvk @hameerabbasi
请参阅https://github.com/shoyer/numpy/tree/array-function-easy-impl作为我的分支机构,以Python包装器在函数上实现所有“简单”替代。 剩下的部分是np.block
, np.einsum
和少数几个完全用C编写的多数组函数(例如np.concatenate
)。 #12099完成后,我将其分成一堆PR。
请注意,我尚未针对每个函数的重写编写测试。 我想在完成后添加一些集成测试(例如,记录所有应用操作的鸭子数组),但是我认为为每个单独的函数编写调度测试不会产生任何效果。 #12099中的检查应捕获调度程序上最常见的错误,并且调度程序功能中的每一行代码都应由现有测试执行。
@shoyer-关于测试,我同意为每个测试编写代码不是特别有用; 取而代之的是,在numpy中,以相对较快的速度在MaskedArray
开始使用覆盖可能是最有意义的。
@mhvk对我来说听起来不错,尽管我会让使用/了解MaskedArray的其他人带头。
见https://github.com/numpy/numpy/pull/12115 , https://github.com/numpy/numpy/pull/12116 ,#12119和https://github.com/numpy/numpy/pull/ PR实现对Python中定义的函数的__array_function__
支持的PR的12117 。
@shoyer-看到一些实现,我有两个担心:
reshape
,原始功能已经提供了一种通过定义reshape
方法来覆盖它的方法。 对于任何定义__array_function__
类,我们实际上都反对这样做。np.median
,请谨慎使用np.asanyarray
和ufuncs以确保子类可以使用它们。 但是该功能不再可以直接访问。我认为总体而言,这两件事可能是有益的,因为我们简化了界面并可以针对纯ndarray
进行优化的实现-尽管后者建议ndarray.__array_function__
来接管转换列表等,到ndarray
,以便实现可以跳过该部分)。 不过,我认为我会注意到它,因为这使我不敢以Quantity
的
尽管后者建议ndarray .__ array_function__应该将列表等转换为ndarray,以便实现可以跳过该部分)。
我不确定我是否会遵循这里。
实际上,我们确实不赞成使用覆盖reshape
和mean
之类的函数的旧方法,尽管该旧方法仍支持NumPy API的不完整实现。
我不确定我是否会遵循这里。
我认为问题在于,即使我们为单个函数实现__array_function__
,以前的机制也会完全中断,并且无法进行故障转移。 这就是为什么我建议我们重新审查我的NotImplementedButCoercible
建议。
@hameerabbasi-是的,这就是问题所在。 尽管我们在这里需要小心,但要使我们更容易依赖于胶带解决方案却是我们真正希望摆脱的……(这就是为什么我在上面写道我的“问题”实际上可能是有益的……) 。 也许有一个案例可以像1.16那样尝试,然后根据实际经验来决定是否要回退“忽略此案例的__array_function__
”。
回复:调度员样式:我对样式的偏爱是基于内存/导入时间的考虑因素和详细程度。 很简单,合并可能保持签名不变的调度程序。 这样,我们创建的对象最少,缓存命中率也将更高。
也就是说,我不太反对lambda样式。
现在,一些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添加了一份草案,说明如果采用Python包装方法,多数组函数(用C编写)的替代可能看起来像什么。
@mattip我们在哪里将matmul
为ufunc? 一旦完成所有这些__array_function__
覆盖,我认为这是使NumPy的公共API完全可重载的最后一件事。 为NumPy 1.16做好一切准备真是太好了!
实施NEP 20的PR#11175进展缓慢。 它是PR#11133的阻止程序,它具有matmul循环代码。 仍然需要对其进行更新,然后通过基准测试来验证新代码的运行速度并不比旧代码慢。
我有四个PR待审核,这些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
添加替代导致一些熊猫测试失败(https://github.com/pandas-dev/pandas/issues/23172)。 我们不太确定发生了什么,但是在发布之前,我们一定要弄清楚。
关于为什么这会导致dask / pandas中的测试失败的最佳猜测,请参阅https://github.com/numpy/numpy/issues/12225 。
导入时间的一些基准(在我的固态硬盘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)
对内存使用情况有任何想法(之前/之后)吗? 这也很有用,尤其是对于物联网应用。
您知道如何可靠地测量模块的内存使用情况吗?
2018年10月20日,星期六,上午6:56 Hameer Abbasi [email protected]
写道:
对内存使用情况有任何想法(之前/之后)吗? 这是很有用的
好,尤其是对于物联网应用。-
您收到此邮件是因为有人提到您。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/12028#issuecomment-431584123或静音
线程
https://github.com/notifications/unsubscribe-auth/ABKS1k_IkrJ2YmYReaDrnkNvcH2X0-ZCks5umyuogaJpZM4W3kSC
。
我认为编写包含import numpy as np
的脚本,添加一个sleep语句和跟踪进程内存应该足够好。 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进行快速浏览(真的,它仅包含两个功能!) array_function_dispatch
的最后一个PR。
作为参考,这是禁用__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仅在单个情况下就节省了一些时间。类型,并且对于唯一类型为ndarray
。 请参阅#12321。
@shoyer-我在邮件列表中提出了两个可能也可以在此处说明的问题:
types
? (而不仅仅是提供覆盖的参数。)了解实现似乎很有帮助。 (请参阅#12327)。ndarray.__array_function__
实现是否应该接受子类,即使它们重写__array_function__
? 考虑到Liskov替代原则,并且子类已经有保释的机会,这将是合理的。 这意味着将调用ndarray.__array_function__
内部的实现而不是公共函数。 (以及__array_ufunc__
类似内容...)请参阅#12328以获取仅__array_function__
的试用版。@ shoyer-有关(1)的快速实现方法,请参见#12327-如果我们采用这种方法,我认为我们也应该调整NEP。
和#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在标量和数组上分派。 这与NEP不兼容吗? 这种变化会破坏一切吗?
有关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
您可能要分派其中任何一个。 例如,这是一个自定义形状对象,我们可能希望对其进行分派。
这个例子不是很有用,但是我的想法是我有一个懒惰的对象,其行为类似于形状,但不返回整数,而是返回表达式。 例如,最好能够执行以下操作:
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
)不够用的情况?
我能想到的其他情况是np.random.RandomState
方法的size
参数,但是请注意,我们目前根本不支持这些方法-请访问http://www.numpy.org/ neps / nep-0018-array-function-protocol.html#callable -objects-generated-at-runtime
您是否有用例无法将数组用作模板(如带有形状的empty_like)的情况?
如果我们采用的是依赖于NumPy的现有API,并且希望透明地使它在其他后端上工作,而无需更改现有源代码。
例如,假设我们尝试使用类似NP的数组调用scipy.optimize.differential_evolution
,这会建立一个调用图而不是立即执行。
您可以在这里看到,如果我们可以更改np.full
来创建符号数组而不是默认的numpy数组,则很有用,如果传入的输入也是符号的。
如果我们采用的是依赖于NumPy的现有API,并且希望透明地使它在其他后端上工作,而无需更改现有源代码。
通常这是不可能的。 像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)切换为能量= 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 :
我要马上做...
我想对NEP进行一个修订,以明确阐明NEP-18对子类作者的保证: https :
自gh-13329修复以来,我将可用性任务标记为完成。 我们决定gh-#13588可以等到1.17发布之后。 这就留下了文档方面的改进, arange
gh-12379仍然可以在1.17中使用。
还有#13728-调度程序中的histogram[2d]d
这就留下了文档方面的改进,而gh-12379系列仍然可以在1.17中使用。
缺少文档问题,因此我打开了gh-13844。 我认为文档比arange
公开问题重要得多。
@shoyer我们可以关闭它吗?
最有用的评论
NEP 22在这里对这些选项进行了一些讨论。 我认为我们无法安全地更改
np.asarray()
的语义以返回除numpy.ndarray
对象之外的任何内容-为此,我们将需要一个新的协议。问题在于
np.asarray()
当前是转换为numpy数组对象的惯用方式,该对象使用can并且确实希望与numpy.ndarray
完全匹配,例如向下匹配到内存布局。当然,有很多用例不是这种情况,但是切换此行为会破坏很多下游代码,因此这不是入门。 下游项目将需要至少参与阵列鸭子类型的这一方面。
是。 NEP 18并非旨在成为NumPy替代方案的完整解决方案,而是朝着这个方向迈出的一步。