Optimization 在Haskell中进行有效的数值计算
我受到了这篇名为“”的帖子的启发,来看看他在Haskell中提出的问题(从一个向量中求出几百万个数字的和),并与他的结果进行比较 我是一个Haskell新手,所以我真的不知道如何正确计时或如何有效地进行计时,我对这个问题的第一次尝试如下。请注意,我没有在向量中使用随机数,因为我不知道如何以一种好的方式使用。我也在打印资料,以确保全面评估Optimization 在Haskell中进行有效的数值计算,optimization,haskell,numerics,Optimization,Haskell,Numerics,我受到了这篇名为“”的帖子的启发,来看看他在Haskell中提出的问题(从一个向量中求出几百万个数字的和),并与他的结果进行比较 我是一个Haskell新手,所以我真的不知道如何正确计时或如何有效地进行计时,我对这个问题的第一次尝试如下。请注意,我没有在向量中使用随机数,因为我不知道如何以一种好的方式使用。我也在打印资料,以确保全面评估 import System.TimeIt import Data.Vector as V vector :: IO (Vector Int) vector
import System.TimeIt
import Data.Vector as V
vector :: IO (Vector Int)
vector = do
let vec = V.replicate 3000000 10
print $ V.length vec
return vec
sumit :: IO ()
sumit = do
vec <- vector
print $ V.sum vec
time = timeIt sumit
import System.TimeIt
导入数据。向量为V
向量::IO(向量Int)
向量=do
设vec=V.10,复制3000000
打印$V.length向量
返回向量
sumit::IO()
sumit=do
vec尝试使用一个未绑定的向量,尽管我不确定它在这种情况下是否有明显的区别。还要注意,这种比较有点不公平,因为向量包应该完全优化向量(这种优化称为流融合)。首先,要认识到GHCi是一个解释器,它的设计速度不是很快。为了获得更有用的结果,您应该编译启用优化的代码。这可以带来巨大的不同
此外,对于Haskell代码的任何重要基准测试,我建议使用。它使用各种统计技术来确保获得可靠的测量结果
我修改了您的代码以使用Criteria并删除了打印语句,这样我们就不会对I/O进行计时
import Criterion.Main
import Data.Vector as V
vector :: IO (Vector Int)
vector = do
let vec = V.replicate 3000000 10
return vec
sumit :: IO Int
sumit = do
vec <- vector
return $ V.sum vec
main = defaultMain [bench "sumit" $ whnfIO sumit]
所以我得到的平均值刚刚超过9毫秒,标准偏差不到一毫秒。对于更大的测试用例,我得到大约100ms
当使用vector
包时,启用优化尤其重要,因为它大量使用流融合,在这种情况下,流融合能够完全消除数据结构,将您的程序变成一个高效、紧密的循环
通过使用-fllvm
选项来试验新的基于LLVM的代码生成器也是值得的 您的原始文件,未编译,然后未经优化编译,然后使用简单优化标志编译:
$ runhaskell boxed.hs
3000000
30000000
CPU time: 0.35s
$ ghc --make boxed.hs -o unoptimized
$ ./unoptimized
3000000
30000000
CPU time: 0.34s
$ ghc --make -O2 boxed.hs
$ ./boxed
3000000
30000000
CPU time: 0.09s
使用import qualified Data.Vector.unbox作为V
而不是import qualified Data.Vector作为V
导入文件(Int
是一种不可绑定类型)--
首先没有优化,然后使用:
$ ghc --make unboxed.hs -o unoptimized
$ ./unoptimized
3000000
30000000
CPU time: 0.27s
$ ghc --make -O2 unboxed.hs
$ ./unboxed
3000000
30000000
CPU time: 0.04s
所以,编译,优化。。。在可能的情况下,使用Data.Vector.unbox
如果使用足够大的向量,则向量unbox可能变得不切实际。对于我来说,如果向量大小>50000000,纯(惰性)列表更快:
import System.TimeIt
sumit :: IO ()
sumit = print . sum $ replicate 50000000 10
main :: IO ()
main = timeIt sumit
我得到这些时间:
Unboxed Vectors
CPU time: 1.00s
List:
CPU time: 0.70s
Edit:我使用criteria重复了基准测试,并使sumit
pure。代码和结果如下所示:
代码:
结果:
warming up
estimating clock resolution...
mean is 7.248078 us (80001 iterations)
found 24509 outliers among 79999 samples (30.6%)
6044 (7.6%) low severe
18465 (23.1%) high severe
estimating cost of a clock call...
mean is 68.15917 ns (65 iterations)
found 7 outliers among 65 samples (10.8%)
3 (4.6%) high mild
4 (6.2%) high severe
benchmarking sumit
collecting 100 samples, 1 iterations each, in estimated 46.07401 s
mean: 451.0233 ms, lb 450.6641 ms, ub 451.5295 ms, ci 0.950
std dev: 2.172022 ms, lb 1.674497 ms, ub 2.841110 ms, ci 0.950
它看起来像是print
产生了很大的不同,这是意料之中的 小心你测量的东西。目前,您正在测量向量的分配和求和。不要使用ghci进行性能测试。使用ghc--make-O2.ique
,查看关于使用向量
包的优秀教程:您是使用优化编译的吗?对于你的版本,我得到了相同的比率,4:60,即使是100倍的数字。是的,我用ghc--make-O2
编译。
warming up
estimating clock resolution...
mean is 7.248078 us (80001 iterations)
found 24509 outliers among 79999 samples (30.6%)
6044 (7.6%) low severe
18465 (23.1%) high severe
estimating cost of a clock call...
mean is 68.15917 ns (65 iterations)
found 7 outliers among 65 samples (10.8%)
3 (4.6%) high mild
4 (6.2%) high severe
benchmarking sumit
collecting 100 samples, 1 iterations each, in estimated 46.07401 s
mean: 451.0233 ms, lb 450.6641 ms, ub 451.5295 ms, ci 0.950
std dev: 2.172022 ms, lb 1.674497 ms, ub 2.841110 ms, ci 0.950