我终于在我的代码中发现了一个性能瓶颈,但是我不知道原因是什么。为了解决这个问题,我将所有的numpy.zeros_like
调用改为使用numpy.zeros
。但是为什么zeros_like
要慢得多呢?
例如(注意zeros
调用上的e-05
):
>>> timeit.timeit('np.zeros((12488, 7588, 3), np.uint8)', 'import numpy as np', number = 10)
5.2928924560546875e-05
>>> timeit.timeit('np.zeros_like(x)', 'import numpy as np; x = np.zeros((12488, 7588, 3), np.uint8)', number = 10)
1.4402990341186523
但奇怪的是,用zeros
创建的数组的写入速度明显慢于用zeros_like
创建的数组:
>>> 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'
现代操作系统实际上是在分配内存,也就是说,只有当一个进程首次使用时,内存才被分配给它。
zeros
从操作系统获取内存,以便操作系统在首次使用时将其归零。zeros_like
另一方面,它自己用零填充分配的内存。这两种方法都需要大约相同的工作量——只不过对于zeros_like
调零是预先完成的,而zeros
最终是在飞行中完成的。从技术上讲,C语言的区别在于调用
calloc
与malloc+memset
。我在Ipython中的计时是(使用更简单的timeit接口):
当我使用IPython(
np.zeros_like??
)查看代码时,我看到:而
np.zeros
是纯黑盒编译的代码。empty
的计时为:所以
zeros_like
中的额外时间是copy
。在我的测试中,赋值时间(
x[]=1
)的差异可以忽略不计。我猜
zeros
、ones
、empty
都是早期编译的产物。empty_like
是为了方便起见添加的,只需从输入中绘制形状和类型信息。zeros_like
编写时更注重易于编程维护(重用empty_like
),而不是速度。np.ones
和np.full
也使用np.empty ... copyto
序列,并显示类似的计时。https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/array_assign_scalar.c 似乎是将标量(例如
0
)复制到数组的文件。我看不到memset
的用法。https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/alloc.c调用了
malloc
和calloc
。https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/ctors.c-用于
zeros
和empty
的源。两者都调用PyArray_NewFromDescr_int
,但一个最终使用npy_alloc_cache_zero
,另一个使用npy_alloc_cache
。npy_alloc_cache
在alloc.c
调用alloc
。npy_alloc_cache_zero
调用npy_alloc_cache
,然后是memset
。alloc.c
中的代码与线程选项进一步混淆。更多关于
calloc
vmalloc+memset
差异的信息,请访问: Why malloc+memset is slower than calloc?但是,对于缓存和垃圾收集,我想知道
calloc/memset
区别是否适用。这个带有
memory_profile
包的简单测试支持这样一种说法,即zeros
和empty
动态分配内存,而zeros_like
预先分配所有内容:相关问题 更多 >
编程相关推荐