NumPy中就地赋值为何比创建新数组更慢?与内存管理有关吗?
为什么NumPy中就地赋值
D[:] = A @ B比创建新数组C = A @ B更慢? 核心原因不是内存分配的开销,而是额外的内存拷贝步骤和临时数组的处理,具体拆解如下:
1. 矩阵乘法的执行流程差异
方法1(
C = A @ B):
NumPy调用BLAS/LAPACK底层库执行矩阵乘法时,会直接分配一块连续的内存块存储计算结果,这个过程是高度优化的——BLAS会利用CPU缓存、SIMD向量指令等最大化计算效率。最后只是把新数组的引用赋值给C,几乎没有额外开销。方法2(
D[:] = A @ B):
流程变成了两步:先计算A@B生成临时数组,再把临时数组的内容逐元素复制到D的内存空间里。这多出来的内存拷贝操作,才是拖慢速度的关键——尤其是当数组规模较大时,拷贝的开销远超过你以为的“重复内存分配”开销。
2. 内存分配的实际开销被高估
现代操作系统的内存管理器对固定大小的连续内存分配优化极强,加上NumPy自身的内存池机制,重复分配相同尺寸的数组时,开销几乎可以忽略不计。相比之下,额外的内存拷贝是实打实的线性时间开销,自然会让方法2变慢。
3. 内存布局与缓存的影响
矩阵乘法生成的临时数组默认是**C连续(行优先)**的内存布局,而即使D也是C连续的,复制过程也无法利用BLAS的高效计算指令,只能做简单的内存拷贝。如果D的内存布局不连续(比如之前做过切片转置等操作),拷贝的开销会更大。
正确的高效预分配方式
想要真正利用预分配提升性能,应该使用np.matmul的out参数,让计算结果直接写入预分配的数组,跳过临时数组的生成:
import numpy as np A = np.random.rand(10, 10) B = np.random.rand(10, 10000) D = np.random.rand(10, 10000) # 真正高效的原地计算 for i in range(20000): np.matmul(A, B, out=D)
用%timeit测试的话,这个版本的速度会和方法1持平甚至更快,因为它既复用了预分配的内存,又避免了临时数组的拷贝。
内容的提问来源于stack exchange,提问作者cap
相关产品推荐
相关产品推荐

