Jru*_*ruv 3 performance python-3.x
请看下面的代码:
from timeit import timeit
import random
for size in (10 ** 3, 10 ** 4, 10 ** 5, 10 ** 6):
print('---')
nums = [random.randint(1, 10000000) for i in range(size)]
l = len(nums)
sp = l // 2
def with_slice():
for n in nums[sp:]:
pass
def pure_loop():
for i in range(sp, l):
pass
def no_slice():
for i in range(sp, l):
n = nums[i]
for method in (with_slice, pure_loop, no_slice):
print(f'Size={size:<10} method={method.__name__:<20} time={timeit(method, number=300)} ms')
Run Code Online (Sandbox Code Playgroud)
测试结果:
---
Size=1000 method=with_slice time=0.003086836077272892 ms
Size=1000 method=pure_loop time=0.00524100661277771 ms
Size=1000 method=no_slice time=0.01368624996393919 ms
---
Size=10000 method=with_slice time=0.031104332767426968 ms
Size=10000 method=pure_loop time=0.05062220152467489 ms
Size=10000 method=no_slice time=0.12812853325158358 ms
---
Size=100000 method=with_slice time=0.35493253357708454 ms
Size=100000 method=pure_loop time=0.5190340830013156 ms
Size=100000 method=no_slice time=1.2699861200526357 ms
---
Size=1000000 method=with_slice time=5.068190313875675 ms
Size=1000000 method=pure_loop time=5.029146575368941 ms
Size=1000000 method=no_slice time=12.877492633648217 ms
Run Code Online (Sandbox Code Playgroud)
据我所知,切片操作将创建一个新列表,其中包含切片中每个项目的引用的副本。在我的想象中,应该比no_slice没有任何复制操作的方式贵。但从测试结果来看,sliceeven 的方式比range(). 为什么会出现这种情况?
\n\n据我所知,切片操作将创建一个新列表,其中包含切片中每个项目的引用的副本
\n
事实上,CPython 解释器的标准实现确实复制了一份。
\n\n\n在我的想象中,它应该比没有任何复制操作的 no_slice 方式昂贵。但从测试结果来看,切片方式甚至比 range() 上的纯循环更快。为什么会出现这种情况?
\n
要点是复制列表比迭代列表快得多因为与 C 中优化的切片操作(使用相同的解释器)相比,解释器的循环相当慢。
\n在我的机器上,包含nums5000 个随机项,切片nums大约需要 8 \xc2\xb5s,而迭代大约nums需要 33 \xc2\xb5s。迭代切片nums需要 41 \xc2\xb5s。迭代范围对象(具有相同数量的项目)需要 68 \xc2\xb5s。
乍一看,迭代范围对象比其他方法要慢得多,这一事实非常令人惊讶。您可能期望它比迭代更快nums或至少一样快。它更昂贵,因为循环迭代生成器(更具体地说是range_iterator)。在 CPython 上迭代这样的生成器对象非常慢。这主要是由于纯Python整数的增量及其每次循环迭代的比较。纯 Python 整数非常昂贵,因为它们可能比本机整数更大(CPython 需要考虑范围可能非常大的情况,尽管在实践中从未发生过)。管理纯 Python 对象也会带来额外的开销(分配、释放、引用计数、间接等)。当 CPython 内部迭代列表时,无需使用此类整数,因为列表的大小应始终适合本机整数(在 64 位计算机上通常约为 2**64)。
总之,测量切片然后迭代列表对象with_slice的时间。测量迭代范围对象所需的时间。 测量迭代范围对象以及每个项目访问以将值放入局部变量的时间。最后一个操作比其他两个操作要昂贵得多,因为每次迭代都会解释访问,而解释器可以在第一个函数中更快地迭代列表。它还包括第二个功能的成本,该成本已经相当大了。缺少的是 与 的迭代之间的比较。numspure_loopno_slicenumsnumsnums[:]nums
请注意, 的成本with_slice更接近于较大pure_loop时的成本size,因为列表太大而无法容纳快速的 L1 缓存,并且需要从 LLC 缓存或通常从慢速主 RAM 复制。
| 归档时间: |
|
| 查看次数: |
968 次 |
| 最近记录: |