C++ 简单π;(x) 在哈斯凯尔vs C++;

C++ 简单π;(x) 在哈斯凯尔vs C++;,c++,performance,haskell,C++,Performance,Haskell,我在学哈斯克尔。我的兴趣是把它用于个人电脑实验。现在,我想看看哈斯克尔能跑多快。许多人声称与C++是对等的,如果这是真的,我将非常高兴(我应该注意,我将使用Haskell,无论它是否快,但快仍然是一件好事) 我的测试程序用一个非常简单的算法实现π(x):素数加1到结果。素数没有介于1和之间的整数除数√十,。这不是一场算法之战,这纯粹是为了提高编译器性能 Haskell在我的计算机上似乎慢了6倍,这很好(仍然比纯Python快100倍),但这可能只是因为我是Haskell的新手 现在,我的问题是:

我在学哈斯克尔。我的兴趣是把它用于个人电脑实验。现在,我想看看哈斯克尔能跑多快。许多人声称与C++是对等的,如果这是真的,我将非常高兴(我应该注意,我将使用Haskell,无论它是否快,但快仍然是一件好事)

我的测试程序用一个非常简单的算法实现π(x):素数加1到结果。素数没有介于1和之间的整数除数√十,。这不是一场算法之战,这纯粹是为了提高编译器性能

Haskell在我的计算机上似乎慢了6倍,这很好(仍然比纯Python快100倍),但这可能只是因为我是Haskell的新手

现在,我的问题是:如何在不改变算法的情况下优化Haskell实现?Haskell的性能真的与C不相上下吗?

这是我的
Haskell
代码:

导入系统环境
--简单整数平方根
isqrt::Int->Int
isqrt=地板。sqrt。从积分
--素性检验
素数:Int->Bool

Primex= null [x,qqp] >您的Haskell版本正在构造一个懒惰列表,在代码> Prime < /C>中,只测试它是否为NULL。这看起来确实是瓶颈。下面的版本运行速度与我的机器上的C++版本一样快:

prime :: Int -> Bool
prime x = go 3
  where
    go q | q <= isqrt x = if rem x q == 0 then False else go (q+2)
    go _  = True
给予

这清楚地表明,
prime
是事情变得热门的地方

要真正了解正在发生的事情,您可以查看GHC的中间语言核心之一,它将向您展示优化后代码的外观。一些好信息是。除非必要,否则我不建议这样做,但很高兴知道这种可能性存在

关于你的其他问题:

1) 如何在不改变算法的情况下优化Haskell实现

配置文件,并尝试编写内部循环,以便它们不进行任何内存分配,并且可以由编译器严格控制。这样做需要一些实践和经验

2) Haskell的性能真的与C相当吗


那要看情况了。GHC非常棒,通常可以很好地优化你的程序。如果你知道你在做什么,你通常可以接近优化C的性能(C速度的100%-200%)。也就是说,这些优化并不总是简单或好看的,高级Haskell可能会慢一些。但不要忘记,在使用Haskell时,您获得了惊人的表达能力和高级抽象。通常,除了性能最关键的应用程序外,它的速度足够快,即使这样,您也可以获得非常好的CLOE-C与一些分析和性能优化。

< P>我不认为Haskell版本(原始和改进的第一个答案)等同于C++版本。 原因是: 两个都只考虑每个元素(在质数函数中),而C++版本扫描每个元素(只有iS+(在IsPrime())函数中。

<>当我修复C++(iC++)函数中的i+++到i+=2时,我得到了优化的Haskell版本(2.1s C+VS 6S Haskell)的运行时间的1/3。 当然,这两种方法的输出都是相同的。
注意,这不是C++版本的具体化,只是对Haskell版本中已经应用过的技巧的一种改编。

你是否编译了优化代码?在编译GHC时,你想使用<代码> -O2>代码。LLVM位与它不应该有太大关系。(不完全确定,我从未将其主要用于windows开发)。因此,通过运行一个没有数据结构甚至没有任何不可预测的分支的程序,您将函数式编程系统与具有显式内存管理的命令式编程系统进行比较。顺便说一句,不要忘了
-fprofile generate
-fprofile use
。顺便说一句,当我看到那些sqrt/floor计算时,我的眼睛会痛CK如果代码> Q> IGO:……完全不需要的(哇!谢谢所有的信息)(尤其是剖析部分)。我尝试了一个尾递归版本,但是它由于某些原因而非常缓慢(我使用多个参数来携带变量)。C++是(4.8)真的很快吗?它的速度仍然比哈斯克尔快1.5倍,尽管我自己也很高兴。我不会很快放弃这种表现力,我只是想看看它有多痛。:)我会看看其他人是否有什么要补充的……如果没有,你的答案是可以接受的。@PythonNut:如果你在启用分析的情况下运行它,当然会慢一些。根据我的经验,你可以非常接近(+50-100%),但奇偶性有时很难/不可能实现。取决于你的经验,我不是这里的绝对专家。但是,这些表达性语言会让你很容易使用高级结构,而这些结构的速度不会像一个简单的循环那么快。懒惰列表就是一个很好的例子。尽管如此,大多数情况下这并不重要,而且你仍然可以获得很好的速度。我认为
prime
的问题不在于懒惰,而在于解封。在@Paul的
prime
中,ghc生成了一个未装箱的内部循环。有了懒惰列表,内部循环非常好,但它可以处理装箱整数,因此必须同时解封它们每一步。顺便说一句,我通过将原始的
prime
定义更改为使用未装箱的向量而不是列表来获得与递归
prime
相同的性能。您是否尝试过
V.null$V.filter(\q->rem x q==0)$V.iterateN(quot(isqrt x)2)(+2)$3
相反?似乎您可能会比
generate
赢一点。使用criterian检查,使用iterateN和正确数量的元素的版本与循环一样快,因此
V.null.V.filter(==0)。(rem x)).V.iterateN((isqrt x-1)`quot`2)(+2)$3
$ ghc --make primes.hs -O2 -prof -auto-all -fforce-recomp && ./primes 5000000 +RTS -p
# primes.prof
  Thu Feb 20 00:49 2014 Time and Allocation Profiling Report  (Final)

     primes +RTS -p -RTS 5000000

  total time  =        5.71 secs   (5710 ticks @ 1000 us, 1 processor)
  total alloc = 259,580,976 bytes  (excludes profiling overheads)

COST CENTRE MODULE  %time %alloc

prime.go    Main     96.4    0.0
main        Main      2.0   84.6
isqrt       Main      0.9   15.4

                                                      individual     inherited
COST CENTRE MODULE                  no.     entries  %time %alloc   %time %alloc

MAIN        MAIN                     45           0    0.0    0.0   100.0  100.0
 main       Main                     91           0    2.0   84.6   100.0  100.0
  prime     Main                     92     2500000    0.7    0.0    98.0   15.4
   prime.go Main                     93   326103491   96.4    0.0    97.3   15.4
    isqrt   Main                     95           0    0.9   15.4     0.9   15.4

--- >8 ---