如何快速将numpy数组重置为零且不改变其原有内存地址?
你观察到np.zeros仅需几微秒的现象是**写时复制(COW)机制导致的伪性能假象:现代操作系统对内存分配采用延迟绑定策略,np.zeros调用时仅分配虚拟地址空间,并未实际分配物理内存,也没有真的写入0值,实际的内存写入开销会延迟到你第一次访问对应内存页的时候才触发,所以单独测np.zeros的时间没有包含实际的内存操作成本。
问题1:是否有方法可以和创建新数组一样快的速度重置原有数组的值为0?
没有。原有数组已经完成了虚拟地址到物理内存的映射,你要把整个/部分区域清零,必须真实遍历对应内存页写入0值,这个内存写入的开销是无法避免的,不可能达到np.zeros那种仅分配虚拟地址的速度。
但可以用以下方法优化原有数组清零的速度:
- 优先用
x.fill(0):比切片广播赋值x[:] = 0性能高5%~10%,尤其是非连续数组的场景下差距更明显 - 连续数组可以调用底层
memset操作,C层面直接写入的实现:
import ctypes import numpy as np def fast_zero_fill(arr): if not arr.flags['C_CONTIGUOUS']: raise ValueError("仅支持连续数组") ctypes.memset(arr.ctypes.data, 0, arr.nbytes)
该方法和numpy内置的fill速度基本持平,部分场景下有小幅性能提升
- 合并清零和后续计算逻辑:如果清零后马上要做计算的话,用numba JIT把两个操作合并,减少一次内存读写,总开销能降低一半左右
问题2:是否有方法可以创建新的全零数组时沿用原有数组的内存地址?
可以,直接指定out参数即可:
np.zeros(x.shape, dtype=x.dtype, out=x)
这个操作不会分配新内存,直接在原有数组的内存空间上写入0值,内存地址和原数组完全一致,性能和x.fill(0)完全相同。
关于你测试结果的说明
你测试中np.zeros + sum仅42ms,而切片赋值+sum要413ms的核心原因是:
- 新创建的
np.zeros数组你只sum了一小段区域,操作系统仅分配了你sum用到的160MB左右物理页,剩下3.8GB未使用的内存完全没有触发实际的内存操作,所以总耗时短 - 切片赋值的操作是真的把3GB的区域全部写入了0值,这部分写入的开销就有300ms以上,sum的开销只有几十ms
如果你的业务逻辑后续需要访问整个数组的所有区域,np.zeros的总开销会和直接清零原有数组完全一致,甚至更高(因为触发大量页错误的开销比直接memset更高。
其他优化方案
如果你对性能要求极高,可以考虑:
- 每次仅清零实际用过的区域,不要清零整个数组
- 大数组操作迁移到GPU执行,正如你最后用torch的方案,GPU显存带宽远高于CPU内存,大数组清零速度可以提升一个数量级
内容的提问来源于stack exchange,提问作者THN
相关产品推荐
相关产品推荐

