Optimization Haskell中的严格性优化和内存分配
通过实现一个特征选择算法,我学习了一些Haskell 我在基准数据集上的性能从20秒下降到5秒,其中C程序在0.5秒内处理相同的数据集。可以找到数据集。要运行,请像这样调用编译后的二进制文件:Optimization Haskell中的严格性优化和内存分配,optimization,haskell,functional-programming,Optimization,Haskell,Functional Programming,通过实现一个特征选择算法,我学习了一些Haskell 我在基准数据集上的性能从20秒下降到5秒,其中C程序在0.5秒内处理相同的数据集。可以找到数据集。要运行,请像这样调用编译后的二进制文件:/Mrmr 10 test\u nci9\u s3.csv 代码是,我感兴趣的是优化MutualInfo内部循环: mutualInfoInnerLoop :: Double -> Data.Vector.Unboxed.Vector (Int, Int) -> Double -> (In
/Mrmr 10 test\u nci9\u s3.csv
代码是,我感兴趣的是优化MutualInfo内部循环:
mutualInfoInnerLoop :: Double -> Data.Vector.Unboxed.Vector (Int, Int) -> Double -> (Int, Int, Double) -> Double
mutualInfoInnerLoop n xys !acc (!i, !j, !px_py)
| n == 0 || px_py == 0 || pxy == 0 = acc
| otherwise = pxy * logBase 2 ( pxy / px_py ) + acc
where
pxy = ( fromIntegral . U.foldl' accumEq2 0 $ xys ) / n
accumEq2 :: Int -> (Int, Int) -> Int
accumEq2 !acc (!i', !j')
| i' == i && j' == j = acc + 1
| otherwise = acc
剖析者说:
COST CENTRE MODULE %time %alloc
mutualInfoInnerLoop Main 75.0 47.9
mutualInfo Main 14.7 32.1
parseCsv Main 5.9 13.1
CAF GHC.Float 1.5 0.0
readInt Main 1.5 1.2
doMrmr Main 1.5 4.0
这表明MutualInfo InnerLoop完成了50%的分配,程序中有75%的运行时。分配令人不安
此外,该功能的核心有一个签名:
mutualInfoInnerLoop_rXG
:: GHC.Types.Double
-> Data.Vector.Unboxed.Base.Vector (GHC.Types.Int, GHC.Types.Int)
-> GHC.Types.Double
-> (GHC.Types.Int, GHC.Types.Int, GHC.Types.Double)
-> GHC.Types.Double
[GblId,
Arity=4,
Caf=NoCafRefs,
Str=DmdType U(L)LU(L)U(U(L)U(L)U(L))m]
将大多数参数显示为惰性计算和装箱(与严格和未装箱相反)
我试过刘海模式,我试过魔术师,但我似乎无法让它走得更快
有人有什么建议吗?我目前还不是这方面的专家,但我确实看到了一个小小的改进。在你的资料中,我看到:
mutualInfo n ... = foldl' (mutualInfoInnerLoop n $ U.zip xs ys) ...
您不需要每次调用函数时都检查n==0
,因为调用函数时从不更改n
参数。xys
参数也不会改变,这意味着pxy
不会在调用之间改变,因为它完全依赖于xys
和n
。让我们利用这些东西来确保创建一个闭包,它只对这些东西进行一次评估
mutualInfoInnerLoop n xys
| n == 0 || pxy == 0 = const
| otherwise = go
where pxy = (fromIntegral . U.foldl' accumEq2 0 $ xys) / n
accumEq2 :: Int -> (Int, Int) -> Int
accumEq2 !acc (!i', !j')
| i' == i && j' == j = acc + 1
| otherwise = acc
go !acc (!i, !j, !px_py)
| px_py == 0 = acc
| otherwise = pxy * logBase 2 ( pxy / px_py ) + acc
我不确定GHC是否足够聪明,能够独立执行此优化,也不确定这是否节省了很多时间/空间,但这是我所拥有的最好的。这些爆炸模式到处都是,我想这是不是太严格了。我想你已经试过ghc-O2了?是的,我试过了。源代码还带有
{-#LANGUAGE BangPatterns}
和{-#OPTIONS\u GHC-O2-fexcess precision-funbox strict fields}
可以提供用于基准测试的Int
和文件吗?核心并不令人惊讶。您已将mutualInfoInnerLoop
注释为NOINLINE,因此GHC不会将其内联。因此,所有参数都不能解除绑定。如果您将NOINLINE
更改为INLINE
,会发生什么?如果您的想法是要满足或击败C代码,那么我们也需要它。非常好的建议!实际上,我最终将n==0移到了domrmrmr中,这似乎是个好主意。至于这个构造,如果不解包i或j,我们就无法构造accumEq2——这让我想知道accumEq2的动态构造性质是否是问题所在。实际上,所有这些爆炸模式似乎都有帮助,只需刮去半秒钟(我们实际上在4秒左右)。哦,我忘了accumEq2中使用了i和j。我的错。