1

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

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

现在,我知道至少有两种(非冗余)方法可以使这种除法正常,浮点除法(不运行 a 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

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

4

2 回答 2

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

从上面可以看出,简单的 using比 usinga*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.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: afloat(a)CALL_FUNCTIONBINARY_DIVIDE

浮动分配

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

浮动分配 dis 结果

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      

提速的原因

您可以看到,在执行除法的那一行,它不再需要调用 float 函数,允许立即执行除法。它只是调用LOAD_GLOBAL: float和调用CALL_FUNCTION一次,这是在分配中,而不是在循环中。这意味着它可以直接跳到BINARY_DIVIDE通话中。

用于此基准的统计数据:

Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)] on win32
于 2012-12-09T17:38:51.787 回答
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

所以第一种方法显得稍微快一些。

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

于 2012-12-09T17:38:26.560 回答