我正在学习
Haskell使用项目欧拉问题作为我的操场.
我很惊讶,我的Haskell计划结果比较相似
以其他语言编写的程序.我想知道我是否会看到某些东西,或者是使用Haskell时这种表现的惩罚.
我很惊讶,我的Haskell计划结果比较相似
以其他语言编写的程序.我想知道我是否会看到某些东西,或者是使用Haskell时这种表现的惩罚.
以下程序灵感来自于问题331,但是我在发布之前已经改变了,所以我不会为别人破坏任何东西.它计算在2 ^ 30 x 2 ^ 30网格上绘制的离散圆的弧长.这是一个简单的尾递归实现,我确保累积变量跟踪弧长度的更新是严格的.然而,完成需要将近一个半分钟(用ghc的-O标志编译).
import Data.Int arcLength :: Int64->Int64 arcLength n = arcLength' 0 (n-1) 0 0 where arcLength' x y norm2 acc | x > y = acc | norm2 < 0 = arcLength' (x + 1) y (norm2 + 2*x +1) acc | norm2 > 2*(n-1) = arcLength' (x - 1) (y-1) (norm2 - 2*(x + y) + 2) acc | otherwise = arcLength' (x + 1) y (norm2 + 2*x + 1) $! (acc + 1) main = print $arcLength (2^30)
这是Java中相应的实现.完成约需4.5秒
public class ArcLength { public static void main(String args[]) { long n = 1 << 30; long x = 0; long y = n-1; long acc = 0; long norm2 = 0; long time = System.currentTimeMillis(); while(x <= y) { if (norm2 < 0) { norm2 += 2*x + 1; x++; } else if (norm2 > 2*(n-1)) { norm2 += 2 - 2*(x+y); x--; y--; } else { norm2 += 2*x + 1; x++; acc++; } } time = System.currentTimeMillis() - time; System.err.println(acc); System.err.println(time); }
}
编辑:在评论中讨论之后,我对Haskell代码进行了一些修改,并进行了一些性能测试.首先我将n改为2 ^ 29以避免溢出.然后我尝试了6个不同的版本:使用Int64或Int,并且在norm2或者两者之前都有bangs,而norm2和acc在声明arcLength’x y!norm2!acc.所有都是编译的
ghc -O3 -prof -rtsopts -fforce-recomp -XBangPatterns arctest.hs
结果如下:
(Int !norm2 !acc) total time = 3.00 secs (150 ticks @ 20 ms) total alloc = 2,892 bytes (excludes profiling overheads) (Int norm2 !acc) total time = 3.56 secs (178 ticks @ 20 ms) total alloc = 2,892 bytes (excludes profiling overheads) (Int norm2 acc) total time = 3.56 secs (178 ticks @ 20 ms) total alloc = 2,892 bytes (excludes profiling overheads) (Int64 norm2 acc) arctest.exe: out of memory (Int64 norm2 !acc) total time = 48.46 secs (2423 ticks @ 20 ms) total alloc = 26,246,173,228 bytes (excludes profiling overheads) (Int64 !norm2 !acc) total time = 31.46 secs (1573 ticks @ 20 ms) total alloc = 3,032 bytes (excludes profiling overheads)
我在64位Windows 7(Haskell平台二进制分发)下使用GHC 7.0.2.根据注释,在其他配置下进行编译时,不会出现此问题.这使我觉得Int64类型在Windows版本中被破坏.
嗯,我用7.0.3安装了一个新的Haskell平台,并为您的程序大概地获得了以下内核(-ddump-simple):
Main.$warcLength' = \ (ww_s1my :: GHC.Prim.Int64#) (ww1_s1mC :: GHC.Prim.Int64#) (ww2_s1mG :: GHC.Prim.Int64#) (ww3_s1mK :: GHC.Prim.Int64#) -> case {__pkg_ccall ghc-prim hs_gtInt64 [...] ww_s1my ww1_s1mC GHC.Prim.realWorld# [...]
所以GHC已经意识到它可以打包整数,这是很好的.但是这个hs_getInt64调用看起来像C调用一样可疑.看看汇编器输出(-ddump-asm),我们看到的东西就像:
pushl %eax movl 76(%esp),%eax pushl %eax call _hs_gtInt64 addl $16,%esp
所以这看起来非常像Int64中的每个操作在后台变成一个完整的C调用.这很慢,显然.
GHC.IntWord64的source code似乎验证了:在32位版本(如当前与平台一起提供的版本)中,您将只能通过FFI界面进行仿真.