Pythonの方が高速です:x **。5またはmath.sqrt(x)?


187

私はこれをしばらくの間考えていました。タイトルが言うように、どちらが速いのか、実際の機能、または単に半分の電力に引き上げますか?

更新

これは時期尚早の最適化の問題ではありません。これは、基礎となるコードが実際にどのように機能するかという問題です。Pythonコードが機能する方法の理論は何ですか?

これらの方法の違いを本当に知りたかったので、Guido van Rossumにメールを送りました。

私のメール:

Pythonで平方根を実行するには、少なくとも3つの方法があります。math.sqrt、 '**'演算子、およびpow(x、.5)です。これらのそれぞれの実装の違いに興味があります。効率に関してはどちらが良いですか?

彼の反応:

powと**は同等です。math.sqrtは複素数では機能せず、Cのsqrt()関数にリンクします。どちらが速いかわかりません...


81
Guidoがメールに返信するのは素晴らしいことです。
エヴァンフォスマルク2009年

3
エヴァン、私は返事をもらったのに驚きました
いいえ

11
これは悪い質問ではないと思います。たとえば、x * xはx ** 2よりも完全に10倍高速です。この状況では、読みやすさは非常に速いので、どうして高速な方法をとらないのでしょうか。
TM。

12
ケーシー、私は「時期尚早の最適化」についてあなたと一緒にいます。:)あなたの質問は私にとって時期尚早な最適化のようには見えません:バリアントのいずれかがあなたのコードを壊すリスクはありません。math.sqrt()よりもpow()を選択した場合、(実行時間の観点から)何をするかをよりよく知ることが重要です。
エリックOレビゴット2009年

8
これは時期尚早の最適化ではなく、時期尚早の悲観化を回避します(参照番号28、C ++コーディング標準、A.Alexandrescu)。math.sqrtがより最適化されたルーチンで(そのまま)、意図をより明確に表現している場合は、常にを優先する必要がありますx**.5。何を書いているかを知るのは時期尚早な最適化ではなく、より高速でコードの明確性を高める代替手段を選択しました。もしそうなら、なぜ他の選択肢を選ぶのかについても同様に議論する必要があります。
swalog 2014年

回答:


89

math.sqrt(x)はよりもはるかに高速ですx**0.5

import math
N = 1000000
%%timeit
for i in range(N):
    z=i**.5

10ループ、最高3:ループあたり156 ms

%%timeit
for i in range(N):
    z=math.sqrt(i)

10ループ、最高3:ループあたり91.1ミリ秒

Python 3.6.9(ノートブック)を使用。


codepad.orgで3回実行しましたが、3回すべてのa()はb()よりもはるかに高速でした。
Jeremy Ruten、

10
標準のtimeitモジュールはあなたの友達です。実行時間の測定に関しては、よくある落とし穴を回避できます。
エリックOレビゴット2009年

1
スクリプトの結果は次のとおりです。zoltan @ host:〜$ python2.5 p.py 0.183226秒かかった0.155829秒かかったzoltan @ host:〜$ python2.4 p.py 0.181142秒かかった0.153742秒かかったzoltan @ host:〜$ python2.6 p.py Took 0.157436秒Took 0.093905秒ターゲットシステム:Ubuntu Linux CPU:Intel(R)Core(TM)2 Duo CPU T9600 @ 2.80GHzご覧のとおり、異なる結果が得られました。これによるとあなたの答えは一般的ではありません。
zoli2k 2010

2
コードパッドは優れたサービスですが、タイミングパフォーマンスにとって恐ろしいものです。つまり、特定の瞬間にサーバーがどれだけビジーになるかを誰が知っているかということです。実行ごとに非常に異なる結果が生じる可能性があります
adamJLev 2010年

1
Linuxでのpy32、py31、py30、py27、py26、pypy、jython、py25、py24インタープリターのx **。5とsqrt(x)のパフォーマンス比較を追加しました。gist.github.com/783011
jfs

19
  • 最適化の最初のルール:しないでください
  • 2番目のルール:まだ実行しないでください

ここにいくつかのタイミングがあります(Python 2.5.2、Windows):

$ python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
1000000 loops, best of 3: 0.445 usec per loop

$ python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
1000000 loops, best of 3: 0.574 usec per loop

$ python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
1000000 loops, best of 3: 0.727 usec per loop

このテストは、それx**.5がよりわずかに速いことを示していsqrt(x)ます。

Python 3.0の場合、結果は逆になります。

$ \Python30\python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
1000000 loops, best of 3: 0.803 usec per loop

$ \Python30\python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
1000000 loops, best of 3: 0.695 usec per loop

$ \Python30\python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
1000000 loops, best of 3: 0.761 usec per loop

math.sqrt(x)x**.5別のマシンよりも常に高速です(Ubuntu、Python 2.6および3.1):

