Python3与Python2在列表/生成器范围性能上的比较

5 投票
1 回答
7953 浏览
提问于 2025-04-18 05:01

我有一个简单的函数,它可以把一个列表分开,并返回一个索引 i。这个索引 i 的特点是,列表中索引小于 i 的元素都比 list[i] 小,而索引大于 i 的元素都比 list[i] 大。

def partition(arr):
    first_high = 0
    pivot = len(arr) - 1
    for i in range(len(arr)):
        if arr[i] < arr[pivot]:
            arr[first_high], arr[i] = arr[i], arr[first_high]
            first_high = first_high + 1

    arr[first_high], arr[pivot] = arr[pivot], arr[first_high]
    return first_high


if __name__ == "__main__":
    arr = [1, 5, 4, 6, 0, 3]
    pivot = partition(arr)
    print(pivot)

在 OS X 上,使用 Python 3.4 的运行时间明显比 Python 2.7.6 要长:

time python3 partition.py
real 0m0.040s
user 0m0.027s
sys  0m0.010s

time python partition.py
real 0m0.031s
user 0m0.018s
sys  0m0.011s

在 ubuntu 14.04 / 虚拟机上也是一样的情况。

使用 python3:

real 0m0.049s
user 0m0.034s
sys  0m0.015s

使用 python:

real 0m0.044s
user 0m0.022s
sys  0m0.018s

是 Python 3 本身就比 Python 2.7 慢,还是说有一些特定的优化可以让代码在 Python 2.7 上运行得更快呢?

1 个回答

13

正如评论中提到的,你应该用 timeit 来测试代码的运行时间,而不是用操作系统的工具。

我猜测在Python 3中,range 函数的速度可能稍微慢一些。在Python 2中,它直接返回一个 列表,而在Python 3中,它返回一个 range,这个东西的行为更像是一个生成器。我做了一些性能测试,结果可能能给你一些启示:

python -mtimeit "range(10)"
1000000 loops, best of 3: 0.474 usec per loop

python3 -mtimeit "range(10)"
1000000 loops, best of 3: 0.59 usec per loop

python -mtimeit "range(100)"
1000000 loops, best of 3: 1.1 usec per loop

python3 -mtimeit "range(100)"
1000000 loops, best of 3: 0.578 usec per loop

python -mtimeit "range(1000)"
100000 loops, best of 3: 11.6 usec per loop

python3 -mtimeit "range(1000)"
1000000 loops, best of 3: 0.66 usec per loop

从结果来看,当给 range 的输入很时,Python 2的速度比较快。如果输入变大,Python 3的 range 表现得更好。

我的建议是:用更大的数组进行测试,比如一百个或一千个元素。

实际上,我还进一步测试了对所有元素的完整迭代。结果完全支持Python 2:

python -mtimeit "for i in range(1000):pass"
10000 loops, best of 3: 31 usec per loop

python3 -mtimeit "for i in range(1000):pass"
10000 loops, best of 3: 45.3 usec per loop

python -mtimeit "for i in range(10000):pass"
1000 loops, best of 3: 330 usec per loop

python3 -mtimeit "for i in range(10000):pass"
1000 loops, best of 3: 480 usec per loop

我的结论是,遍历一个列表可能比遍历一个生成器要快。虽然后者在内存使用上绝对更高效。这是速度和内存之间权衡的经典例子。虽然速度差异本身并不那么大(少于毫秒级别)。所以你应该考虑这一点,以及哪个对你的程序更好。

撰写回答