python
1def crop_01(img): 2 cropped = img[0:100, 0:100] 3 return np.count_nonzero(cropped) 4 5def crop_02(img): 6 return np.count_nonzero(img[0:100, 0:100])
例えば画像を切り出してnp.count_nonzero()関数に突っ込むものがあったとします。
01の方が一時変数を利用していますが、02の方はダイレクトに引数に突っ込んでいます。
10000000回繰り返して01と02で速度に差は出るのでしょうか?
当方で速度差を計測しましたが実行順番によって偏りが出てしまい、正確な計測ができませんでした。
何か計測方法等のアドバイスや理論的な面からの議論があれば教えて欲しいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
ベストアンサー
バイトコードディスアセンブラで見てみます。
python
1>>> def crop_01(img): 2... cropped = img[0:100, 0:100] 3... return np.count_nonzero(cropped) 4... 5>>> def crop_02(img): 6... return np.count_nonzero(img[0:100, 0:100]) 7... 8>>> import dis 9>>> dis.dis(crop_01) 10 2 0 LOAD_FAST 0 (img) 11 3 LOAD_CONST 1 (0) 12 6 LOAD_CONST 2 (100) 13 9 BUILD_SLICE 2 14 12 LOAD_CONST 1 (0) 15 15 LOAD_CONST 2 (100) 16 18 BUILD_SLICE 2 17 21 BUILD_TUPLE 2 18 24 BINARY_SUBSCR 19 25 STORE_FAST 1 (cropped) 20 21 3 28 LOAD_GLOBAL 0 (np) 22 31 LOAD_ATTR 1 (count_nonzero) 23 34 LOAD_FAST 1 (cropped) 24 37 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 25 40 RETURN_VALUE 26>>> dis.dis(crop_02) 27 2 0 LOAD_GLOBAL 0 (np) 28 3 LOAD_ATTR 1 (count_nonzero) 29 6 LOAD_FAST 0 (img) 30 9 LOAD_CONST 1 (0) 31 12 LOAD_CONST 2 (100) 32 15 BUILD_SLICE 2 33 18 LOAD_CONST 1 (0) 34 21 LOAD_CONST 2 (100) 35 24 BUILD_SLICE 2 36 27 BUILD_TUPLE 2 37 30 BINARY_SUBSCR 38 31 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 39 34 RETURN_VALUE
32.12. dis — Python バイトコードの逆アセンブラ — Python 3.6.5 ドキュメント
違うといえば違うし、crop_02
の方が命令数が少ないので恐らく速いのだと思いますが……
単純な代入はたぶんpythonで一番速い処理なので、気にする必要はないです。スライスとnp.count_nonzeroが相対的に遅いので埋もれるということです。
追記
一応代入の有無による違いを確認しておきます。
passするだけの関数と、ローカル変数に代入して参照するだけの関数を動かします。
python
1import timeit 2 3import numpy as np 4from scipy import stats 5 6def f1(): 7 pass 8 9def f2(): 10 x = None 11 x 12 13a = [] 14b = [] 15for i in range(20): 16 a.append(timeit.timeit(f1)) 17 b.append(timeit.timeit(f2)) 18 19print(np.mean(a), np.mean(b)) 20print(stats.ttest_ind(a, b, equal_var = False)) 21 22""" 無駄に検定=> 230.08295731854450424 0.09186851899430622 24Ttest_indResult(statistic=-2.9737127732440825, pvalue=0.005209074456296565) 25"""
検定すれば確かに有意差はあるんですが、timeit.timeitはデフォルトで10^6回回して所要時間を測ります。
ということは、一回の代入&参照によって生じている差はわずか約9*10^-9[sec]。たった10ナノ秒弱の違いです。
やっぱり、気にする必要はないです。
投稿2018/10/09 08:13
編集2018/10/09 08:46総合スコア30933
0
IPythonで%timeitを使うと簡易的なベンチマークが取れます。
Python
1In [1]: import numpy as np 2In [2]: def crop_01(img): 3 ...: cropped = img[0:100, 0:100] 4 ...: return np.count_nonzero(cropped) 5 ...: 6 ...: def crop_02(img): 7 ...: return np.count_nonzero(img[0:100, 0:100]) 8 ...: 9 10In [3]: img = np.random.random(size=(400, 600)) 11 12In [4]: %timeit crop_01(img) 1341.1 µs ± 583 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 14 15In [5]: %timeit crop_02(img) 1641 µs ± 404 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
ほとんどパフォーマンスにほとんど差が出てないですね。NumPyのスライスcropped = img[0:100, 0:100]
はメモリーへの参照を返すだけでコピーは発生しません。もちろん参照を保持する変数を確保する分だけオーバーヘッドがありますが、コピーに比べて参照を作るのは非常に高速なので今回のようなベンチマークでは差は計測できないでしょうね。
投稿2018/10/09 08:25
総合スコア3601
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/10/09 08:17
2018/10/09 08:29