首页 文章

优化反复调用数学函数

提问于
浏览
0

我正在编写一些代码作为非线性回归工具的一部分,我试图找出一种方法,以一种可读和快速的良好 balancer 的方式返回给定函数的第n个偏导数 . 函数(以及部分的分析表示)在运行时是已知的,这些可以是硬编码的 . 这是我到目前为止(有效):

let getPartials (paramVect: array<float>) idx =
    let a = paramVect.[0]
    let b = paramVect.[1]
    let c = paramVect.[2]
    match idx with
    | 1 -> (fun  x -> (1.0+b+c*x)**(-1.0/b)) // df(x)/da
    | 2 -> (fun  x -> ((a*(1.0+c*b*x)**(-(b+1.0)/b))*((b*c*x+1.0)*Math.Log(b*c*x+1.0)-b*c*x))/(b*b))       // df(x)/db
    | 3 -> (fun  x -> -a*x*(b*c*x+1.0)**(-(b+1.0)/b))   //  df/dc
    | _ -> (fun x  -> 0.0)   //everything else is zero

我使用它的方法是首先使用参数向量构造一个部分函数,以便最小化需要传入的次数 . 然后我反复调用(getPartials(i)x_val)来构造一个jacobian . 在程序的生命周期中,此函数被调用的次数非常多 .

我得到了相当可接受的性能,但是,我怀疑它可以改进 . 分析表明,第二个函数计算(长一个)的评估是一个cpu排放 - 这可以优化吗?我不确定匿名函数是否会产生性能问题,因为它是可读的......

我是F#编程的新手,所以如果您发现任何风格/形式或性能方面的任何问题,请告诉我!

谢谢


更新:在实现 JohnPalmer 建议的更改并重构之后,不是返回一个接受x值作为参数的匿名函数,而是在原地进行整个计算,我看到大约300%的速度增加 . 能够返回部分功能更方便,但不值得花费 .

let getPartials  (paramVect: array<float>) idx x =
let a = paramVect.[0]
let b = paramVect.[1]
let cbx =  paramVect.[1] * paramVect.[2] * x

match idx with
| 1 -> (1.0+cbx)**(-1.0/b) // df(x)/da
| 2 -> ((a*(1.0+cbx)**(-(b+1.0)/b))*((cbx+1.0)*Math.Log(cbx+1.0)-cbx))/(b**2.0) // df(x)/db
| 3 ->  -a*x*(cbx+1.0)**(-(b+1.0)/b)      //  df/dc
| _ ->  0.0                               //everything else is zero

1 回答

  • 1

    导致性能问题的匿名函数最可能的原因是每次调用getPartials函数时都会创建一个新的堆对象 . 如果您只有少量不同的 paramVect ,则可以通过缓存匿名函数获得一些性能优势 .

    至于第二个表达式的评估,你可以尝试这个(采用John Palmer的建议来消除常见的子表达式):

    fun  x -> let bcx = b * c * x
              let bcx1 = bcx + 1.0
              a * bcx1 ** (-(b+1.0)/b) * (bcx1 * Math.Log bcx1 - bcx)/(b*b)
    

相关问题