如何优化Haskell中数值库的速度

时间:2018-03-01 13:37:51

标签: performance haskell optimization

我已经发布了一个用于求解延迟微分方程的小型数值库:http://github.com/masterdezign/dde

主要技术限制:

  1. 使动态变量(x(t),y(t),...)的数量灵活,即动态系统的State在给定的时刻。< / LI>
  2. 轻松与利用Data.Vector.Storable(例如hmatrix)的库集成。因此,作为输入/输出,我广泛使用Data.Vector.Storable。
  3. 因此,与此解决方案不同: How do I optimize numerical integration performance in Haskell (with example),  使用newtype State = State { _state :: V.Vector Double } 而不是data State = State {-# UNPACK #-} !Double {-# UNPACK #-} !Double。但是,库现在运行速度慢了两倍。

    问题:有没有办法为未指定数量的变量带来data State = State {-# UNPACK #-}...的速度和newtype State = State { _state :: V.Vector Double }的灵活性?我应该考虑模板Haskell在编译时创建data UNPACK - 类似的结构吗?

1 个答案:

答案 0 :(得分:2)

我不会使用任何特定的矢量实现。像Data.Vector这样的可变长度类型是一个糟糕的选择,不仅因为当空间的维度较低时额外的长度信息是一个相当大的开销,还因为你失去了尺寸匹配的任何类型系统保证。

相反,你应该在矢量空间的选择上做出所有参数。即,您有效地使维度成为编译时变量,并且允许带有一些有意义的子变量的矢量类型。

import Data.VectorSpace
import Data.AdditiveGroup

newtype Stepper1 state = Stepper1 {
  _stepper
    ::  Double
    -> RHS state   -- parameterised in a similar way
    -> state
    -> (Double, Double)
    -> (Double, Double)
    -> state
  }

rk₄ :: VectorSpace v => Stepper1 v
rk₄ = Stepper1 _rk4
 where _rk4 hStep rhs' y₀ ... = y₀ ^+^ (h/6)*^(k₁ ^+^ 2*^k₂ ^+^ 2*^k₃ ^+^ k₄)
         where k₁ = rhs' (y₀, ...)
               k₂ = rhs' (y₀ ^+^ (h/2)*^k₁, ...)
               k₃ = rhs' (y₀ ^+^ (h/2)*^k₂, ...)
               k₄ = rhs' (y₀ ^+^ h*^k₃, ...)

然后,用户可以选择具体的实现方式。对于二维向量,标准选择是来自linear包的V2;它使用free-vector-spaces中的VectorSpace个实例重新导出。但是对于测试,您也可以使用普通的旧元组,vector-space中有VectorSpace个实例。当然,也可以从hmatrix包装类型,但这对性能来说真的不好 - 如果需要,最好只转换最终结果。

要获得最佳性能,您可能需要使用一些{-# INLINE #-} pragma。 Bang模式OTOH通常不会带来太多性能优势 - 最重要的是类型是严格的,并且是未装箱的。毫无疑问,在每个变量定义之前进行先发制人 - 这些都不会产生任何影响,因为CAF仅在使用时进行评估。

我很高兴听到你最终获得的表现!如果它明显比原来的State {-# UNPACK #-} !Double {-# UNPACK #-} !Double差,那就是我们应该调查的事情。