我有一个python脚本,作为进化优化算法的一部分,我正在评估偏导数数千次。我已经逐行完成了一个行,这个偏导数计算占用了大部分的运行时间。我正在使用scipy.optimize.approx_fprime
计算偏导数,我试图在cython中重写它而没有太大的成功。
逐行配置文件如下。我的scipy.optimize.approx_fprime
的cython化版本简称为approx_fprime
。
Line # Hits Time Per Hit % Time Line Contents
==============================================================
84 @profile
100 1500 14889652 9926.4 25.3 df1 = approx_fprime(inp_nom,evaluate1,epsilon)
101 1500 14939889 9959.9 25.4 df2 = scipy.optimize.approx_fprime(inp_upp,evaluate1,epsilon)
下面是我的cython文件。
import numpy as np
cimport numpy as np
cimport cython
@cython.boundscheck(False) # turn of bounds-checking for entire function
def approx_fprime(np.ndarray xk, f, double epsilon, *args):
# From scipy.optimize.approx_fprime
f0 = f(*((xk,) + args))
cdef np.ndarray grad = np.zeros((len(xk),), float)
cdef np.ndarray ei = np.zeros((len(xk),), float)
cdef np.ndarray d = epsilon * ei
for k in xrange(len(xk)):
ei[k] = 1.0
grad[k] = (f(*((xk + d,) + args)) - f0) / d[k]
ei[k] = 0.0
return grad
我试图输入所有相关的类型声明,并确保它与numpy很好地配合。不过,最终,正如他们所说,证据就在于布丁。这个版本并不比scipy版本快。该函数只有一些变量,因此它不是一个庞大的计算,并且在一次迭代中可能只有增量改进的空间。然而,函数被反复调用,因为它被用在进化优化算法中,所以我期望/希望增量性能增益倍增会产生很大的回报。
那里的一位cython专家可以看看这段代码并帮我弄清楚我是否在正确的轨道上,或者这只是一个傻瓜的差事?
谢谢!
答案 0 :(得分:0)
首先要注意的是优化代码就是要找到代码中的瓶颈。通常很少有功能,循环等消耗大部分时间。这些是优化的合适候选者。最重要的是: 使用分析器评估代码性能 。
优化python代码的第一件事是逐行遍历代码并检查每一行是否创建了新对象。这是因为对象创建与简单算术相比非常昂贵。经验法则:尽可能避免创建对象。但请确保您没有在时间关键循环中创建任何新对象。
看看f*((xk + d,) + args)
。这是非常好的python代码 - 但如果你需要高性能则不适合。它将在循环的每个步骤中创建一个新的参数元组。以不会创建任何对象的方式重写它可能会为您带来巨大的性能提升。
下一步是静态输入。确保键入循环中使用的所有内容。输入k
可能会获得很多收益。
之后,您可以尝试通过取消设置boundscheck
等来进一步优化。
最重要的是:迭代地进行优化,并通过分析代码来检查性能提升。大多数情况下,很难看出代码中的瓶颈究竟是什么。分析将给你提示:如果优化没有获得太多,你可能错过了瓶颈。