假设我们有一个简单的Haskell函数,可以生成勾股三元组:
pytha :: [(Int, Int, Int)]
pytha = [(x, y, z)
| z <- [0..]
, x <- [1..z]
, y <- [x..z]
, x * x + y * y == z * z
]
,我们希望确定生产前100个三元组所需的时间。因此(使用criterion
库并假设import Criterion.Main
),我们有以下基准:
main :: IO ()
main = do
countStr <- readFile "count.txt"
defaultMain [ bgroup "pytha" [ bench countStr $ nf (`take` pytha) (read countStr) ] ]
我们甚至从文件中读取count
,以确保ghc在编译期间不会尝试评估pytha
!
进行echo 100 > count.txt
,用-O2
编译基准并在我的机器(4.0 GHz Sandy Bridge CPU)上运行时显示了一些有趣的数字:
time 967.4 ns (957.6 ns .. 979.3 ns)
0.999 R² (0.998 R² .. 0.999 R²)
mean 979.6 ns (967.9 ns .. 995.6 ns)
std dev 45.34 ns (33.96 ns .. 60.29 ns)
稍微修改该程序以显示总体上考虑了多少个三元组(方法是先生成所有三元组,然后用[0..]
压缩列表,然后过滤掉所有非毕达哥拉斯三元组,然后查看生成的三元组的索引)表明已考虑了将近900000个三元组。
所有这些自然地引发了一个问题:上面的代码如何在漂亮的标准CPU的单核上实现1000个triple / ns?还是仅仅是我的基准测试错了?
答案 0 :(得分:2)
您需要使用将要记忆的函数而不是值。
pytha :: Int -> [(Int, Int, Int)]
pytha z_max =
[ (x, y, z)
| z <- [0..z_max]
, x <- [1..z]
, y <- [x..z]
, x * x + y * y == z * z
]
GHC不会变得足够聪明,无法从常量列表中将其纳入takeWhile
,因此它应该提供有意义的基准。只要确保Criterion负责传递z_max
,您就可以合理地将其设置为maxBound :: Int
或类似的东西。
顺便说一句:通过使用浮点运算为y
计算更严格的界限,可以使实现的速度降低很多。