1

Python関数呼び出しは比較的高価です。 しかし、さまざまな方法で関数を呼び出せるようにしたい場合があります。最も簡単な方法は、さまざまな呼び出しで関数の周りに軽いラッパーを作成することです。

関数を呼び出す方法以上のものを有効にするための、よりPython的および/またはより効率的な方法はありますか?


私が求めていることを説明する、完全に考案された、過度に単純な例については、次のようになります。

from math import sqrt
from collections import namedtuple

Point = namedtuple('Point', 'x y')

def distFunc(x1, y1, x2, y2):
    return sqrt((x1-x2)**2 + (y1-y2)**2)

def pointDistFunc(p1, p2):
    return distFunc(p1.x, p1.y, p2.x, p2.y)

pointDistFuncを書くためのより良い方法はありますか?

そのまま、今回は:

p1 = Point(1, 1)
p2 = Point(100, 100)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("distFunc(1, 1, 100, 100)", setup="from __main__ import distFunc"))
    print(timeit.timeit('pointDistFunc(p1, p2)', setup= 'from __main__ import pointDistFunc, p1, p2'))

与える:

0.392938508373
0.977704155415

したがって、オーバーヘッドが目立つようです。

4

1 に答える 1

2

一般的には、効率をあまり気にせずに、最も明確なコードを書くのが一番だと思います。この場合、私はあなたがすでに行ったようにそれをコーディングし、それについて心配する必要はないと思います。

しかし、少しのコードが頻繁に呼び出されることがわかっていて、それをできるだけ速くしたい場合は、書き直すことで処理を高速化できる可能性があります。簡単な例では、ラッパーを書き直して計算を行うだけで速度を上げることができます。

def pointDistFunc(p1, p2):
    return sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2)

理想的には、それをチェックするいくつかのユニットテストをどこかに持っている必要があります

pointDistFunc(p1, p2) == distFunc(p1.x, p1.y, p2.x, p2.y)

そうすれば、変更することになったが、変更distFunc()も忘れたpointDistFunc()場合、テストは失敗し、通知されます。

あなたが言及したそのガイドラインは、ラッパーを書かないようにすることをあまり意図していませんでした。リストのようなものを含むホットスポットを書き直す方法を提案することはもっと重要でした:

def gen_point_dist_from_lst(lst, p2):
    return (sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2) for p1 in lst)

リストに1000ポイントがある場合、上記は単純なジェネレータ式と比較して2000の関数呼び出しを節約します

(pointDistFunc(p1, p2) for p1 in lst)

重要なのは、これらのトリックを試す前に、実際に最初に問題を抱えることです。プログラムがすでに十分に高速に実行されている場合は、何も最適化する必要がない可能性があります。コードを高速化する必要がある場合は、これらのトリックを試すことができます。

PS PyPyを実行していることに使用できる場合は、関数呼び出しのオーバーヘッドを一掃する必要があります。PyPyには、プログラムのホットスポットを最適化するためのジャストインタイムコンパイラがあります。

http://speed.pypy.org/

于 2013-03-06T23:20:54.453 に答える