简体   繁体   English

为什么直接索引数组比迭代快得多?

[英]Why is direct indexing of an array significantly faster than iteration?

Just some Python code for an example: 仅提供一些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 can grasp that some extra time will be taken in the loop because the value of i must be incremented a few times, but the difference between the running times of these two different methods seems a lot bigger than I expected. 我可以理解,因为i的值必须增加几次,所以循环会花费一些额外的时间,但是这两种不同方法的运行时间之间的差异似乎比我预期的要大得多。 Is there something else happening underneath the hood that I'm not considering? 引擎盖下是否还有其他我没有考虑的事情?

Short answer: it isn't, unless the loop is very small. 简短的答案:除非循环非常小,否则不会。 The for loop has a small overhead, but the way you're doing it is inefficient. for循环的开销很小,但是您这样做的效率很低。 By using range(len(nums)) you're effectively creating another list and iterating through that, then doing the same index lookups anyway. 通过使用range(len(nums))您可以有效地创建另一个列表并对其进行迭代,然后无论如何都要进行相同的索引查找。 Try this: 尝试这个:

for i in nums:
    print(i)

Results for me were as expected: 对我而言,结果符合预期:

>>> 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

With a bigger list the advantage of the loop becomes apparent, because the incremental cost of accessing an element by index outweighs the one-off cost of the loop: 列表越大,循环的优势就越明显,因为按索引访问元素的增量成本超过了循环的一次性成本:

>>> 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

In python 3, which puts a greater emphasis on iterators over indexable lists, the difference is even greater: 在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

With such a small array you're probably measuring noise first, and then the overhead of calling range() . 使用如此小的数组,您可能首先要测量噪声,然后再测量range()的开销。 Note that range not only has to increment a variable a few times, it also creates an object that holds its state (the current value) because it's a generator. 请注意, range不仅必须增加变量几次,而且还因为它是生成器而创建了一个保存其状态(当前值)的对象。 The function call and object creation are two things you don't pay for in the second example and for very short iterations they will probably dwarf three array accesses. 函数调用和对象创建是您在第二个示例中无需支付的两件事,对于非常短的迭代,它们可能会使三个数组访问相形见war。

Essentially your second snippet does loop unrolling , which is a viable and frequent technique of speeding up performance-critical code. 本质上,您的第二个代码片段确实会循环展开 ,这是一种提高性能关键代码的可行且频繁的技术。

The for loop have a cost in any case, and the one you write is especially costly. 在任何情况下, for loop产生成本,并且您编写的for loop特别昂贵。 Here is four versions, using timeit for measure time: 这是四个版本,使用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))

Here is the found times: 这是找到的时间:

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

The one_no_access show the cost of the expression range(len(NUMS)) . one_no_access显示表达式range(len(NUMS))的开销。 While lists in python are stocked contiguously in memory, the random access of elements is in O(1) , explaining two as the quicker. 虽然python中的列表连续存储在内存中,但是元素的随机访问位于O(1) ,这两个元素的解释速度更快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM