経緯・目的
numpy.exp()の計算速度が気になって少し調べているときに,np.exp much slower than np.e? という記事を見つけた.
質問と回答を読んでざっくり理解したことはこんな感じ:
-
np.exp(100)よりも'np.e**100'の方が10倍くらい速い. - これは
np.exp()が array に対応しているので,型の判定をする分のoverheadで遅くなっている可能性がある(ただしこれだけで速度差のすべてを説明できるわけではなさそう).
これは2015年の記事なので,そのあと変化があったのか気になり,同じような簡単な計測をしてみた(Python 3.11.5, numpy 1.26.1を使用).
結論
上に引用した記事と同様の結果だった:
- 引数がスカラー値(arrayではないという意味)の場合には
np.e**(x)の方が速い. - 引数がarrayの場合にはそこまで差がないか,要素数が多ければ
np.exp(x)の方がかなり速い.
*網羅的に調べたわけではないので,環境や引数の具体的な値によって結果が変わり得ると思う.
具体的なコードと結果
簡単な計測なのでターミナルで実行していく.
バージョンはPython 3.11.5,numpy 1.26.1.
はじめにtimeitをimportしておく.
>>> from timeit import timeit
引数の要素数が1の場合
引数の要素数が1である場合,intやfloatの変数をそのまま渡すとnp.e**xの方がだいぶ速いが,要素数1のndarrayを渡すと速度の差が小さくなった.
int型をそのまま渡すとnp.e**(x)の方が数倍速い.
>>> timeit("np.exp(x)", setup="import numpy as np; x = 10")
0.48411501699956716
>>> timeit("np.e**(x)", setup="import numpy as np; x = 10")
0.07219130100020266
1要素からなる1次元のndarrayを渡すと,np.exp(x)は速くなって,np.e**(x)はかなり遅くなる.
>>> timeit("np.exp(x)", setup="import numpy as np; x = np.array([10], dtype='f8')")
0.2373395429995071
>>> timeit("np.e**(x)", setup="import numpy as np; x = np.array([10], dtype='f8')")
1.2322995980002815
ちなみにx = np.array([10])とすると(dtypeの指定をやめると)多少速くなった.
しかしそれでもnp.exp(x)の方が速かったし,バグのもとだと思うので個人的にはdtypeは明示しておきたい.
引数の値を変えても傾向は変わらない.引数がarrayではない場合,むしろ差が開く.
>>> timeit("np.exp(x)", setup="import numpy as np; x = 100")
0.5663321909996739
>>> timeit("np.e**(x)", setup="import numpy as np; x = 100")
0.06345759199939494
>>> timeit("np.exp(x)", setup="import numpy as np; x = np.array([100], dtype='f8')")
0.24133333999998285
>>> timeit("np.e**(x)", setup="import numpy as np; x = np.array([100], dtype='f8')")
1.221896048000417
引数の要素数が2以上の場合
2以上といいながらnp.arange(95, 105, dtype="f8")で試した結果だけを載せる.
>>> timeit("np.exp(x)", setup="import numpy as np; x = np.arange(95, 105, dtype='f8')")
0.2651955380006257
>>> timeit("[np.e**(n) for n in x]", setup="import numpy as np; x = np.arange(95, 105, dtype='f8')")
1.3422866170003545
ちなみにこのケースではdtypeを指定した方が速かった(指定しないと2 - 5倍程度時間がかかった).
教訓
- 要素数1の変数しか扱わないことが確実な場面では
float型の変数を使ってnp.e**xとすると速い.-
ndarrayから取り出したスカラー値はnp.float64などになっていて,これだとnp.e**xはそこまで速くない.トリッキー.
-
- 要素数1でも
ndarrayを渡すならnp.exp(x)とした方がよい.またnp.exp(x)を使うなら引数はndarrayにしておいた方が速い.- コードの他の部分の都合で1要素から成る
ndarrayを扱うことがあるので,この点は(自分にとっては)意外と大事. - 例えば
x = np.arange(5, dtype="f8")[2:3]のようにして要素数1のndarrayを扱うときがある.size属性とか使いたい場合.
- コードの他の部分の都合で1要素から成る
- 複数の値に対する結果がほしい場合は
ndarrayのxを引数としてnp.exp(x)とするのがよい.-
xを生成するときはdtypeも指定しておくとより速そう.
-