Pythonint是封装实际数值的对象。我们可以修改这个值吗,例如将对象的值设置1为 2?那么这就1 == 2变成了True?
使用列表的insert函数比使用切片分配实现相同的效果要慢得多:
> python -m timeit -n 100000 -s "a=[]" "a.insert(0,0)"
100000 loops, best of 5: 19.2 usec per loop
> python -m timeit -n 100000 -s "a=[]" "a[0:0]=[0]"
100000 loops, best of 5: 6.78 usec per loop
Run Code Online (Sandbox Code Playgroud)
(请注意,这a=[]只是设置,因此a开始时为空,但后来增长到 100,000 个元素。)
起初我想可能是属性查找或函数调用开销左右,但在末尾插入表明这是可以忽略不计的:
> python -m timeit -n 100000 -s "a=[]" "a.insert(-1,0)"
100000 loops, best of 5: 79.1 nsec per loop
Run Code Online (Sandbox Code Playgroud)
为什么可能更简单的专用“插入单个元素”功能这么慢?
我也可以在 repl.it 上重现它:
from timeit import repeat
for _ in range(3):
for …Run Code Online (Sandbox Code Playgroud) 这段代码:
a = [1, 2, 3]
print(*a, a.pop(0))
Run Code Online (Sandbox Code Playgroud)
Python 3.8 打印2 3 1(在解包之前进行pop)。
Python 3.9 打印1 2 3 1(解压后执行pop)。
是什么导致了这种变化?我在变更日志中没有找到它。
编辑:不仅在函数调用中,而且在列表显示中:
a = [1, 2, 3]
b = [*a, a.pop(0)]
print(b)
Run Code Online (Sandbox Code Playgroud)
打印[2, 3, 1]vs[1, 2, 3, 1]打印 表达式列表显示“表达式从左到右求值”(这是 Python 3.8 文档的链接),所以我希望首先发生解包表达式。
调用 with n = 10**8,对我来说,简单循环始终比复杂循环慢得多,我不明白为什么:
def simple(n):
while n:
n -= 1
def complex(n):
while True:
if not n:
break
n -= 1
Run Code Online (Sandbox Code Playgroud)
有时以秒为单位:
def simple(n):
while n:
n -= 1
def complex(n):
while True:
if not n:
break
n -= 1
Run Code Online (Sandbox Code Playgroud)
这是字节码的循环部分,如下所示dis.dis(simple):
6 >> 6 LOAD_FAST 0 (n)
8 LOAD_CONST 1 (1)
10 BINARY_OP 23 (-=)
14 STORE_FAST 0 (n)
5 16 LOAD_FAST 0 (n)
18 POP_JUMP_BACKWARD_IF_TRUE 7 (to 6)
Run Code Online (Sandbox Code Playgroud)
对于complex:
10 >> 4 …Run Code Online (Sandbox Code Playgroud) 让他们竞争 3 次(每次 100 万次弹出/删除):
\nfrom timeit import timeit\n\nfor _ in range(3):\n t1 = timeit(\'b.pop(0)\', \'b = bytearray(1000000)\')\n t2 = timeit(\'del b[0]\', \'b = bytearray(1000000)\')\n print(t1 / t2)\nRun Code Online (Sandbox Code Playgroud)\n时间比例(在线尝试!):
\n274.6037053753368\n219.38099365582403\n252.08691226683823\nRun Code Online (Sandbox Code Playgroud)\n为什么pop做同样的事情要慢得多?
我list(x for x in a)用三个不同的 CPython 版本进行了测试。Ona = [0]比 on 快得多a = []:
3.9.0 64-bit 3.9.0 32-bit 3.7.8 64-bit
a = [] a = [0] a = [] a = [0] a = [] a = [0]
465 ns 412 ns 543 ns 515 ns 513 ns 457 ns
450 ns 406 ns 544 ns 515 ns 506 ns 491 ns
456 ns 408 ns 551 ns 513 ns 515 ns 487 ns
455 …Run Code Online (Sandbox Code Playgroud) 我偶尔会使用“技巧”来通过自身的映射版本来扩展列表,例如有效地计算 2 的幂:
from operator import mul
powers = [1]
powers += map(mul, [2] * 10, powers)
print(powers) # prints [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
Run Code Online (Sandbox Code Playgroud)
这依赖于+=立即将每个值附加map到列表中,以便map随后找到它并继续该过程。换句话说,它需要像这样工作:
powers = [1]
for value in map(mul, [2] * 10, powers):
powers.append(value)
Run Code Online (Sandbox Code Playgroud)
而不是首先像这样计算和存储整个右侧,最终powers结果是[1, 2]:
powers = [1]
powers += list(map(mul, [2] * 10, powers))
Run Code Online (Sandbox Code Playgroud)
是否在某个地方保证它能像以前那样工作?我检查了可变序列类型s += t文档,但除了暗示和的等价性之外,它没有说太多s.extend(t)。它确实引用了MutableSequence,其源代码包括:
def extend(self, …Run Code Online (Sandbox Code Playgroud) (更新:可能只发生在 Windows 的 CPython 3.8 32 位中,所以如果您不能在其他版本中重现它,请不要感到惊讶。请参阅更新部分中的表格。)
双方iter并reversed因此在列表专门迭代器:
>>> iter([1, 2, 3])
<list_iterator object at 0x031495C8>
>>> reversed([1, 2, 3])
<list_reverseiterator object at 0x03168310>
Run Code Online (Sandbox Code Playgroud)
但reversed一个要慢得多:
> python -m timeit -s "a = list(range(1000))" "list(iter(a))"
50000 loops, best of 5: 5.76 usec per loop
> python -m timeit -s "a = list(range(1000))" "list(reversed(a))"
20000 loops, best of 5: 14.2 usec per loop
Run Code Online (Sandbox Code Playgroud)
而且我可以始终如一地重现它。后来又试了iter五次,分别是5.98、5.84、5.85、5.87、5.86。然后又是reversed五次,分别是 14.3、14.4、14.4、14.5、14.3。
我认为iter增加列表元素的内存位置可能会带来好处,所以我事先尝试反转列表。同一张图:
> python …Run Code Online (Sandbox Code Playgroud) 我被一个感兴趣的博客文章由Mike槎他比较需要两个函数的时间numpy.zeros((N,N))和numpy.empty((N,N))为N=200和N=1000。我使用%timeit魔法在 jupyter notebook 中运行了一个小循环。下面的图表给出的所需要的时间之比numpy.zero来numpy.empty。对于N=346,numpy.zero比 慢大约 125 倍numpy.empty。在N=361及以上,这两个功能所需的时间几乎相同。
后来,在 Twitter 上的讨论导致了这样的假设:要么numpy为小分配做一些特殊的事情以避免malloc调用,要么操作系统可能会主动将分配的内存页面清零。
造成这种差异的原因是什么N,而较大的所需时间几乎相等N?
通过启动堆溢出编辑:我可以重现它(这就是为什么我来到这里的第1名),这里有一个情节np.zeros和np.empty独立。该比率看起来像 GertVdE 的原始图:
在 Python 3.9.0 64 位、NumPy 1.19.2、Windows 10 Pro 2004 64 位上完成,使用此脚本生成数据:
from timeit import repeat
import numpy as np
funcs = np.zeros, np.empty
number = 10
index = …Run Code Online (Sandbox Code Playgroud) 假设的优化使代码速度慢了一倍以上。
\n我通过查找某个值出现的范围来计算该值x在排序列表中出现的频率:a
from bisect import bisect_left, bisect_right\n\ndef count(a, x):\n start = bisect_left(a, x)\n stop = bisect_right(a, x)\n return stop - start\nRun Code Online (Sandbox Code Playgroud)\n但是,嘿,它不能在开始之前停止,因此我们可以通过省略开始之前的部分(doc)来优化第二次搜索:
\ndef count(a, x):\n start = bisect_left(a, x)\n stop = bisect_right(a, x, start)\n return stop - start\nRun Code Online (Sandbox Code Playgroud)\n但当我进行基准测试时,优化版本花费的时间是原来的两倍:
\n 254 ms \xc2\xb1 1 ms original\n 525 ms \xc2\xb1 2 ms optimized\nRun Code Online (Sandbox Code Playgroud)\n为什么?
\n该基准测试构建了一个从 0 到 99999 的一千万个随机整数的排序列表,然后对所有不同的整数进行计数(仅用于基准测试,指出没有用Counter)(在线尝试!):
import random\nfrom bisect …Run Code Online (Sandbox Code Playgroud) python ×10
performance ×7
cpython ×4
32-bit ×1
64-bit ×1
arrays ×1
list ×1
numpy ×1
python-3.11 ×1
sorting ×1