#1.概要
ndarrayをforするのはどの方法が速度が出るのか確認。
※ 何故かググっても出てこなかったのでとりあえずやってみた。
#2.結論
- to_listしてforが最速。
- forよりto_listがボトルネック
- to_listはやった時点でメモリコピーが走るんでその辺の考慮は必要。
- インデックスでの参照は遅い。
#3.詳細
3.1 測定内容と結果
np.zeros(50000000)に「測定項目」の方法で各値を取得し、かかった総時間を測定。
ndarrayをforすると遅いってさんざん言われてるけどインデックスが更に遅いのは知りませんでした。
測定項目 | 関数名 | 結果[sec] |
---|---|---|
インデックス | test1 | 6.716 |
普通にfor | test2 | 3.723 |
to_listしてfor | test3 | 1.601 |
3.2 使用したソースコード
import numpy as np
import io
import cProfile
import pstats
from pstats import SortKey
def test1(target):
# インデックスで取り出し
for i in range(len(target)):
target[i]
def test2(target):
# 普通にfor
for v in target:
pass
def test3(target):
# tolistで取り出し
target = target.tolist()
for n in target:
pass
def run_all_test():
target = np.zeros(50000000)
test1(target)
test2(target)
test3(target)
def profile_it(func, *args, **kwargs):
# funcをプロファイリングする
pr = cProfile.Profile()
pr.enable()
func(*args, **kwargs)
pr.disable()
s = io.StringIO()
ps = pstats.Stats(pr, stream=s).strip_dirs().sort_stats(SortKey.CUMULATIVE) # 累積時刻でソート
ps.print_stats(10) # 上位n件のみ
print(s.getvalue()) # コンソール出力
if __name__ == "__main__":
profile_it(run_all_test)
#3.3 プロファイル結果
- cumtimeが関数の実行時間です。
- to_listを使用してるtest3が高速。
- test3の1.601秒のうち、1.012秒がto_list。つまりforの時間は0.6秒程度。listのfor自体はndarrayの約6倍高速。
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.643 0.643 12.683 12.683 speed_test_ndarray_for.py:27(run_all_test)
1 6.716 6.716 6.716 6.716 speed_test_ndarray_for.py:9(test1)
1 3.723 3.723 3.723 3.723 speed_test_ndarray_for.py:14(test2)
1 0.590 0.590 1.601 1.601 speed_test_ndarray_for.py:20(test3)
1 1.012 1.012 1.012 1.012 {method 'tolist' of 'numpy.ndarray' objects}
1 0.001 0.001 0.001 0.001 {built-in method numpy.zeros}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 {built-in method builtins.len}
4. 測定環境
- python: 3.7.4
- numpy: 1.17.4