Python 为什么numpy.zero和numpy.zero的性能会有差异?

Python 为什么numpy.zero和numpy.zero的性能会有差异?,python,numpy,Python,Numpy,我终于在我的代码中发现了一个性能瓶颈,但我不知道原因是什么。为了解决这个问题,我将所有对numpy.zeros\u的调用改为使用numpy.zeros。但是为什么零像那么慢呢 例如(注意e-05调用上的zeros): 但是奇怪的是,对用零创建的数组的写入速度明显比用零创建的数组慢得多 >>> timeit.timeit('x[100:-100, 100:-100] = 1', 'import numpy as np; x = np.zeros((12488, 7588, 3),

我终于在我的代码中发现了一个性能瓶颈,但我不知道原因是什么。为了解决这个问题,我将所有对
numpy.zeros\u的调用改为使用
numpy.zeros
。但是为什么零像
那么慢呢

例如(注意
e-05
调用上的
zeros
):

但是奇怪的是,对用
零创建的数组的写入速度明显比用
零创建的数组慢得多

>>> timeit.timeit('x[100:-100, 100:-100] = 1', 'import numpy as np; x = np.zeros((12488, 7588, 3), np.uint8)', number = 10)
0.4310588836669922
>>> timeit.timeit('x[100:-100, 100:-100] = 1', 'import numpy as np; x = np.zeros_like(np.zeros((12488, 7588, 3), np.uint8))', number = 10)
0.33325695991516113
我的猜测是
zeros
使用了一些CPU技巧,而不是实际写入内存来分配它。这是在写给用户时动态完成的。但这仍然不能解释阵列创建时间的巨大差异

我正在使用当前的numpy版本运行Mac OS X Yosemite:

>>> numpy.__version__
'1.9.1'
我在Ipython中的计时是(使用更简单的timeit接口):

当我用IPython(
np.zero\u like???
)查看代码时,我看到:

np.zeros
是一个黑盒纯编译代码

empty
的计时为:

In [63]: timeit np.empty_like(x)
100000 loops, best of 3: 13.6 µs per loop

In [64]: timeit np.empty((12488, 7588, 3), np.uint8)
100000 loops, best of 3: 14.9 µs per loop
所以像
那样的
zero\u中的额外时间是在
copy

在我的测试中,分配时间的差异(
x[]=1
)可以忽略不计

我猜
都是早期编译的产物
empty_like
是为了方便而添加的,只是从输入中绘制形状和类型信息<代码>类似于零的
的编写着眼于更简单的编程维护(重用
类似于空的
),而不是速度

np.one
np.full
也使用
np.empty。。。复制到序列,并显示类似的计时


似乎是将标量(如
0
)复制到数组的文件。我看不到
memset
的用途

调用了
malloc
calloc

-源代码为
。两者都调用
PyArray\u NewFromDescr\u int
,但其中一个最终使用
npy\u alloc\u cache\u zero
,另一个调用
npy\u alloc\u cache

alloc.c
中的npy\u alloc\u缓存
调用
alloc
npy\u alloc\u cache\u zero
调用
npy\u alloc\u cache
,后跟
memset
alloc.c中的代码进一步与线程选项混淆

有关
calloc
v
malloc+memset
差异的更多信息,请访问:

但是对于缓存和垃圾收集,我想知道
calloc/memset
的区别是否适用


使用
memory\u profile
包进行的这个简单测试支持
zeros
empty
动态分配内存,而
zeros\u like
预先分配所有内容:

N = (1000, 1000) 
M = (slice(None, 500, None), slice(500, None, None))

Line #    Mem usage    Increment   Line Contents
================================================
     2   17.699 MiB    0.000 MiB   @profile
     3                             def test1(N, M):
     4   17.699 MiB    0.000 MiB       print(N, M)
     5   17.699 MiB    0.000 MiB       x = np.zeros(N)   # no memory jump
     6   17.699 MiB    0.000 MiB       y = np.empty(N)
     7   25.230 MiB    7.531 MiB       z = np.zeros_like(x) # initial jump
     8   29.098 MiB    3.867 MiB       x[M] = 1     # jump on usage
     9   32.965 MiB    3.867 MiB       y[M] = 1
    10   32.965 MiB    0.000 MiB       z[M] = 1
    11   32.965 MiB    0.000 MiB       return x,y,z

现代操作系统虚拟地分配内存,也就是说,只有在进程首次使用时,才会将内存分配给它<代码>归零
从操作系统获取内存,以便操作系统在首次使用时将其归零<另一方面,code>zero_like本身用零填充分配内存。这两种方法都需要大约相同的工作量——只是像
zero\u那样的
调零是预先完成的,而
zero
最终是动态完成的


从技术上讲,在C语言中,调用
calloc
malloc+memset
的区别在于
zero
使用
memset
<像
这样的code>zero\u似乎有效地完成了一个
填充
,这是一堆废话。我试图追踪实际执行情况,但它毫无必要地迟钝。
zeros
不使用memset。查看内部结构是正确的方向。奇怪的是,
zeros\u like
不只是调用
zeros
。我已经多次运行了赋值测试,并且总是得到很小的差异,但是对于
zero_(如
array)数组,测试速度明显加快。上述猜测不是原因。实际的区别在于内存归零是留给操作系统的VM子系统,还是由进程本身来完成。
calloc/memset
的解释听起来合乎逻辑,但我在
numpy
代码中无法确认这一点。
res = empty_like(a, dtype=dtype, order=order, subok=subok)
multiarray.copyto(res, 0, casting='unsafe')
In [63]: timeit np.empty_like(x)
100000 loops, best of 3: 13.6 µs per loop

In [64]: timeit np.empty((12488, 7588, 3), np.uint8)
100000 loops, best of 3: 14.9 µs per loop
N = (1000, 1000) 
M = (slice(None, 500, None), slice(500, None, None))

Line #    Mem usage    Increment   Line Contents
================================================
     2   17.699 MiB    0.000 MiB   @profile
     3                             def test1(N, M):
     4   17.699 MiB    0.000 MiB       print(N, M)
     5   17.699 MiB    0.000 MiB       x = np.zeros(N)   # no memory jump
     6   17.699 MiB    0.000 MiB       y = np.empty(N)
     7   25.230 MiB    7.531 MiB       z = np.zeros_like(x) # initial jump
     8   29.098 MiB    3.867 MiB       x[M] = 1     # jump on usage
     9   32.965 MiB    3.867 MiB       y[M] = 1
    10   32.965 MiB    0.000 MiB       z[M] = 1
    11   32.965 MiB    0.000 MiB       return x,y,z