为何Numpy数组逐元素遍历比按列批量操作速度更快?
问题与测试结果
原本想验证遍历数组前两个维度是否比按列执行操作慢很多,结果意外发现逐元素操作反而更快,求解释。
测试代码
import numpy as np import timeit def row_by_row(arr, cop): for i in range(arr.shape[0]): for ii in range(arr.shape[1]): arr[i, ii] = cop[i, ii].copy() return arr def all(arr, cop): for i in range(arr.shape[1]): arr[:,i] = cop[:, i].copy() return arr print(timeit.timeit("row_by_row(arr, cop)", setup="arr=np.ones((26, 15, 5000)); cop = np.random.random((26, 15,5000))",number=50, globals=globals())) print(timeit.timeit("all(arr, cop)",setup="arr=np.ones((26, 15,5000)); cop=np.random.random((26, 15,5000))", number=50, globals=globals()))
运行耗时
0.12496590000000007 0.4989047
原因解释
核心原因在于NumPy数组的内存布局和连续/非连续数组的操作效率差异:
内存存储顺序:NumPy默认采用C语言式的**行优先(C-order)**存储,也就是数组元素在内存中按
第0维度→第1维度→第2维度的顺序连续排列。对于你的(26,15,5000)数组:arr[i, ii]是取第i行第ii列对应的一维子数组(shape=(5000,)),这部分元素在内存中是完全连续的块,访问和拷贝效率极高。arr[:, i]是取所有行的第i列对应的二维子数组(shape=(26,5000)),这部分元素在内存中是分散的(按arr[0,i,0], arr[1,i,0], ..., arr[25,i,0], arr[0,i,1], arr[1,i,1], ...的顺序存储),属于非连续数组视图,操作时无法利用连续内存的快速拷贝优化。
操作开销对比:
row_by_row函数:循环次数是26×15=390次,但每次操作的是连续的5000元素块。cop[i,ii].copy()是对连续内存的快速拷贝,赋值给arr[i,ii]也是连续内存的直接覆盖,底层可以调用高效的memcpy类操作,单次循环开销极低。all函数:循环次数仅15次,但每次操作的是非连续的(26,5000)数组。首先cop[:,i].copy()需要把非连续的内存重新整理成连续块(额外的内存拷贝开销),然后赋值给arr[:,i]时,又要把连续块的元素分散写入到非连续的内存地址中(无法用批量拷贝,只能逐元素或分块处理),单次循环的开销远高于前者。
冗余的
copy()调用:其实两个函数里的.copy()都是多余的——直接赋值arr[i,ii] = cop[i,ii]或arr[:,i] = cop[:,i]就能完成数据覆盖。但即使去掉copy(),all函数的效率依然会远低于row_by_row,因为核心的非连续数组操作瓶颈没有解决。
补充:如果想让按列操作的效率提升,可以先把数组转成Fortran顺序(列优先),或者使用np.ascontiguousarray将非连续视图转为连续数组后再操作,但这会带来额外的内存开销。
内容的提问来源于stack exchange,提问作者Samuel K.
相关产品推荐
相关产品推荐

