Python は関数を呼ぶコストがハンパない
テキストファイルを1バイトずつ走査するテストコードを書いてみた。
テストするテキストファイルのサイズは 6,811,956 バイト。
まずは元のソース。
def main(): buffer = '' filei = open('sample.txt', 'r') while True: read_buffer = filei.read(0x2000) if read_buffer == '': break index = 0 length = len(read_buffer) while index < length: buffer += read_buffer[index] index += 1 filei.close() fileo = open('out.txt', 'w') fileo.write(buffer) fileo.close() if __name__ == '__main__': main()
profile した結果、
1622 function calls in 5.103 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 2 0.000 0.000 0.000 0.000 :0(close) 806 0.001 0.000 0.001 0.000 :0(len) 2 0.001 0.000 0.001 0.000 :0(open) 807 0.041 0.000 0.041 0.000 :0(read) 1 0.007 0.007 0.007 0.007 :0(setprofile) 1 0.024 0.024 0.024 0.024 :0(write) 1 0.000 0.000 5.096 5.096 RLE-SA~1.PY:7(<module>) 1 5.029 5.029 5.096 5.096 RLE-SA~1.PY:7(main) 1 0.000 0.000 5.103 5.103 profile:0() 0 0.000 0.000 profile:0(profiler)
ここに空関数を足してみる。
# ↓ コレを追加 def foo(): pass def main(): buffer = '' filei = open('sample.txt', 'r') while True: read_buffer = filei.read(0x2000) if read_buffer == '': break index = 0 length = len(read_buffer) while index < length: buffer += read_buffer[index] index += 1 foo() # ← コレを追加 filei.close() fileo = open('out.txt', 'w') fileo.write(buffer) fileo.close() if __name__ == '__main__': main()
profile した結果はこちら。
6598988 function calls in 14.462 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 2 0.000 0.000 0.000 0.000 :0(close) 806 0.001 0.000 0.001 0.000 :0(len) 2 0.001 0.000 0.001 0.000 :0(open) 807 0.042 0.000 0.042 0.000 :0(read) 1 0.007 0.007 0.007 0.007 :0(setprofile) 1 0.023 0.023 0.023 0.023 :0(write) 1 10.526 10.526 14.454 14.454 RLE-SA~1.PY:10(main) 1 0.000 0.000 14.455 14.455 RLE-SA~1.PY:7(<module>) 6597366 3.861 0.000 3.861 0.000 RLE-SA~1.PY:7(foo) 1 0.000 0.000 14.462 14.462 profile:0() 0 0.000 0.000 profile:0(profiler)
5.103 seconds → 14.462 seconds って…。
プロファイルの結果によると、ざっくり考えても
- 空関数の foo の実行時間は 3.861 seconds
- 関数の呼び出しコストは 5.498 seconds
ってこと。
速度の最適化を考える場合、『ループの中の関数呼び出しを減らす』のは Python には効果てきめんのようだ。