4

比較的簡単な質問があります(と思います)。ひずみと特定の方向が与えられたときにひずみ楕円の半径を計算するCythonコードに取り組んでいます(つまり、特定の量のひずみに対して指定された方向に平行な半径)。この関数は、各プログラムの実行中に数百万回呼び出され、プロファイリングにより、この関数がパフォーマンス上の制限要因であることが明らかになりました。コードは次のとおりです。

# importing math functions from a C-library (faster than numpy)
from libc.math cimport sin, cos, acos, exp, sqrt, fabs, M_PI

cdef class funcs:

    cdef inline double get_r(self, double g, double omega):
        # amount of strain: g, angle: omega
        cdef double l1, l2, A, r, g2, gs   # defining some variables
        if g == 0: return 1   # no strain means the strain ellipse is a circle
        omega = omega*M_PI/180   # converting angle omega to radians
        g2 = g*g
        gs = g*sqrt(4 + g2)
        l1 = 0.5*(2 + g2 + gs)   # l1 and l2: eigenvalues of the Cauchy strain tensor
        l2 = 0.5*(2 + g2 - gs)
        A = acos(g/sqrt(g2 + (1 - l2)**2))   # orientation of the long axis of the ellipse
        r = 1./sqrt(sqrt(l2)*(cos(omega - A)**2) + sqrt(l1)*(sin(omega - A)**2))   # the radius parallel to omega
        return r   # return of the jedi

このコードの実行には、呼び出しごとに約 0.18 マイクロ秒かかります。これは、このような単純な関数としては少し長いと思います。また、 square(x) 関数がありますが、ライブラリmath.hからインポートできません。libc.mathこの小さなコードのパフォーマンスをさらに改善するための他の提案はありますか?

更新 2013/09/04:

目に見える以上のことが行われているようです。1000 万回呼び出す 1 つの関数をプロファイルするとget_r、別の関数を呼び出す場合とは異なるパフォーマンスが得られます。部分コードの更新バージョンを追加しました。プロファイリングにを使用するget_r_profileと、 の呼び出しごとに 0.073 マイクロ秒が得られますがget_r、の呼び出しごとにMC_criterion_profile約 0.164 マイクロ秒が得られます。これget_rは、 のオーバーヘッド コストに関連しているように見える 50% の差ですreturn r

from libc.math cimport sin, cos, acos, exp, sqrt, fabs, M_PI

cdef class thesis_funcs:

    cdef inline double get_r(self, double g, double omega):
        cdef double l1, l2, A, r, g2, gs, cos_oa2, sin_oa2
        if g == 0: return 1
        omega = omega*SCALEDPI
        g2 = g*g
        gs = g*sqrt(4 + g2)
        l1 = 0.5*(2 + g2 + gs)
        l2 = l1 - gs
        A = acos(g/sqrt(g2 + square(1 - l2)))
        cos_oa2 = square(cos(omega - A))
        sin_oa2 = 1 - cos_oa2
        r = 1.0/sqrt(sqrt(l2)*cos_oa2 + sqrt(l1)*sin_oa2)
        return r

    @cython.profile(False)
    cdef inline double get_mu(self, double r, double mu0, double mu1):
        return mu0*exp(-mu1*(r - 1))

    def get_r_profile(self): # Profiling through this guy gives me 0.073 microsec/call
        cdef unsigned int i
        for i from 0 <= i < 10000000:
            self.get_r(3.0, 165)

    def MC_criterion(self, double g, double omega, double mu0, double mu1, double C = 0.0):
        cdef double r, mu, theta, res
        r = self.get_r(g, omega)
        mu = self.get_mu(r, mu0, mu1)
        theta = 45 - omega
        theta = theta*SCALEDPI
        res = fabs(g*sin(2.0*theta)) - mu*(1 + g*cos(2.0*theta)) - C
        return res

    def MC_criterion_profile(self): # Profiling through this one gives 0.164 microsec/call
        cdef double g, omega, mu0, mu1
        cdef unsigned int i
        omega = 165
        mu0 = 0.6
        mu1 = 2.0
        g = 3.0
        for i from 1 <= i < 10000000:
            self.MC_criterion(g, omega, mu0, mu1)

get_r_profileと の間には根本的な違いがあり、MC_criterionそれが余分な間接費を引き起こす可能性があると思います。あなたはそれを見つけることができますか?

4

3 に答える 3

5

あなたのコメントによると、ラインコンピューティングrが最も高価です。その場合、パフォーマンスを低下させているのはトリガー関数呼び出しであると思われます。

ピタゴラスcos(x)**2 + sin(x)**2 == 1によるものなので、計算することでこれらの呼び出しの1つをスキップできます

cos_oa2 = cos(omega - A)**2
sin_oa2 = 1 - cos_oa2
r = 1. / sqrt(sqrt(l2) * cos_oa2 + sqrt(l1) * sin_oa2)

(または、それらを反転することもできます。私のマシンでは、sinよりも高速に見えますcos。NumPy の不具合かもしれません。

于 2013-09-03T21:46:17.593 に答える