You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 10:50:46