python – 在Cython中优化代码的技巧
我有一个相对简单的问题(我认为).我正在研究一段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微秒,我认为这对于这样一个简单的函数来说有点长.另外,math.h有一个square(x)函数,但是我不能从libc.math库中导入它,谁知道怎么做?有什么其他建议可以进一步改善这段小代码的性能吗? 更新2013/09/04: 似乎有更多的在发挥,而不是满足眼睛.当我分析一个调用get_r 10万次的函数时,我获得的性能与调用另一个函数不同.我添加了部分代码的更新版本.当我使用get_r_profile进行性能分析时,每次调用get_r得到0.073微秒,而MC_criterion_profile给我约0.164微秒/ get_r调用,50%的差异似乎与返回r的开销成本有关. from libc.math cimport sin,M_PI cdef class thesis_funcs: cdef inline double get_r(self,double omega): cdef double l1,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 omega,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,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,mu1) 我认为get_r_profile和MC_criterion之间可能存在根本区别,这会导致额外的开销成本.你能发现它吗? 解决方法
根据你的评论,线路计算r是最昂贵的.如果是这种情况,那么我怀疑是触发性能的trig函数调用.
通过Pythagoras,cos(x)** 2 sin(x)** 2 == 1所以你可以通过计算跳过其中一个调用 cos_oa2 = cos(omega - A)**2 sin_oa2 = 1 - cos_oa2 r = 1. / sqrt(sqrt(l2) * cos_oa2 + sqrt(l1) * sin_oa2) (或者可能翻转它们:在我的机器上,罪似乎比cos快.可能是一个NumPy小故障.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |