Python 为什么or语句比in语句快?
在我的项目中,我必须检查一个值是否是两个值之一。由于我可以使用if or语句或if in语句来实现这一点,而且我不知道这两个语句中哪一个运行得更快,因此我运行了以下代码来检查它们各自的性能:Python 为什么or语句比in语句快?,python,performance,if-statement,Python,Performance,If Statement,在我的项目中,我必须检查一个值是否是两个值之一。由于我可以使用if or语句或if in语句来实现这一点,而且我不知道这两个语句中哪一个运行得更快,因此我运行了以下代码来检查它们各自的性能: import time import datetime from scipy.stats import ttest_ind def if_in(t): bln = False for z in range(400): if z % 100 == 0: print("tes
import time
import datetime
from scipy.stats import ttest_ind
def if_in(t):
bln = False
for z in range(400):
if z % 100 == 0: print("test1", z)
starttime = time.time()
for x in range(1000000):
for i in range(5):
if i in [2, 4]:
bln = True
t.append(time.time() - starttime)
return t
def if_or(t):
bln = False
for z in range(400):
if z % 100 == 0: print("test2", z)
starttime = time.time()
for x in range(1000000):
for i in range(5):
if i == 2 or i == 4:
bln = True
t.append(time.time() - starttime)
return t
st = time.time()
times1 = if_in([])
times2 = if_or([])
t, p = ttest_ind(times1, times2)
print("\nTotal execution time:", str(datetime.timedelta(seconds=time.time() - st)))
t1mean = sum(times1) / len(times1)
t2mean = sum(times2) / len(times2)
print("Test1 mean:", t1mean, "\nTest2 mean:", t2mean)
print("\nT-test p-score:", p)
其中印刷:
测试1平均值:0.47915725761612455
测试2平均值:0.46851890563964843
T检验p得分:0.001033983121482868
p值表示in和or语句循环的执行时间之间的差异很大
为什么会有这种差异?对于“or”方法,我假设当第一个条件被认为是真的时,进一步的检查将停止。我再次假设,“in”方法也是如此。但是,其中一个确实比另一个跑得快
此外,这会持续更多的情况吗?例如,何时应将我检查为100个值之一?您的观察结果存在多个问题 让我们暂时忘记谁是赢家 第一个最重要的问题是,您观察到一些统计上显著的偏离值的平均值,并将其概括为某部分代码执行速度的反映。 虽然这对于特定的代码运行可能是正确的,但是对于一般的方法来说,没有什么可说的,因为在这个级别上,您的度量主要是由操作系统驱动的波动。 我很有信心我自己也观察到了这一点,这段代码的多次运行将为每次运行带来不同的赢家 第二个问题是,您使用的是不适合基准测试的。您可能应该使用,即使这样,它也可能不适合测量如此短的计时 第三个问题是,您的数据不支持您的结论,因为tmean2与if_相关,或者实际上小于tmean1与if_in相关 请注意,这是非常具有挑战性的,可能与实际测量您建议的两个选项之间哪一个更快无关 相反,研究第二个问题是有趣的,即对于模式x==y0或x==y1等的较大重复,在容器中使用是否更快 让我们研究一下,使用IPython%timeit magic对不同短路量的计时:
def if_or(n, ks, timer=time.perf_counter):
for _ in range(n):
for k in ks:
if k == 0 or k == 1 or k == 2 or k == 3 or k == 4 \
or k == 5 or k == 6 or k == 7 or k == 8 or k == 9:
pass
def if_in_set(n, ks, timer=time.perf_counter):
for _ in range(n):
for k in ks:
if k in {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}:
pass
def if_in_tuple(n, ks, timer=time.perf_counter):
for _ in range(n):
for k in ks:
if k in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9):
pass
def if_in_list(n, ks, timer=time.perf_counter):
for _ in range(n):
for k in ks:
if k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:
pass
n = 100000
m = 20
ks = [0] * m
%timeit if_or(n, ks)
# 10 loop, best of 3: 59 ms per loop
%timeit if_in_set(n, ks)
# 10 loop, best of 3: 57.6 ms per loop
%timeit if_in_tuple(n, ks)
# 10 loop, best of 3: 52.4 ms per loop
%timeit if_in_list(n, ks)
# 10 loop, best of 3: 54.7 ms per loop
ks = list(range(m))
%timeit if_or(n, ks)
# 1 loop, best of 3: 351 ms per loop
%timeit if_in_set(n, ks)
# 10 loop, best of 3: 57.6 ms per loop
%timeit if_in_tuple(n, ks)
# 1 loop, best of 3: 209 ms per loop
%timeit if_in_list(n, ks)
# 1 loop, best of 3: 214 ms per loop
ks = [-1] * m
%timeit if_or(n, ks)
# 1 loop, best of 3: 421 ms per loop
%timeit if_in_set(n, ks)
# 10 loop, best of 3: 54.4 ms per loop
%timeit if_in_tuple(n, ks)
# 1 loop, best of 3: 238 ms per loop
%timeit if_in_list(n, ks)
# 1 loop, best of 3: 237 ms per loop
正如您所看到的,在足够短的时间内,or解决方案的速度与在任何给定容器上的速度一样快,但一般来说,使用集合是一个更好的选择,因为它证明了自己的速度很快,因为它有O1查找时间,而元组或列表的查找时间不长,并且与短路赌注无关
最后,为了了解速度较慢的原因或原因,让我们使用dis分解if_或if_in_set:
如果
如果_在_集合中
在这里,您可以看到第三个冗长的if_或带有多个相对昂贵的COMPARE_OP调用的块被一个COMPARE_OP调用所取代。
Python的优化机制正在冻结容器。您的观察结果存在多个问题 让我们暂时忘记谁是赢家 第一个最重要的问题是,您观察到一些统计上显著的偏离值的平均值,并将其概括为某部分代码执行速度的反映。 虽然这对于特定的代码运行可能是正确的,但是对于一般的方法来说,没有什么可说的,因为在这个级别上,您的度量主要是由操作系统驱动的波动。 我很有信心我自己也观察到了这一点,这段代码的多次运行将为每次运行带来不同的赢家 第二个问题是,您使用的是不适合基准测试的。您可能应该使用,即使这样,它也可能不适合测量如此短的计时 第三个问题是,您的数据不支持您的结论,因为tmean2与if_相关,或者实际上小于tmean1与if_in相关 请注意,这是非常具有挑战性的,可能与实际测量您建议的两个选项之间哪一个更快无关 相反,研究第二个问题是有趣的,即对于模式x==y0或x==y1等的较大重复,在容器中使用是否更快 让我们研究一下,使用IPython%timeit magic对不同短路量的计时:
def if_or(n, ks, timer=time.perf_counter):
for _ in range(n):
for k in ks:
if k == 0 or k == 1 or k == 2 or k == 3 or k == 4 \
or k == 5 or k == 6 or k == 7 or k == 8 or k == 9:
pass
def if_in_set(n, ks, timer=time.perf_counter):
for _ in range(n):
for k in ks:
if k in {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}:
pass
def if_in_tuple(n, ks, timer=time.perf_counter):
for _ in range(n):
for k in ks:
if k in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9):
pass
def if_in_list(n, ks, timer=time.perf_counter):
for _ in range(n):
for k in ks:
if k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:
pass
n = 100000
m = 20
ks = [0] * m
%timeit if_or(n, ks)
# 10 loop, best of 3: 59 ms per loop
%timeit if_in_set(n, ks)
# 10 loop, best of 3: 57.6 ms per loop
%timeit if_in_tuple(n, ks)
# 10 loop, best of 3: 52.4 ms per loop
%timeit if_in_list(n, ks)
# 10 loop, best of 3: 54.7 ms per loop
ks = list(range(m))
%timeit if_or(n, ks)
# 1 loop, best of 3: 351 ms per loop
%timeit if_in_set(n, ks)
# 10 loop, best of 3: 57.6 ms per loop
%timeit if_in_tuple(n, ks)
# 1 loop, best of 3: 209 ms per loop
%timeit if_in_list(n, ks)
# 1 loop, best of 3: 214 ms per loop
ks = [-1] * m
%timeit if_or(n, ks)
# 1 loop, best of 3: 421 ms per loop
%timeit if_in_set(n, ks)
# 10 loop, best of 3: 54.4 ms per loop
%timeit if_in_tuple(n, ks)
# 1 loop, best of 3: 238 ms per loop
%timeit if_in_list(n, ks)
# 1 loop, best of 3: 237 ms per loop
正如您所看到的,在足够短的时间内,or解决方案的速度与在任何给定容器上的速度一样快,但一般来说,使用集合是一个更好的选择,因为它证明了自己的速度很快,因为它有O1查找时间,而元组或列表的查找时间不长,并且与短路赌注无关
最后,为了了解速度较慢的原因或原因,让我们使用dis分解if_或if_in_set:
如果
如果_在_集合中
在那里,你可以看到第三块较长的if_或具有多个相对昂贵的
e COMPARE_OP调用将替换为单个COMPARE_OP调用。
容器被Python的优化机制冻结。如果_in有400*1000000次构建列表对象的开销,而其他函数没有。我几乎不会调用468 ms比479 ms快得多。同样,这表明if_或比if_in快。当事实上t2mean小于t1mean时,为什么说in语句的执行时间明显少于替代语句?请执行两次相同函数的测试。第二次运行会更快一些,因为已经完成了一些背景魔术初始化。你可以考虑使用一个更好的表示RIN——这样一个小列表-列表是可以的,对于一个更大的列表,估计10个项目中的一个集合对于检查正确性来说要快得多:如果我在FROZSESET { 2, 4 }中:-并将集合的声明放在loopif_in之外,它有400*1000000次构建列表对象的开销,这是另一个函数所没有的。我几乎不会调用468 ms比479 ms快得多。另外,这表明if_或比if_in快。当事实上t2mean小于t1mean时,为什么说in语句的执行时间明显少于替代语句?请执行两次相同函数的测试。第二次运行会更快一些,因为已经完成了一些背景魔术初始化。你可以考虑使用一个更好的表示RIN——这样一个小列表-列表是可以的,对于一个更大的列表,估计10个项目中的一个集合对于检查正确性来说要快得多:如果我在FROZSESET { 2, 4 }中:-并将集合的声明置于循环之外这是一个很好的答案:这两种方法都是对我的方法的关键分解,然后是对这两种方法的深入探索。我不能要求更多!这是一个很好的答案:这两种方法都对我的方法进行了关键性的分解,然后对这两种方法进行了深入的探讨。我不能要求更多!
2 0 SETUP_LOOP 110 (to 112)
2 LOAD_GLOBAL 0 (range)
4 LOAD_FAST 0 (n)
6 CALL_FUNCTION 1
8 GET_ITER
>> 10 FOR_ITER 98 (to 110)
12 STORE_FAST 3 (_)
3 14 SETUP_LOOP 92 (to 108)
16 LOAD_FAST 1 (ks)
18 GET_ITER
>> 20 FOR_ITER 84 (to 106)
22 STORE_FAST 4 (k)
4 24 LOAD_FAST 4 (k)
26 LOAD_CONST 1 (0)
28 COMPARE_OP 2 (==)
30 POP_JUMP_IF_TRUE 20
32 LOAD_FAST 4 (k)
34 LOAD_CONST 2 (1)
36 COMPARE_OP 2 (==)
38 POP_JUMP_IF_TRUE 20
40 LOAD_FAST 4 (k)
42 LOAD_CONST 3 (2)
44 COMPARE_OP 2 (==)
46 POP_JUMP_IF_TRUE 20
48 LOAD_FAST 4 (k)
50 LOAD_CONST 4 (3)
52 COMPARE_OP 2 (==)
54 POP_JUMP_IF_TRUE 20
56 LOAD_FAST 4 (k)
58 LOAD_CONST 5 (4)
60 COMPARE_OP 2 (==)
62 POP_JUMP_IF_TRUE 20
64 LOAD_FAST 4 (k)
66 LOAD_CONST 6 (5)
68 COMPARE_OP 2 (==)
70 POP_JUMP_IF_TRUE 20
72 LOAD_FAST 4 (k)
74 LOAD_CONST 7 (6)
76 COMPARE_OP 2 (==)
78 POP_JUMP_IF_TRUE 20
80 LOAD_FAST 4 (k)
82 LOAD_CONST 8 (7)
84 COMPARE_OP 2 (==)
86 POP_JUMP_IF_TRUE 20
88 LOAD_FAST 4 (k)
90 LOAD_CONST 9 (8)
92 COMPARE_OP 2 (==)
94 POP_JUMP_IF_TRUE 20
96 LOAD_FAST 4 (k)
98 LOAD_CONST 10 (9)
100 COMPARE_OP 2 (==)
102 POP_JUMP_IF_FALSE 20
5 104 JUMP_ABSOLUTE 20
>> 106 POP_BLOCK
>> 108 JUMP_ABSOLUTE 10
>> 110 POP_BLOCK
>> 112 LOAD_CONST 0 (None)
114 RETURN_VALUE
import dis
dis.dis(if_in_set)
9 0 SETUP_LOOP 38 (to 40)
2 LOAD_GLOBAL 0 (range)
4 LOAD_FAST 0 (n)
6 CALL_FUNCTION 1
8 GET_ITER
>> 10 FOR_ITER 26 (to 38)
12 STORE_FAST 3 (_)
10 14 SETUP_LOOP 20 (to 36)
16 LOAD_FAST 1 (ks)
18 GET_ITER
>> 20 FOR_ITER 12 (to 34)
22 STORE_FAST 4 (k)
11 24 LOAD_FAST 4 (k)
26 LOAD_CONST 11 (frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}))
28 COMPARE_OP 6 (in)
30 POP_JUMP_IF_FALSE 20
12 32 JUMP_ABSOLUTE 20
>> 34 POP_BLOCK
>> 36 JUMP_ABSOLUTE 10
>> 38 POP_BLOCK
>> 40 LOAD_CONST 0 (None)
42 RETURN_VALUE