Haskell三角微基准

时间:2012-09-05 09:51:36

标签: haskell ghc

考虑这个简单的“基准”:

n :: Int
n = 1000
main = do
    print $ length [(a,b,c) | a<-[1..n],b<-[1..n],c<-[1..n],a^2+b^2==c^2]

和适当的C版本:

#include <stdio.h>

int main(void)
{
    int a,b,c, N=1000;
    int cnt = 0;

    for (a=1;a<=N;a++)
        for (b=1;b<=N;b++)
            for (c=1;c<=N;c++)
                if (a*a+b*b==c*c) cnt++;
    printf("%d\n", cnt);
}

汇编:

  • Haskell版本编译为:ghc -O2 triangle.hs(ghc 7.4.1)
  • C版编译为:gcc -O2 -o triangle-c triangle.c(gcc 4.6.3)

运行时间:

  • Haskell:4.308s真实
  • C:1.145s real

即使对于如此简单且可以很好地优化的程序,Haskell几乎慢了4倍,它是否行为正常? Haskell在哪里浪费时间?

3 个答案:

答案 0 :(得分:8)

Haskell版本浪费时间分配盒装整数和元组。

您可以通过运行带有标志+RTS -s的haskell程序来验证这一点。对我来说,输出的统计数据包括:

  80,371,600 bytes allocated in the heap

C版本的直接编码更快,因为编译器可以使用未装箱的整数并跳过分配元组:

n :: Int
n = 1000
main = do
  print $ f n

f :: Int -> Int
f max = go 0 1 1 1
  where go cnt a b c
          | a > max = cnt
          | b > max = go cnt (a+1) 1 1
          | c > max = go cnt a (b+1) 1
          | a^2+b^2==c^2 = go (cnt+1) a b (c+1)
          | otherwise = go cnt a b (c+1)

请参阅:

  51,728 bytes allocated in the heap

对于C版本,此版本的运行时间为1.920s1.212s

答案 1 :(得分:4)

我不知道你的“替补”有多大关系。 我同意list-comprehension语法是“很好”使用,但如果你想比较两种语言的性能,你应该在更公平的测试中比较它们。 我的意思是,创建一个可能包含很多元素的列表然后计算它的长度就像在(三重循环)中递增计数器一样。

所以也许haskell有一些很好的优化可以检测你在做什么并且永远不会创建列表,但是我不会依赖于那个代码,你可能也不应该。

如果你需要快速计算,我认为你不会像这样编写你的程序代码,为什么要为这个工作台做这个呢?

答案 2 :(得分:3)

Haskell 可以进行优化 - 但是你需要适当的技术,你需要知道你在做什么。

这个列表理解语法优雅而浪费。您应该阅读appropriate chapter of RealWorldHaskell以了解有关您的分析机会的更多信息。在这个确切的情况下,你创建了许多列表刺和盒装Int,完全没有任何理由。请参见此处:type profile for benchmark

你一定要对此采取行动。编辑:@opqdonut刚刚发布了一个很好的答案,如何加快速度。

请记住下次在比较任何基准测试之前分析您的应用程序。 Haskell使编写惯用代码变得容易,但它也隐藏了很多复杂性。