Python の関数呼び出しの速度2016年2月7日 | |
はじめにPython の関数は呼び出しの記述の仕方によって実行時間が変わるらしいので、その調査をしてみた。元ネタは Micha Gorelick, Ian Ozsvald 著 "ハイパフォーマンス Python"。 環境
※MinGW にも Python が入っているので、Anaconda のほうを使うようにパスを設定している。 コードサンプルコードは以下の通り。
import math
from math import sin
def test(n):
dx = 2*math.pi/(n - 1)
r = []
for i in range(n):
x = i*dx
f = math.sin(x)
r.append((x, f))
return r
def test2(n):
dx = 2*math.pi/(n - 1)
r = []
for i in range(n):
x = i*dx
f = sin(x)
r.append((x, f))
return r
def test3(n):
dx = 2*math.pi/(n - 1)
r = []
sin_l = math.sin
for i in range(n):
x = i*dx
f = sin_l(x)
r.append((x, f))
return r
def test4(n):
dx = 2*math.pi/(n - 1)
r = []
for i in xrange(n):
x = i*dx
f = sin(x)
r.append((x, f))
return r
それぞれ x = [0, 2π] の範囲で sin 関数を計算した結果をリストで返すものであるが、以下の違いがある。
逆アセンブル以下のようにすると、関数のバイトコードを逆アセンブルできる。 import dis dis.dis(test) sin 関数呼び出し部分のコードは、それぞれ以下の通りである。 test
10 56 LOAD_GLOBAL 0 (math)
59 LOAD_ATTR 3 (sin)
62 LOAD_FAST 4 (x)
65 CALL_FUNCTION 1
68 STORE_FAST 5 (f)
test2
20 56 LOAD_GLOBAL 3 (sin)
59 LOAD_FAST 4 (x)
62 CALL_FUNCTION 1
65 STORE_FAST 5 (f)
test3
31 65 LOAD_FAST 3 (sin_l)
68 LOAD_FAST 5 (x)
71 CALL_FUNCTION 1
74 STORE_FAST 6 (f)
test() では、math を検索してから sin を検索している。test2() では sin の検索だけである。test3 はローカル変数を参照している。 実行時間の比較以下に timeit による実行時間を示す。 $ python -m timeit -n 5 -r 5 -s "import call_func" "call_func.test(1000000)" 5 loops, best of 5: 642 msec per loop $ python -m timeit -n 5 -r 5 -s "import call_func" "call_func.test2(1000000)" 5 loops, best of 5: 558 msec per loop $ python -m timeit -n 5 -r 5 -s "import call_func" "call_func.test3(1000000)" 5 loops, best of 5: 578 msec per loop 余計な処理がない分、やはり test() より test2() の方が実行時間が短い。文献では test3() のほうが test2() より時間が短くなるようだが、今回はそんなことはなかった。 range() より xrange() のほうが実行速度が速いらしいが、結果は以下の通りで、確かに速くなっている。 $ python -m timeit -n 5 -r 5 -s "import call_func" "call_func.test4(1000000)" 5 loops, best of 5: 522 msec per loop 参考文献 | |
| PENGUINITIS | |