1

Python 2.* (主に 6 と 7) で、整数と見なされる 2 つの変数を分割したいとします。例えば:

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

現在、この除算を通常の浮動小数点除算にする方法が少なくとも 2 つ (冗長ではない) わかっています ( 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

これらの方法の 1 つは、他の方法よりも (速度の点で) 優れていますか? 一方のオーバーヘッドが他方よりも高いですか?

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

上記から、単純に を使用する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()1 回だけであり、それが の割り当てにあるためですa。これにより、1.0*aを考慮する必要がなくなり、はるかに高速な結果が得られます。

モジュールを使用してこれをさらに分解するdisと、これに対する実際の呼び出しがループで表示されます。

分割時に浮きます

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

除算時のフロート dis results

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

float 割り当て 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呼び出して 1 回呼び出すだけです。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 に答える