比較的簡単な質問があります(と思います)。ひずみと特定の方向が与えられたときにひずみ楕円の半径を計算する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
それが余分な間接費を引き起こす可能性があると思います。あなたはそれを見つけることができますか?