在Python 2. * division中有哪些开销?

时间:2012-12-09 17:31:40

标签: performance floating-point python-2.x integer-division

假设我想在Python 2中划分两个变量。*(主要是6和7),它们被认为是整数。例如:

a, b = 3, 2
print a/b
# Prints "1"

现在,我知道至少有两种(非冗余的)方法可以使这种除法成为正常的浮点除法(不运行from __future__ import division)。他们是:

print a*1.0/b      # Of course you could multiply b by 1.0 also 

print float(a)/b   # Here you could also have cast b as a float

这些方法中的一种是否具有优势(速度)优于另一种?一个人有比另一个更多的开销吗?

2 个答案:

答案 0 :(得分:2)

>>> timeit.timeit(stmt="a*1.0/b",setup="a,b=3,2",number=100)
4.669614510532938e-05
>>> timeit.timeit(stmt="float(a)/b",setup="a,b=3,2",number=100)
7.18402232422477e-05

从上面可以看出,简单地使用a*1.0/b比使用float(a)快得多。这是因为Python中的调用函数非常昂贵。尽管如此,你可以这样做:

a,b=float(3),2
print a/b

你将获得基准:

>>> timeit.timeit(stmt="a/b",setup="a,b=float(3),2",number=100)
2.5144078108496615e-05

这是因为您只需拨打float()一次,即a。这反过来不需要考虑1.0*a,从而提供更快的结果。

使用dis模块进一步打破此问题,您可以在循环中看到对此的实际调用:

分裂时

浮动

def floatmethod():
    a,b=3,2
    while True:
        print float(a)/b
分区结果

浮动

dis.dis(floatmethod)
  2           0 LOAD_CONST               3 ((3, 2))
              3 UNPACK_SEQUENCE          2
              6 STORE_FAST               0 (a)
              9 STORE_FAST               1 (b)

  3          12 SETUP_LOOP              25 (to 40)
        >>   15 LOAD_GLOBAL              0 (True)
             18 POP_JUMP_IF_FALSE       39

  4          21 LOAD_GLOBAL              1 (float)
             24 LOAD_FAST                0 (a)
             27 CALL_FUNCTION            1
             30 LOAD_FAST                1 (b)
             33 BINARY_DIVIDE       
             34 PRINT_ITEM          
             35 PRINT_NEWLINE       
             36 JUMP_ABSOLUTE           15
        >>   39 POP_BLOCK           
        >>   40 LOAD_CONST               0 (None)
             43 RETURN_VALUE        

速度减少的原因

此方法速度慢的原因是它必须先LOAD_GLOBAL: float,然后获取aLOAD_FAST: a)的值,然后调用float(a)({{ 1}})。然后它最终执行除法(CALL_FUNCTION),所有这些都在循环期间一遍又一遍地完成。

浮动作业

BINARY_DIVIDE

浮动分配结果

def initfloatmethod():
    a,b=float(3),2
    while True:
        print a/b

速度增加的原因

你可以看到,在执行除法的行上,它不再需要调用浮点函数,允许立即执行除法。它只需调用dis.dis(initfloatmethod) 2 0 LOAD_GLOBAL 0 (float) 3 LOAD_CONST 1 (3) 6 CALL_FUNCTION 1 9 LOAD_CONST 2 (2) 12 ROT_TWO 13 STORE_FAST 0 (a) 16 STORE_FAST 1 (b) 3 19 SETUP_LOOP 19 (to 41) >> 22 LOAD_GLOBAL 1 (True) 25 POP_JUMP_IF_FALSE 40 4 28 LOAD_FAST 0 (a) 31 LOAD_FAST 1 (b) 34 BINARY_DIVIDE 35 PRINT_ITEM 36 PRINT_NEWLINE 37 JUMP_ABSOLUTE 22 >> 40 POP_BLOCK >> 41 LOAD_CONST 0 (None) 44 RETURN_VALUE 并调用LOAD_GLOBAL: float一次,这是在分配时,而不是在循环中。这意味着它可以直接跳至CALL_FUNCTION电话。

用于此基准的统计数据:

BINARY_DIVIDE

答案 1 :(得分:1)

使用Python 2.7.3:

In [7]: %timeit a*1.0/b
10000000 loops, best of 3: 165 ns per loop

In [8]: %timeit float(a)/b
1000000 loops, best of 3: 228 ns per loop

所以第一种方法看起来要快一些。

也就是说,在开始进行微优化之前,总是值得对代码进行分析。