根据博客文章here,任何()+生成器表达式应该比for循环更快地运行,看起来他的推理是有道理的。
但是我尝试使用这种方法(虽然在其他一些函数上),但它似乎需要比显式for循环运行更长的时间。
def with_loop(a, b):
for x in xrange(1, b):
if x * a % b == 1: return True
return False
def with_generator(a, b):
return any(x * a % b == 1 for x in xrange(1, b))
基本上,代码循环遍历从1到 b 的所有数字,以查找数字 a 是否具有模块化反转。
我从运行这些功能中获得的时间是:
>>> from timeit import Timer as T
>>> T(lambda : with_generator(100, 300)).repeat(number = 100000)
[3.4041796334919923, 3.6303230626526215, 3.6714475531563266]
>>> T(lambda : with_loop(100, 300)).repeat(number = 100000)
[2.1977450660490376, 2.2083902291327604, 2.1905292602997406]
>>> T(lambda : with_generator(101, 300)).repeat(number = 100000)
[1.213779524696747, 1.2228346702509043, 1.2216941170634072]
>>> T(lambda : with_loop(101, 300)).repeat(number = 100000)
[0.7431202233722161, 0.7444361146951906, 0.7525384471628058]
with_generator(100,300)返回False,with_generator(101,300)返回True。
似乎with_generator的运行时间比with_loop要长。这有什么理由吗?
编辑: 有没有其他更短或更优雅的方法重写with_loop,以便我们实现类似或更好的性能?谢谢!
答案 0 :(得分:3)
<强>上下文强>
我认为
任何()+生成器表达式应该比for循环
运行得更快
表示any
不生成所有值,但循环执行:
>>> T(lambda : any([x * 101 % 300 == 1 for x in xrange(1, 300)])).repeat(number = 100000)
[5.7612644951345935, 5.742304846931542, 5.746804810873488]
>>> T(lambda : any(x * 101 % 300 == 1 for x in xrange(1, 300))).repeat(number = 100000)
[2.1652204281427814, 2.1640463131248886, 2.164674290446399]
因此引用并不意味着循环永远无法实现生成器的性能。
引用意味着循环通常会生成所有元素,而任何元素都不会使用所有元素,而生成器只生成任何使用的元素。
您的函数with_loop
等同于生成器。所以你不能指望不同的行为。
更明确地说:any(loop)
比any(generator)
慢,因为循环会生成所有内容。您的with_loop
相当于any(generator)
,而不是any(loop)
。
原始问题
>>> profile.run("""T(lambda : with_loop(101, 300)).repeat(number = 100000)""")
600043 function calls (600040 primitive calls) in 6.133 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
3 0.000 0.000 0.000 0.000 :0(append)
6 0.000 0.000 0.000 0.000 :0(clock)
3 0.000 0.000 0.000 0.000 :0(disable)
3 0.000 0.000 0.000 0.000 :0(enable)
3 0.000 0.000 0.000 0.000 :0(globals)
1 0.000 0.000 0.000 0.000 :0(hasattr)
3 0.000 0.000 0.000 0.000 :0(isenabled)
2 0.000 0.000 0.000 0.000 :0(isinstance)
1 0.000 0.000 0.000 0.000 :0(range)
1 0.005 0.005 0.005 0.005 :0(setprofile)
300000 0.579 0.000 5.841 0.000 <string>:1(<lambda>)
4/1 0.000 0.000 6.128 6.128 <string>:1(<module>)
300000 5.262 0.000 5.262 0.000 <string>:1(with_loop)
1 0.000 0.000 6.133 6.133 profile:0(T(lambda : with_loop(101, 300)).repeat(number = 100000))
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.000 0.000 timeit.py:121(__init__)
3 0.000 0.000 0.000 0.000 timeit.py:143(setup)
3 0.000 0.000 6.128 2.043 timeit.py:178(timeit)
1 0.000 0.000 6.128 6.128 timeit.py:201(repeat)
1 0.000 0.000 0.000 0.000 timeit.py:94(_template_func)
3 0.287 0.096 6.128 2.043 timeit.py:96(inner)
>>> profile.run("""T(lambda : with_generator(101, 300)).repeat(number = 100000)""")
31500043 function calls (31500040 primitive calls) in 70.531 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
300000 30.898 0.000 67.590 0.000 :0(any)
3 0.000 0.000 0.000 0.000 :0(append)
6 0.000 0.000 0.000 0.000 :0(clock)
3 0.000 0.000 0.000 0.000 :0(disable)
3 0.000 0.000 0.000 0.000 :0(enable)
3 0.000 0.000 0.000 0.000 :0(globals)
1 0.000 0.000 0.000 0.000 :0(hasattr)
3 0.000 0.000 0.000 0.000 :0(isenabled)
2 0.000 0.000 0.000 0.000 :0(isinstance)
1 0.000 0.000 0.000 0.000 :0(range)
1 0.000 0.000 0.000 0.000 :0(setprofile)
300000 0.667 0.000 70.222 0.000 <string>:1(<lambda>)
4/1 0.000 0.000 70.531 70.531 <string>:1(<module>)
300000 1.629 0.000 69.555 0.000 <string>:6(with_generator)
30600000 37.027 0.000 37.027 0.000 <string>:7(<genexpr>)
1 0.000 0.000 70.531 70.531 profile:0(T(lambda : with_generator(101, 300)).repeat(number = 100000))
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.000 0.000 timeit.py:121(__init__)
3 0.000 0.000 0.000 0.000 timeit.py:143(setup)
3 0.000 0.000 70.531 23.510 timeit.py:178(timeit)
1 0.000 0.000 70.531 70.531 timeit.py:201(repeat)
1 0.000 0.000 0.000 0.000 timeit.py:94(_template_func)
3 0.309 0.103 70.531 23.510 timeit.py:96(inner)
每次调用生成器,30600000次,似乎比for循环慢得多。
如果您知道列表中存在多少元素,那么您可以写下:
l[0] * 101 % 300 == 1 or l[1] * 101 % 300 == 1 or l[2] * 101 % 300 == 1 or ....