Python中“和”理解的速度

时间:2016-02-04 03:12:50

标签: python optimization

我的印象是使用sum构造要比运行for循环快得多。但是,在以下代码中,for循环实际上运行得更快:

import time

Score = [[3,4,5,6,7,8] for i in range(40)]

a=[0,1,2,3,4,5,4,5,2,1,3,0,5,1,0,3,4,2,2,4,4,5,1,2,5,4,3,2,0,1,1,0,2,0,0,0,1,3,2,1]

def ver1():
    for i in range(100000):
        total = 0
        for j in range(40):
            total+=Score[j][a[j]]
    print (total)

def ver2():
    for i in range(100000):
        total = sum(Score[j][a[j]] for j in range(40))
    print (total)


t0 = time.time()
ver1()
t1 = time.time()
ver2()
t2 = time.time()

print("Version 1 time: ", t1-t0)
print("Version 2 time: ", t2-t1)

输出结果为:

208
208
Version 1 time:  0.9300529956817627
Version 2 time:  1.066061019897461

我做错了吗?有没有办法更快地做到这一点?

(请注意,这只是我设置的一个演示,在我的实际应用中,分数不会以这种方式重复)

一些额外的信息:这是在64位的Python 3.4.4上,在Windows 7 64位上,在i7上运行。

2 个答案:

答案 0 :(得分:2)

这似乎取决于系统,可能是python版本。在我的系统上,差异大约是13%:

python sum.py 
208
208
('Version 1 time: ', 0.6371259689331055)
('Version 2 time: ', 0.7342419624328613)

这两个版本没有衡量sum与手动循环,因为循环"机构"不一样。 ver2做了更多的工作,因为它创建了100000次生成器表达式,而ver1的循环体几乎是微不足道的,但它为每次迭代创建了一个包含40个元素的列表。您可以将示例更改为相同,然后您会看到sum

的效果
def ver1():
    r = [Score[j][a[j]] for j in range(40)]
    for i in xrange(100000):
        total = 0
        for j in r:
            total+=j
    print (total)

def ver2():
    r = [Score[j][a[j]] for j in xrange(40)]
    for i in xrange(100000):
        total = sum(r)
    print (total)

我已将所有内容移出内部循环体并退出sum调用,以确保我们仅测量手工制作循环的开销。使用xrange而不是range可以进一步改善整个运行时,但这适用于两个版本,因此不会更改比较。我系统上修改过的代码的结果是:

python sum.py
208
208
('Version 1 time: ', 0.2034609317779541)
('Version 2 time: ', 0.04234910011291504)

ver2ver1快五倍。这是使用sum而不是手工制作循环的纯粹性能提升。

ShadowRanger's comment on the question about lookups的启发,我修改了示例以比较原始代码并检查是否查找了绑定符号:

def gen(s,b):
    for j in xrange(40):
        yield s[j][b[j]]

def ver2():
    for i in range(100000):
        total = sum(gen(Score, a))
    print (total)

我创建了一个小型自定义生成器,它在本地绑定Scorea,以防止在父作用域中进行昂贵的查找。执行此:

python sum.py
208
208
('Version 1 time: ', 0.6167840957641602)
('Version 2 time: ', 0.6198039054870605)

单独的符号查找占运行时的约12%。

答案 1 :(得分:1)

由于j迭代两个列表,我想我会看到zip是否更好用了:

def ver3():
    for i in range(100000):
        total = sum(s[i] for s,i in zip(Score,a))
    print (total)

在Py2上,这比版本2慢大约30%,但在Py3上比版本1慢大约20%。如果我将zip更改为izip(从itertools导入),这样可以缩短版本1到2之间的时间。