Python Numpy二维数组遍历效率对比:切片与Numba加速实测
本文通过实测对比Python中Numpy二维数组的两种遍历方式:双层循环与切片后一维遍历,并引入Numba JIT加速。结果显示,切片遍历通常更快,但Numba首次调用存在编译开销。文章详细解析了不同数据规模下的耗时差异及底层内存引用机制,为高性能计算提供实践参考。
Numpy二维数组遍历方式对比
在使用python-numpy处理数据时,访问数组元素主要有两种方式:一是使用双层for循环直接遍历所有元素,二是先对数组进行切片操作提取每一行,再对得到的一维数组进行遍历。
具体实现代码如下:
import numpy as np
import time
NUM = 160
a=np.random.random((NUM,NUM))
start = time.time()
for i in range(NUM):
for j in range(NUM):
if a[i][j] == 1.0:
pass
end1 = time.time()
for ii in range(NUM):
b = a[ii,:]
for jj in range(NUM):
if b[jj] == 1.0:
pass
end2 = time.time()
print("end1",end1-start)
print("end2",end2-end1)由于生成的随机数位于[0,1)区间内,上述两种操作均会完整遍历数组中的所有元素。经过多轮测试,得到的耗时数据如下:
当数组规模NUM设置为160时:
end1 0.006983518600463867 end2 0.003988742828369141
当数组规模NUM扩大至1600时:
end1 0.71415114402771 end2 0.45178747177124023
结论:切片后的遍历方式速度更快。 原因分析: 作者目前尚未完全明确具体原因。
一种可能的解释:
b=a[ii,:]在中,为了提高运行效率,切片生成的子矩阵实际上只是原矩阵的内存引用(视图),因此修改子矩阵会影响原矩阵。在内层循环中,第二种方法直接在元素所在的内存区域进行查找;而第一种方法需要先定位行索引,再定位列索引,涉及更多的寻址操作,因此速度较慢。欢迎分享其他观点。
Numba加速对性能的影响
关于numba在小数据量下性能反而低于普通操作的现象分析
什么是numba?
numba简介
实验对比:
import numpy as np
import time
NUM = 160
from numba import jit
a=np.random.random((NUM,NUM))
@jit(nopython=True)
def fun1(a):
for i in range(NUM):
for j in range(NUM):
if a[i][j] == 1.0:
pass
def fun2(a):
for i in range(NUM):
for j in range(NUM):
if a[i][j] == 1.0:
pass
@jit(nopython=True)
def fun3(a):
for ii in range(NUM):
b = a[ii,:]
for jj in range(NUM):
if b[jj] == 1.0:
pass
def fun4(a):
for iii in range(NUM):
b = a[iii,:]
for jjj in range(NUM):
if b[jjj] == 1.0:
pass
start = time.time()
fun1(a)
end1 = time.time()
fun2(a)
end2 = time.time()
fun3(a)
end3 = time.time()
fun4(a)
end4 = time.time()
print("end1",end1-start)
print("end2",end2-end1)
print("end3",end3-end2)
print("end4",end4-end3)首先,当NUM为1600时,测试结果如下:
end1 0.2991981506347656 #无切片,有加速 end2 0.6372940540313721 #无切片,无加速 end3 0.08377814292907715 #有切片,有加速 end4 0.358079195022583 #有切片,无加速
在其他条件相同的情况下,使用切片的速度更快。同时,启用加速的效果也优于未加速的情况。 然而,当NUM设置为160时,结果如下:
end1 0.29620814323425293 #无切片,有加速 end2 0.006980180740356445 #无切片,无加速 end3 0.08580684661865234 #有切片,有加速 end4 0.0029993057250976562 #有切片,无加速
尽管有切片的情况依然比无切片快,但启用加速后的运行时间却比未加速时更长。 原因在于:@jit(nopython=True)装饰器仅对函数进行修饰,首次调用时会触发编译过程,将代码转换为机器码,这一过程需要消耗时间;后续调用则直接使用编译后的代码,速度显著提升。
实验相关代码如下:
import numpy as np
import time
NUM = 160
from numba import jit
a=np.random.random((NUM,NUM))
@jit(nopython=True)
def fun1(a):
for i in range(NUM):
for j in range(NUM):
if a[i][j] == 1.0:
pass
def fun2(a):
for i in range(NUM):
for j in range(NUM):
if a[i][j] == 1.0:
pass
@jit(nopython=True)
def fun3(a):
for ii in range(NUM):
b = a[ii,:]
for jj in range(NUM):
if b[jj] == 1.0:
pass
def fun4(a):
for iii in range(NUM):
b = a[iii,:]
for jjj in range(NUM):
if b[jjj] == 1.0:
pass
for b in range(4):
start = time.time()
fun1(a)
end1 = time.time()
fun2(a)
end2 = time.time()
fun3(a)
end3 = time.time()
fun4(a)
end4 = time.time()
print("end1",end1-start)
print("end2",end2-end1)
print("end3",end3-end2)
print("end4",end4-end3)
print("---")运行结果如下:
end1 0.29421305656433105 end2 0.0059833526611328125 end3 0.08181905746459961 end4 0.0029909610748291016 --- end1 0.0 end2 0.005949735641479492 end3 0.0 end4 0.004008769989013672 --- end1 0.0 end2 0.006977558135986328 end3 0.0 end4 0.00399017333984375 --- end1 0.0 end2 0.005974292755126953 end3 0.0 end4 0.003837108612060547 ---
结论总结:
在使用numba加速时,第一次调用需要执行编译过程,因此耗时较长;之后的调用则无需重复编译,速度大幅提升。
至此,关于Python中Numpy二维数组遍历与切片后遍历效率比较的内容介绍完毕。如需了解更多相关Numpy二维数组遍历与二维数组切片后遍历效率比较的内容,请搜索本站历史文章或继续浏览下方相关文章。希望大家今后多多支持本站!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









