Max*_*hon 67 python performance time-complexity python-3.x
我们都知道在Python中执行语句一定次数的常用方法是使用for循环.
这样做的一般方法是,
# I am assuming iterated list is redundant.
# Just the number of execution matters.
for _ in range(count):
pass
Run Code Online (Sandbox Code Playgroud)
我相信没有人会争辩说上面的代码是常见的实现,但是还有另一种选择.通过乘以引用来使用Python列表创建的速度.
# Uncommon way.
for _ in [0] * count:
pass
Run Code Online (Sandbox Code Playgroud)
还有旧的while方式.
i = 0
while i < count:
i += 1
Run Code Online (Sandbox Code Playgroud)
我测试了这些方法的执行时间.这是代码.
import timeit
repeat = 10
total = 10
setup = """
count = 100000
"""
test1 = """
for _ in range(count):
pass
"""
test2 = """
for _ in [0] * count:
pass
"""
test3 = """
i = 0
while i < count:
i += 1
"""
print(min(timeit.Timer(test1, setup=setup).repeat(repeat, total)))
print(min(timeit.Timer(test2, setup=setup).repeat(repeat, total)))
print(min(timeit.Timer(test3, setup=setup).repeat(repeat, total)))
# Results
0.02238852552017738
0.011760978361696095
0.06971727824807639
Run Code Online (Sandbox Code Playgroud)
如果存在小的差异,我不会发起主题,但是可以看出速度的差异是100%.如果第二种方法效率更高,为什么Python不鼓励这种用法呢?有没有更好的办法?
该测试使用Windows 10和Python 3.6完成.
按照@Tim Peters的建议,
.
.
.
test4 = """
for _ in itertools.repeat(None, count):
pass
"""
print(min(timeit.Timer(test1, setup=setup).repeat(repeat, total)))
print(min(timeit.Timer(test2, setup=setup).repeat(repeat, total)))
print(min(timeit.Timer(test3, setup=setup).repeat(repeat, total)))
print(min(timeit.Timer(test4, setup=setup).repeat(repeat, total)))
# Gives
0.02306803115612352
0.013021619340942758
0.06400113461638746
0.008105080015739174
Run Code Online (Sandbox Code Playgroud)
这提供了一个更好的方式,这几乎回答了我的问题.
为什么这比range两者都快,因为它们都是发电机.是因为价值永远不变吗?
Tim*_*ers 92
运用
for _ in itertools.repeat(None, count)
do something
Run Code Online (Sandbox Code Playgroud)
是获得最佳世界的非显而易见的方法:微小的恒定空间要求,并且每次迭代都不会创建新对象.在封面下,C代码repeat使用本机C整数类型(不是Python整数对象!)来跟踪剩余的计数.
出于这个原因,计数需要适合平台C ssize_t类型,通常最多2**31 - 1在32位盒子上,并且在64位盒子上:
>>> itertools.repeat(None, 2**63)
Traceback (most recent call last):
...
OverflowError: Python int too large to convert to C ssize_t
>>> itertools.repeat(None, 2**63-1)
repeat(None, 9223372036854775807)
Run Code Online (Sandbox Code Playgroud)
这对我的循环来说很重要;-)
Hyp*_*ino 11
第一种方法(在Python 3中)创建一个范围对象,它可以遍历值范围.(它就像一个生成器对象,但你可以多次遍历它.)它不会占用太多内存,因为它不包含整个值范围,只包含当前值和最大值,它不断增加步长(默认为1),直到达到或超过最大值.
比较尺寸range(0, 1000)大小list(range(0, 1000)):在线试试!.前者非常有效; 无论大小如何,它只需要48个字节,而整个列表在大小方面呈线性增长.
第二种方法虽然速度更快,却占用了我过去谈论的内存.(而且,似乎虽然0占用24个字节并None占用16个,但10000每个阵列的大小都相同.有趣.可能因为它们是指针)
有趣的[0] * 10000是,小于list(range(10000))大约10000,这是有道理的,因为在第一个,一切都是相同的原始值,所以它可以被优化.
第三个也是不错的,因为它不需要另外的筹码堆值(而调用range需要调用堆栈上的另一个地方),但因为它是慢的6倍,这是不值得说.
最后一个可能是最快的,因为itertools这样很酷:PI认为它使用了一些C库优化,如果我没记错的话.