$ python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
10000000 loops, best of 3: 0.173 usec per loop
$ python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
10000000 loops, best of 3: 0.115 usec per loop
$ python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
10000000 loops, best of 3: 0.158 usec per loop
$ python3.1 -mtimeit -s"from math import sqrt; x = 123" "x**.5"
10000000 loops, best of 3: 0.194 usec per loop
$ python3.1 -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
10000000 loops, best of 3: 0.123 usec per loop
$ python3.1 -mtimeit -s"import math; x = 123" "math.sqrt(x)"
10000000 loops, best of 3: 0.157 usec per loop

10

あなたは本当にいくつの平方根を演奏していますか?Pythonで3Dグラフィックエンジンを記述しようとしていますか?そうでない場合、なぜ読みやすいコードよりも暗号化されたコードを使用するのでしょうか。時間差は、私が予測できるほぼすべてのアプリケーションで誰もが気付くよりも少ないでしょう。私は本当にあなたの質問を控えるつもりはありませんが、あなたは時期尚早な最適化で少し行き過ぎているようです。


16
私は時期尚早の最適化をしていると本当に感じていません。これは、2つの異なる方法から決定するという単純な質問の方が多く、平均して高速になります。
いいえ、

2
Kibbee:間違いなく有効な質問ですが、質問者がスタックオーバーフローでさまざまな質問を行ったときの驚きを共有します。それは間違いなく、すべての言語で尋ねられる質問の大部分です。
Eli Courtwright、2008年

2
math.sqrt(x)はx ** 0.5よりも読みやすいですか?私はどちらもかなり明らかに平方根だと思います...少なくともとにかくあなたがPythonに精通しているならば。Pythonに慣れていないという理由だけで、** "cryptic"のような標準のPython演算子を呼び出さないでください。
TM。

5
**演算子は不可解だとは思いません。私は、平方根を数学に追いつけない人たちに少し謎めく方法として、何かを指数0.5に上げると思います。
Kibbee、2009

13
Pythonで3Dエンジンを作成している場合はどうなりますか?
Chris Burt-Brown、

9

これらのマイクロベンチマークでmath.sqrtsqrt、数学名前空間でを検索するのに少し時間がかかるため、速度が遅くなります。あなたはそれを少し改善することができます

 from math import sqrt

それでも、timeitを介していくつかのバリエーションを実行すると、パフォーマンスがわずかに(4〜5%)向上します。 x**.5

興味深いことに、

 import math
 sqrt = math.sqrt

統計的有意性がほとんどない状態で、速度を1%以内の差でさらに高速化しました。


Kibbeeを繰り返しますが、これはおそらく時期尚早の最適化だと言います。


7

Python 2.6では、(float).__pow__() 関数はC pow()関数を使用し、関数はC 関数をmath.sqrt()使用しsqrt()ます。

glibcコンパイラでは、の実装pow(x,y)は非常に複雑で、さまざまな例外的なケースに最適化されています。たとえば、C pow(x,0.5)を呼び出すと、sqrt()関数が呼び出されます。

使用の速度差.**またはmath.sqrtC関数の周り使用ラッパーによって引き起こされ、速度が強くシステムで使用される最適化フラグ/ Cコンパイラに依存します。

編集:

これが私のマシンでのClaudiuのアルゴリズムの結果です。私はさまざまな結果を得ました:

zoltan@host:~$ python2.4 p.py 
Took 0.173994 seconds
Took 0.158991 seconds
zoltan@host:~$ python2.5 p.py 
Took 0.182321 seconds
Took 0.155394 seconds
zoltan@host:~$ python2.6 p.py 
Took 0.166766 seconds
Took 0.097018 seconds

4

それの価値については(ジムの答えを見てください)。私のマシンでpython 2.5を実行しています:

PS C:\> python -m timeit -n 100000 10000**.5
100000 loops, best of 3: 0.0543 usec per loop
PS C:\> python -m timeit -n 100000 -s "import math" math.sqrt(10000)
100000 loops, best of 3: 0.162 usec per loop
PS C:\> python -m timeit -n 100000 -s "from math import sqrt" sqrt(10000)
100000 loops, best of 3: 0.0541 usec per loop

4

Claudiuのコードを使用すると、私のマシンでは "from math import sqrt" x **。5でも高速ですが、psyco.full()を使用するとsqrt(x)が大幅に高速になり、少なくとも200%増加します。


3

math.sqrt(x)の可能性が高いです。これは、平方根に最適化されているためです。

ベンチマークは、あなたが探している答えを提供します。


3

Quake 3からの「高速ニュートンラフソン平方根」について誰かがコメントしました... ctypesを使用して実装しましたが、ネイティブバージョンと比較すると非常に遅いです。いくつかの最適化と代替実装を試します。

from ctypes import c_float, c_long, byref, POINTER, cast

def sqrt(num):
 xhalf = 0.5*num
 x = c_float(num)
 i = cast(byref(x), POINTER(c_long)).contents.value
 i = c_long(0x5f375a86 - (i>>1))
 x = cast(byref(i), POINTER(c_float)).contents.value

 x = x*(1.5-xhalf*x*x)
 x = x*(1.5-xhalf*x*x)
 return x * num

