Python 2:为什么楼层除法运算符比普通除法运算符快?
考虑下面的Python2代码Python 2:为什么楼层除法运算符比普通除法运算符快?,python,python-2.7,Python,Python 2.7,考虑下面的Python2代码 from timeit import default_timer def floor(): for _ in xrange(10**7): 1 * 12 // 39 * 2 // 39 * 23 - 234 def normal(): for _ in xrange(10**7): 1 * 12 / 39 * 2 / 39 * 23 - 234 t1 = default_timer() floor() t2 =
from timeit import default_timer
def floor():
for _ in xrange(10**7):
1 * 12 // 39 * 2 // 39 * 23 - 234
def normal():
for _ in xrange(10**7):
1 * 12 / 39 * 2 / 39 * 23 - 234
t1 = default_timer()
floor()
t2 = default_timer()
normal()
t3 = default_timer()
print 'Floor %.3f' % (t2 - t1)
print 'Normal %.3f' % (t3 - t2)
我电脑上的输出是
Floor 0.254
Normal 1.766
那么,为什么楼层除法运算符
/
比普通除法运算符/
做同一件事时要快?您可以使用以下命令检查特定python函数的编译字节码:
Python解释器在
floor
中预先计算循环内的表达式,但在normal
中没有
下面是楼层的代码:
>>> dis.dis(floor)
5 0 SETUP_LOOP 24 (to 27)
3 LOAD_GLOBAL 0 (xrange)
6 LOAD_CONST 9 (10000000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 10 (to 26)
16 STORE_FAST 0 (_)
6 19 LOAD_CONST 15 (-234)
22 POP_TOP
23 JUMP_ABSOLUTE 13
>> 26 POP_BLOCK
>> 27 LOAD_CONST 0 (None)
30 RETURN_VALUE
您可以看到表达式已计算LOAD\u CONST 15(-234)
对于normal
,这是相同的:
>>> dis.dis(normal)
9 0 SETUP_LOOP 44 (to 47)
3 LOAD_GLOBAL 0 (xrange)
6 LOAD_CONST 9 (10000000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 30 (to 46)
16 STORE_FAST 0 (_)
10 19 LOAD_CONST 10 (12)
22 LOAD_CONST 5 (39)
25 BINARY_DIVIDE
26 LOAD_CONST 6 (2)
29 BINARY_MULTIPLY
30 LOAD_CONST 5 (39)
33 BINARY_DIVIDE
34 LOAD_CONST 7 (23)
37 BINARY_MULTIPLY
38 LOAD_CONST 8 (234)
41 BINARY_SUBTRACT
42 POP_TOP
43 JUMP_ABSOLUTE 13
>> 46 POP_BLOCK
>> 47 LOAD_CONST 0 (None)
50 RETURN_VALUE
这一次,计算只是部分简化(例如:省略了初始的1*
),并且大多数操作都是在运行时执行的
看起来Python2.7没有执行包含不明确的/
运算符的常量折叠(这可能是整数或浮点除法,具体取决于其操作数)。在程序顶部添加来自uuu future uuuu import division的,会导致常量在normal
中折叠,就像在floor
中一样(尽管结果当然不同,因为现在的//code>是浮点除法)
这并不是说解释器不能用默认的/
操作符进行常量折叠,但是它不能。也许代码是从Python3中移植回来的,并且认为使用不明确的除法运算符并不重要。“产生相同的结果”并不意味着“以相同的方式实现”。
还要注意的是,这些运算符并不总是产生与此处所述相同的结果:
因此,性能度量在很大程度上取决于实现。
通常,硬件浮点除法比整数除法耗时更长。
可能是python经典除法(您称之为普通除法)是通过硬件浮点除法实现的,只有在最后阶段才被截断回整数,而真正的除法(您称之为floored)是通过硬件int除法实现的,这要快得多。Yes,但是为什么会这样呢?@avamsi我进一步调查了一下,并补充了一些细节。
>>> dis.dis(normal)
9 0 SETUP_LOOP 44 (to 47)
3 LOAD_GLOBAL 0 (xrange)
6 LOAD_CONST 9 (10000000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 30 (to 46)
16 STORE_FAST 0 (_)
10 19 LOAD_CONST 10 (12)
22 LOAD_CONST 5 (39)
25 BINARY_DIVIDE
26 LOAD_CONST 6 (2)
29 BINARY_MULTIPLY
30 LOAD_CONST 5 (39)
33 BINARY_DIVIDE
34 LOAD_CONST 7 (23)
37 BINARY_MULTIPLY
38 LOAD_CONST 8 (234)
41 BINARY_SUBTRACT
42 POP_TOP
43 JUMP_ABSOLUTE 13
>> 46 POP_BLOCK
>> 47 LOAD_CONST 0 (None)
50 RETURN_VALUE
normal
10 0 SETUP_LOOP 24 (to 27)
3 LOAD_GLOBAL 0 (xrange)
6 LOAD_CONST 9 (10000000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 10 (to 26)
16 STORE_FAST 0 (_)
11 19 LOAD_CONST 15 (-233.6370808678501)
22 POP_TOP
23 JUMP_ABSOLUTE 13
>> 26 POP_BLOCK
>> 27 LOAD_CONST 0 (None)
30 RETURN_VALUE