【发布时间】:2011-08-16 01:00:51
【问题描述】:
我目前正在使用 Euler 问题作为我的游乐场来学习 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);
}
}
编辑:在 cmets 中进行讨论后,我对 Haskell 代码进行了一些修改并进行了一些性能测试。首先,我将 n 更改为 2^29 以避免溢出。然后我尝试了 6 个不同的版本: Int64 或 Int 以及在 norm2 或两者之前带有刘海,以及声明 arcLength' x y !norm2 !acc 中的 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。根据cmets的说法,在其他配置下编译时不会出现该问题。这让我觉得 Int64 类型在 Windows 版本中被破坏了。
【问题讨论】:
-
你试过刘海模式吗?
arcLength' x y !norm2 !acc?norm2和acc并不总是严格传递,因为在采用第一个分支时可能不需要它们。顺便说一句,在我的机器上只需要 6 秒。 -
一般来说,Haskell 可以是速度更快的语言之一。你的 Haskell 代码中可能有一些东西会导致更复杂的情况,或者不能很好地配合 GHC 的优化。
-
-O2是 GHC 优化的典型标志。 -O 不会做太多,iirc。 -
cmets 似乎暗示这是一个与 32 位 Windows 上的 GHC 7.0.2 和 64 位 GMP(
Int64类型来自哪里)相关的错误。你可以升级libgmp,升级 GHC 到 7.0.3 或者在 64 位 Windows 上测试吗? -
我在 GHC 7.0.3 上得到了相同的行为。我也在 64 位 Windows 上运行。但我怀疑 Haskell 平台的二进制发行版是 32 位的。没有任何 64 次下载。
标签: windows performance haskell 64-bit 32bit-64bit