1

假设有一个类 A 并且有一个类 A 的实例列表,称为lst

假设我们想m在列表中的每个实例上一遍又一遍地调用 A 类的特定方法 ,(实际示例:entity.update()游戏循环中的方法)。我们知道执行此操作的简单方法如下:

for obj in lst: obj.m()

然而,这种代码让我们睡着了。于是我们想到map了如下的使用方式:

map(lambda obj: obj.m(), lst)

for但是我们对上面的代码行进行了几次测试,结果证明它比我们的简单循环慢得多。有时它甚至慢 2 倍。然后我们自己想,“嗯,可能它更慢,因为map为所有函数调用构造了一个返回值列表并返回该列表”。

假设我们从名为xrange. 在大多数情况下,我们自己认为,这是一个更酷的版本range。因此,我们定义了一个名为的函数xmap,它只是将一个函数应用于对象列表,而无需构造返回值列表并返回它。实现如下:

def xmap(func, lst):
    for obj in lst: func(obj)

很酷,因为这个函数只是执行for上面的循环,只有它允许我们保持幻想并发送我们的 lambda 函数。我们认为这是完美的妥协。但是我们非常细致和小心,所以我们决定制作 2 个脚本来测试我们代码的速度,看看我们是否真的让它比map.

我们的第一个脚本将简单地使用map并无用地构建一个我们甚至不需要的列表。

script1.py

class A:
    def m(self):
        pass

lst = [A() for i in xrange(15)]

import time
start = time.time()

for i in xrange(1000000):
    map(lambda obj: obj.m(), lst)

print time.time()-start, 'seconds'

我们的第二个脚本将使用xmap,我们相信它会更快,因为它不必构造一个包含 15 个返回值的列表 1,000,000 次并返回它。

script2.py

def xmap(func, lst):
    for obj in lst: func(obj)

class A:
    def m(self):
        pass

lst = [A() for i in xrange(15)]

import time
start = time.time()

for i in xrange(1000000):
    xmap(lambda obj: obj.m(), lst)

print time.time()-start, 'seconds'

最后,我们完成了并且有点兴奋地看到我们的代码会变得多快。然而,在对彼此运行了几次脚本之后,事实证明这script2.py似乎并不比script1.py. 实际上,事实证明,script2.py有时运行时间甚至比script1.py. xmap似乎花费的时间或多或少与map.

为什么我会得到这些结果?

C:\dev\py>python script1.py
14.7799999714 seconds

C:\dev\py>python script2.py
14.2170000076 seconds

C:\dev\py>python script1.py
12.1800000668 seconds

C:\dev\py>python script2.py
12.5759999752 seconds

C:\dev\py>python script1.py
14.3020000458 seconds

C:\dev\py>python script2.py
14.9490001202 seconds

C:\dev\py>python script1.py
14.6879999638 seconds

C:\dev\py>python script2.py
14.3139998913 seconds

我认为我至少会优化一些东西map因为我没有构建返回值列表,但我的代码似乎并没有更快。我知道列表构建需要一些时间,因为我已经完成了以下操作:

>>> import timeit
>>> timeit.timeit('[]')
0.1297345953932106
>>> timeit.timeit('[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]')
0.6807682686160632
>>> timeit.timeit('[None,None,None,None,None,None,None,None,None,None,None,None,
None,None,None]')
0.7460120889200539

那么为什么我的xmap功能似乎没有比 更快map

4

2 回答 2

7

首先,也是最重要的:像这样的计时微优化只会让您感到困惑,因为您正在测量诸如函数之类的非常低级别的开销。

不过,在这种情况下,我很确定问题在于,map/xmap由于额外的lambda. 如果你A.m直接使用,情况会好很多:

>>> %timeit for obj in lst: obj.m()
100000 loops, best of 3: 2.99 µs per loop
>>> %timeit [obj.m() for obj in lst]
100000 loops, best of 3: 3.5 µs per loop
>>> %timeit xmap(lambda obj: obj.m(), lst)
100000 loops, best of 3: 5.69 µs per loop
>>> %timeit xmap(A.m, lst)
100000 loops, best of 3: 3.32 µs per loop

FWIW,在我看来,您最终xmap可以获胜:

>>> lst = [A() for i in xrange(10**3)]
>>> %timeit for obj in lst: obj.m()
1000 loops, best of 3: 198 µs per loop
>>> %timeit [obj.m() for obj in lst]
1000 loops, best of 3: 216 µs per loop
>>> %timeit xmap(lambda obj: obj.m(), lst)
1000 loops, best of 3: 353 µs per loop
>>> %timeit xmap(A.m, lst)
10000 loops, best of 3: 189 µs per loop

但我也不会太认真对待这些数字。

当您说“那种代码 [即简单的 for 循环] 让我们入睡”时,我同意——编写简单的循环意味着您可以更快地完成编程并且可以早点睡觉。

于 2013-09-25T01:06:56.577 回答
3

map 是用 C 实现的,并且在 C-loop-land 中工作。您正在 Python-loop-land 中运行。

于 2013-09-25T01:03:50.640 回答