経緯・目的
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
も指定しておくとより速そう.
-