1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

np.exp(x) よりも np.e**x の方が速いことがあるらしい

Last updated at Posted at 2023-10-20

経緯・目的

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である場合,intfloatの変数をそのまま渡すと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属性とか使いたい場合.
  • 複数の値に対する結果がほしい場合はndarrayxを引数としてnp.exp(x)とするのがよい.
    • xを生成するときはdtypeも指定しておくとより速そう.
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?