web-dev-qa-db-ja.com

スピード比較。 numpy vs python標準

いくつかの実験を行ったところ、Pythonの標準のrandomおよびmathライブラリがnumpyのライブラリよりも高速であるケースがいくつか見つかりました。

小規模な操作ではpythonの標準ライブラリが約10倍高速になる傾向があると思いますが、大規模な(ベクトル)操作ではnumpyがはるかに高速です。私の推測では、numpyにはいくつかのオーバーヘッドがあり、それが小さなケースでは支配的になると思います。

私の質問は:私の直感は正しいですか?そして一般的に、小さな(通常はスカラー)演算にはnumpyではなく標準ライブラリを使用することをお勧めしますか?

以下に例を示します。

import math
import random
import numpy as np

対数と指数

%timeit math.log(10)
# 158 ns ± 6.16 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

%timeit np.log(10)
# 1.64 µs ± 93.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit math.exp(3)
# 146 ns ± 8.57 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

%timeit np.exp(3)
# 1.72 µs ± 78.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

正規分布を生成

%timeit random.gauss(0, 1)
# 809 ns ± 12.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit np.random.normal()
# 2.57 µs ± 14.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

ランダムな要素を選択する

%timeit random.choices([1,2,3], k=1)
# 1.56 µs ± 55.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit np.random.choice([1,2,3], size=1)
# 23.1 µs ± 1.04 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

numpy配列と同じ

arr = np.array([1,2,3])

%timeit random.choices(arr, k=1)
# 1.72 µs ± 33.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit np.random.choice(arr, size=1)
# 18.4 µs ± 502 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

大きな配列あり

arr = np.arange(10000)

%timeit random.choices(arr, k=1000)
# 401 µs ± 6.16 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit np.random.choice(arr, size=1000)
# 41.7 µs ± 1.39 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
16
Kota Mori

numpyは、実際には大きなデータブロックのパフォーマンス向上にすぎません。 ndarrayをcコンパイル済みのnumpy関数に注ぐ前に、メモリブロックが正しく整列することを確認するオーバーヘッドは、配列が比較的大きくない場合、通常は時間のメリットを圧倒します。これが非常に多くのnumpy質問が基本的に「このループコードを取得して高速化するにはどうすればよいか」であり、他のほとんどすべてのタグがあなたに投げかけるこのタグで有効な質問と見なされる理由です- コードレビュー 彼らがタイトルを通過する前に。

だから、はい、あなたの観察は一般化可能です。ベクトル化はnumpyの要点です。ベクトル化されていないnumpyコードは、常にpythonコードよりも低速であり、おそらく、手持ち削岩機で1つのクルミを割るのと同じくらい「間違っている」でしょう。適切なツールを見つけるか、より多くのナットを取得します。

8
Daniel F

NumPyは、主にarraysでのパフォーマンスに使用されます。これは、連続したメモリブロックの使用と、より効率的な低レベルの反復に依存しています。 NumPy数学関数をスカラーに適用したり、乱数を計算したりすることは、ベクトル化可能な操作ではありません。これは、表示されている動作を説明しています。

参照 通常のNumPyの利点は何ですかPythonリスト?

そして、小さな(通常はスカラー)操作には、NumPyではなく標準ライブラリを使用することが一般に推奨されますか?

プログラムのボトルネックがスカラーの操作によって引き起こされることはまれです。実際には、違いはごくわずかです。したがって、どちらの方法でも問題ありません。すでにNumPyを使用している場合は、スカラーでNumPy操作を継続して使用しても害はありません。

乱数を計算する特別なケースを作成する価値があります。ご想像のとおり、randomとNumPyで選択した乱数は同じではない可能性があります。

assert random.gauss(0, 1) == np.random.normal()  # AssertionError
assert random.choices(arr, k=1)[0] == np.random.choice(arr, size=1)[0]  # AssertionError

NumPyには、乱数を「予測可能」にする追加機能があります。たとえば、以下のスクリプトを繰り返し実行しても、同じ結果が生成されるだけです。

np.random.seed(0)
np.random.normal()

同じことがnp.random.choiceにも当てはまります。したがって、乱数の導出方法の方法と使用可能な機能に違いがあります。テストなどの目的で、一貫した「乱数」を生成できるようにしたい場合があります。

4
jpp