Python 3 では x**4.0 が x**4 より速いのはなぜですか? 質問する

Python 3 では x**4.0 が x**4 より速いのはなぜですか? 質問する

x**4.0がよりも速いのはなぜですかx**4? 私は CPython 3.5.2 を使用しています。

$ python -m timeit "for x in range(100):" " x**4.0"
  10000 loops, best of 3: 24.2 usec per loop

$ python -m timeit "for x in range(100):" " x**4"
  10000 loops, best of 3: 30.6 usec per loop

どのように動作するかを見るために、累乗するべき乗数を変えてみたところ、例えばxを10または16の累乗にすると30から35に跳ね上がるが、10.0フロートとしては24.1~4あたりを動いているだけです。

おそらく浮動小数点変換と 2 の累乗に関係があると思いますが、よくわかりません。

どちらの場合も 2 の累乗の方が高速であることに気付きました。これは、これらの計算がインタープリタ/コンピュータにとってよりネイティブ/簡単だからだと思います。しかし、それでも、浮動小数点数ではほとんど動きません。2.0 => 24.1~4 & 128.0 => 24.1~4 しかし 2 => 29 & 128 => 62


タイガーホークT3 ベース

ベストアンサー1

なぜx**4.0 もっと早くx**4Python 3 と比べて* ?

Python 3intオブジェクトは、任意のサイズをサポートするように設計された本格的なオブジェクトです。そのため、Cレベルではこのように扱われる(すべての変数がPyLongObject *型として宣言されているのを見てくださいlong_pow)。これにより、指数計算もはるかに簡単になります。よりトリッキーそして退屈なob_digitそれを実行するには、値を表すために使用する配列を操作する必要があるためです。(勇敢な人のための源。- 見る:Python での大きな整数のメモリ割り当てを理解する詳細はPyLongObjects をご覧ください。

Pythonfloatオブジェクトは、逆に、変形できるCdouble型(PyFloat_AsDouble)と操作を実行できますこれらのネイティブタイプを使用するこれは素晴らしい関連するエッジケースをチェックした後、Pythonはプラットホームを使う'powCのpow、つまり) を使って実際の累乗を処理します。

/* Now iv and iw are finite, iw is nonzero, and iv is
 * positive and not equal to 1.0.  We finally allow
 * the platform pow to step in and do the rest.
 */
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw); 

ここでiv、および はC としてのiw元の です。PyFloatObjectdouble

参考までに言うと、2.7.13私にとって Python は 1 倍2~3高速で、逆の動作を示します。

前の事実また説明しているPython 2 と 3 の間の相違点について、興味深いのでこのコメントにも触れておこうと思いました。

Python 2 では、 Python 3 のオブジェクトintとは異なる古いオブジェクトを使用しています( 3.x のすべてのオブジェクトは 型です)。Python 2 では、オブジェクトの値 (または、サフィックス を使用する場合) によって区別されます。intintPyLongObjectL/l

# Python 2
type(30)  # <type 'int'>
type(30L) # <type 'long'>

ここに見<type 'int'>られる同じことfloatをする、それは安全にCに変換されますlong 指数演算を実行するとint_powまた、可能であればレジスターに格納するようにコンパイラーに指示するので、できた違いが生じる・異なる):

static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
    register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */    

これにより、速度が大幅に向上します。

<type 'long'>が と比べてどれほど遅いかを確認するために<type 'int'>、Python 2 で呼び出しx内に名前をラップすると (基本的にPython 3 のように使用するように強制すると)、速度の向上は消えます。longlong_pow

# <type 'int'>
(python2) ➜ python -m timeit "for x in range(1000):" " x**2"       
10000 loops, best of 3: 116 usec per loop
# <type 'long'> 
(python2) ➜ python -m timeit "for x in range(1000):" " long(x)**2"
100 loops, best of 3: 2.12 msec per loop

int1 つのスニペットはを に変換しますが、もう 1 つは変換しません (@pydsinger が指摘したように)、このキャストが速度低下の原因ではないことに注意してくださいlong。 の実装はlong_powです。(ステートメントを のみで実行してlong(x)確認します)。

[...] ループ外では発生しません。 [...] それについて何か考えはありますか?

これは、定数を折りたたむ CPython のピープホール オプティマイザーです。指数の結果を見つけるための実際の計算はなく、値の読み込みのみなので、どちらの場合でもまったく同じタイミングになります。

dis.dis(compile('4 ** 4', '', 'exec'))
  1           0 LOAD_CONST               2 (256)
              3 POP_TOP
              4 LOAD_CONST               1 (None)
              7 RETURN_VALUE

に対して同一のバイトコードが生成されますが'4 ** 4.'、唯一の違いは、int ではなくLOAD_CONSTfloat をロードすることです。256.0256

dis.dis(compile('4 ** 4.', '', 'exec'))
  1           0 LOAD_CONST               3 (256.0)
              2 POP_TOP
              4 LOAD_CONST               2 (None)
              6 RETURN_VALUE

つまり時間は同じです。


*上記はすべて、Python のリファレンス実装である CPython にのみ適用されます。他の実装では動作が異なる場合があります。

おすすめ記事