これは、structを使用した別の方法です。ctypesバージョンより約3.6倍高速ですが、Cの1/10の速度です。

from struct import pack, unpack

def sqrt_struct(num):
 xhalf = 0.5*num
 i = unpack('L', pack('f', 28.0))[0]
 i = 0x5f375a86 - (i>>1)
 x = unpack('f', pack('L', i))[0]

 x = x*(1.5-xhalf*x*x)
 x = x*(1.5-xhalf*x*x)
 return x * num

1

Claudiuの結果は私のものとは異なります。古いP4 2.4GhzマシンのUbuntuでPython 2.6を使用しています...結果は次のとおりです。

>>> timeit1()
Took 0.564911 seconds
>>> timeit2()
Took 0.403087 seconds
>>> timeit1()
Took 0.604713 seconds
>>> timeit2()
Took 0.387749 seconds
>>> timeit1()
Took 0.587829 seconds
>>> timeit2()
Took 0.379381 seconds

私にとってsqrtは一貫して高速です... Codepad.orgでさえ、ローカルコンテキストではsqrtが高速であることに同意しているようです(http://codepad.org/6trzcM3j)。コードパッドは現在Python 2.5を実行しているようです。おそらく、Claudiuが最初に回答したとき、彼らは2.4以前を使用していましたか?

実際、arg(i)の代わりにmath.sqrt(i)を使用しても、sqrtの方が時間は長くなります。この場合、私のマシンでtimeit2()は0.53から0.55秒かかりました。これは、timeit1の0.56-0.60の数値よりもさらに優れています。

私は、最新のPythonではmath.sqrtを使用し、somevar = math.sqrtまたはfrom math import sqrtのいずれかを使用して、確実にローカルコンテキストに移動すると言います。


1

最適化するPythonicのことは読みやすさです。このため、sqrt関数を明示的に使用するのが最善だと思います。とはいえ、とにかくパフォーマンスを調べましょう。

Python 3のClaudiuのコードを更新し、計算を最適化することも不可能にしました(優れたPythonコンパイラーが将来的に実行する可能性があるもの)。

from sys import version
from time import time
from math import sqrt, pi, e

print(version)

N = 1_000_000

def timeit1():
  z = N * e
  s = time()
  for n in range(N):
    z += (n * pi) ** .5 - z ** .5
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

def timeit2():
  z = N * e
  s = time()
  for n in range(N):
    z += sqrt(n * pi) - sqrt(z)
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

def timeit3(arg=sqrt):
  z = N * e
  s = time()
  for n in range(N):
    z += arg(n * pi) - arg(z)
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

timeit1()
timeit2()
timeit3()

結果は異なりますが、出力例は次のとおりです。

3.6.6 (default, Jul 19 2018, 14:25:17) 
[GCC 8.1.1 20180712 (Red Hat 8.1.1-5)]
Took 0.3747 seconds to calculate 3130485.5713865166
Took 0.2899 seconds to calculate 3130485.5713865166
Took 0.2635 seconds to calculate 3130485.5713865166

自分で試してみてください。


0

私が最近解決したSQRMINSUM問題は、大きなデータセットで平方根を繰り返し計算する必要があります。他の最適化を行う前の、私の履歴で最も古い2つの送信は、** 0.5をsqrt()で置き換えるだけで異なり、PyPyのランタイムを3.74秒から0.51秒に短縮します。これはClaudiuが測定した400%の大幅な改善のほぼ2倍です。


0

もちろん、リテラルを処理していて定数値が必要な場合、Pythonランタイムは、演算子で記述されていれば、コンパイル時に値を事前計算できます。この場合、各バージョンをプロファイルする必要はありません。

In [77]: dis.dis(a)                                                                                                                       
  2           0 LOAD_CONST               1 (1.4142135623730951)
              2 RETURN_VALUE

In [78]: def a(): 
    ...:     return 2 ** 0.5 
    ...:                                                                                                                                  

In [79]: import dis                                                                                                                       

In [80]: dis.dis(a)                                                                                                                       
  2           0 LOAD_CONST               1 (1.4142135623730951)
              2 RETURN_VALUE

-3

math.pyに移動して、関数 "sqrt"をプログラムにコピーすると、さらに高速になります。プログラムがmath.pyを見つけて開き、探している関数を見つけて、それをプログラムに戻すには時間がかかります。「ルックアップ」ステップを使用してもその関数が高速である場合、関数自体が非常に高速でなければなりません。おそらくあなたの時間を半分に減らすでしょう。要約すれば:

  1. math.pyに移動します
  2. 関数「sqrt」を見つける
  3. コピーして
  4. 関数をsqrtファインダーとしてプログラムに貼り付けます。
  5. 時間を計る。

1
それはうまくいきません。stackoverflow.com/q/18857355/3004881を参照してください。また、Cの関数へのリンクであるという元の質問の引用にも注意してください。また、関数のソースコードをコピーするとどう違うのfrom math import sqrtですか?
Dan Getz 2015

2つの関数を呼び出す際の違いを明確にするためだけに言ったのではありません。
PyGuy 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.