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
vmalloc+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