2014-02-24 101 views
-2

在我開始之前,我想說我已經知道在絕大多數用例中,性能並不是一些「全部結束」或不相關的。我也知道「如果性能是一個巨大的問題,使用填空(C,彙編程序等)。」所以我不需要包含我剛纔陳述的答案。假設我們在這裏的目的是要麼1)我只是在智力上好奇,或者2)我有其他相關的理由來探索這個。無論如何,雖然我並不是新的函數式編程(Erlang等),還是使用遞歸和單賦值(Prolog)編程,但我對Haskell來說是非常新的。我只是試圖獲得一些基本的基準測試,瞭解它如何執行基本任務,例如反覆調用函數,遍歷列表等。基本Haskell性能

要嘗試測量它的執行效果,只需調用一個函數即可一遍又一遍(我想要的功能,實際上什麼也沒做,即「無操作」,但無法弄清楚如何使哈斯克爾執行這樣的建築),我寫了這個:

count 0 = 0 
count x = 1 + count (x-1) 

main = print count 100000000 -- tried various values for this integer. 

我與此相比,它Erlang程序:

count(0) -> 0 ; 
count(I) -> 1 + count(I-1) 

令我驚訝的是,這兩個程序的Erlang程序運行速度更快。事實上,(至少在表面上)比這個更糟糕,因爲即使是遍歷x元素列表的Erlang變體程序(相對於簡單地調用函數x次),運行速度也比上面的Haskell版本快。另外,我正在使用Haskell代碼的編譯版本(ghc --make -O3 -rtsopts)與Erlang的字節碼解釋器(無Hipe)。

我沒有(現在仍然沒有)足夠了解Haskell知道從哪裏開始,但我的第一個猜測是懷疑懶惰。一些在線文檔的快速審查後,我改變主要爲以下內容:

main = print $! count 100000000 

這似乎在一定程度上加快步伐,但Erlang的版本仍然較快,在任何情況下,我不知道我是否做了足夠嚴格,以有足夠的效果,是否還可以做得更多,我是否找錯了樹,還有一些其他的問題,等等。

基於一切我讀過這麼多年來,我相信哈斯克爾編譯對於大多數「通用任務」,通常應該比Erlang更快,但是,進入併發的次數越多,不會越多。任何人都可以看到這些結果嗎? 「棚光」可以重寫我的程序,使用不同的編譯標誌,解釋一些事情等。

編輯:我做了兩件事情,兩個都有加速Haskell程序的效果。我做的第一件事就是這種類型的信息添加到函數:

count :: Int -> Int 

這把哈斯克爾版本的性能就在二郎版本的。我做的第二件事是刪除一個加法:

count 0 = 0 
count x = count (x-1) 

引起哈斯克爾版本打敗二郎神版本(爲了公平我調整了二郎版本還);但是我不知道爲什麼消除一個加法會產生這種效果,因爲我不相信Erlang是一個數學計算野獸。我還想知道Haskell的編譯器是否與其懶惰相結合,只是繞過所有函數調用並直接跳到答案。

+3

注意您在'print $!'中強制執行嚴格規定! ......沒有任何意義,因爲爲了印刷,必須先評估一些東西。 – Sarah

回答

4

您的第一個版本count與第二個版本(總是返回0的版本)之間的一個重要區別是後者是尾遞歸。你的第一個功能的尾遞歸版本將

count' :: Int -> Int 
count' n = go n 0 where 
    go 0 !acc = acc 
    go n !acc = go (n - 1) (acc + 1) 

這需要不到三分之一的時間。

函數簽名在這裏很重要,因爲沒有它,GHC將函數類型默認爲Integer -> Integer,這意味着它必須在任意精度整數而不是Int s上工作。

(這是完全可能的,GHC優化您的第二個版本只返回一個常量,但我沒有考慮這一點。在尾遞歸本身將作出顯著的性能差異,雖然)。

+0

我想我不知道在Haskell中看到什麼樣的尾遞歸。程序的第一個版本應該是Erlang和我曾經工作的其他語言(Prolog等)的尾遞歸。我必須承認我不明白你寫的Haskell代碼,因此我有更多的研究要做。謝謝! – user1992634

+3

我真的不明白你的第一個版本是如何以任何語言遞歸的。一個函數是尾遞歸的,如果它所做的最後一件事就是調用它自己。您的版本在調用自己之後進行添加。 – fjh

+0

哎呀 - 你完全正確。應該可能改爲如下所示:count(1 + x-1) – user1992634