为什么直接索引数组的速度明显快于迭代?

时间:2016-04-04 07:22:04

标签: python arrays performance indexing iteration

一些示例的Python代码:

nums = [1,2,3]
start = timer()
for i in range(len(nums)):
  print(nums[i])
end = timer()

print((end-start)) #computed to 0.0697546862831


start = timer()
print(nums[0])
print(nums[1])
print(nums[2])
end = timer()

print((end-start)) #computed to 0.0167170338524

我可以理解循环中会有一些额外的时间,因为i的值必须增加几次,但这两种不同方法的运行时间之间的差异似乎比我预期的要大很多。是否还有其他事情发生在引擎盖下,我没有考虑过?

3 个答案:

答案 0 :(得分:4)

简短回答:不是,除非循环非常小。 for循环的开销很小,但是你这样做的效率很低。通过使用range(len(nums)),您可以有效地创建另一个列表并迭代它,然后进行相同的索引查找。试试这个:

for i in nums:
    print(i)

我的结果与预期一致:

>>> import timeit
>>> timeit.timeit('nums[0];nums[1];nums[2]', setup='nums = [1,2,3]')
0.10711812973022461
>>> timeit.timeit('for i in nums:pass', setup='nums = [1,2,3]')
0.13474011421203613
>>> timeit.timeit('for i in range(len(nums)):pass', setup='nums = [1,2,3]')
0.42371487617492676

使用更大的列表,循环的优势变得明显,因为通过索引访问元素的增量成本超过了循环的一次性成本:

>>> timeit.timeit('for i in nums:pass', setup='nums = range(0,100)')
1.541944980621338
timeit.timeit(';'.join('nums[%s]' % i for i in range(0,100)), setup='nums = range(0,100)')
2.5244338512420654

在python 3中,它比可索引列表更强调迭代器,差异更大:

>>> timeit.timeit('for i in nums:pass', setup='nums = range(0,100)')
1.6542046590038808
>>> timeit.timeit(';'.join('nums[%s]' % i for i in range(0,100)), setup='nums = range(0,100)')
10.331634456000756

答案 1 :(得分:2)

使用如此小的阵列,您可能首先测量噪音,然后调用range()的开销。请注意,range不仅必须将变量增加几次,还会创建一个保持其状态(当前值)的对象,因为它是一个生成器。函数调用和对象创建是你在第二个例子中付出的两件事,对于非常短的迭代,他们可能会使三个数组访问相形见绌。

基本上你的第二个代码片段循环展开,这是一种加速性能关键代码的可行且常用的技术。

答案 2 :(得分:0)

for loop在任何情况下都有成本,而你写的那个成本特别高。这里有四个版本,使用timeit测量时间:

from timeit import timeit

NUMS = [1, 2, 3]


def one():
    for i in range(len(NUMS)):
          NUMS[i]


def one_no_access():
    for i in range(len(NUMS)):
          i


def two():
    NUMS[0]
    NUMS[1]
    NUMS[2]


def three():
    for i in NUMS:
        i


for func in (one, one_no_access, two, three):
    print(func.__name__ + ':', timeit(func))

以下是发现时间:

one: 1.0467438200000743
one_no_access: 0.8853238560000136
two: 0.3143197629999577
three: 0.3478466749998006

one_no_access显示表达式range(len(NUMS))的费用。 虽然python中的列表在内存中连续存放,但元素的随机访问位于O(1),解释两个更快。