listb や dict の各要素に対して何らかの処理を行うような場合は、内包表記を使用した方が高速
確認用コード
# coding: utf-8 """内包表記の処理速度確認. リストの各値に+Nする処理の速度をforループと内包表記で比較する。 """ import time def normal(list1, num): stime = time.time() result = [] for i in list1: result.append(i + num) print('forループ - {}'.format(time.time() - stime)) return result def tuning(list1, num): stime = time.time() result = [i + num for i in list1] print('内包表記 - {}'.format(time.time() - stime)) return result if __name__ == '__main__': # 確認用のリスト list1 = range(10000) # forループ result1 = normal(list1, 2) # 内包表記 result2 = tuning(list1, 2) # 一応結果が同じか確認 print(result1 == result2)
結果確認
forループ - 0.001007080078125 内包表記 - 0.0006196498870849609 True
ループや関数からグルーバル変数へのアクセスは控える。
どうしても必要な場合は、ループの外でいったんローカル変数に代入して使用する。
他言語(Java等)より関数呼び出しのオーバーヘッドが大きい為、ループの中で繰り返し呼ばれるような関数はべた書きした方が速く動作する。
可読性向上の為だけに切り出したような関数はべた書きにしてしまった方が速い。
※処理にかかる時間は べた書き < 自モジュールで定義した関数の呼出 < 他モジュールで定義した関数の呼出
mymodule.py
def func1(num): """他モジュールで定義した関数.""" return num * 2
call_func_cost.py
# coding: utf-8 """関数の呼び出しコストを計測する.""" import time import mymodule def func1(num): """自モジュール内に定義した関数.""" return num * 2 if __name__ == '__main__': loop = 1000000 # 他モジュール内の関数呼び出し stime = time.time() counter = 0 for i in range(loop): counter += mymodule.func1(i) print(time.time() - stime) # 自モジュール内の関数呼び出し stime = time.time() counter = 0 for i in range(loop): counter += func1(i) print(time.time() - stime) # べた書き stime = time.time() counter = 0 for i in range(loop): counter += (i * 2) print(time.time() - stime)
確認結果
python3 call_func_cost.py 0.17288494110107422 0.1537008285522461 0.09762096405029297
リストやデータフレームに対する&color(red){数値計算}を繰り返し、大量に行なうようなケースでは Numba を使用すれば劇的に改善する場合がある。
ただ、数値計算以外の処理は殆どパフォーマンスは変わらない。(処理によっては遅くなるケースも多々ある)
組み込み関数でも遅いものがあるので、別の方法で実現できないか検討する。
※ Pythonのstrptimeが遅い を参照。
GIL問題により、マルチスレッドにした場合にかえって遅くなる場合がある。
マルチプロセスにして1プロセス:1スレッドにする方がだいたいのケースでうまくいく。
※コア数やメモリ量によって起動するプロセス数は考慮する事。