2012-02-17 10 views
8

runhaskellの下でプログラムを実行しているときに、パフォーマンス異常が発生していることを理解しようとしています。Runhaskellパフォーマンス異常

問題のプログラムは次のとおりです。私はこれを実行すると、それは1.18秒を

isFactor n = (0 ==) . (mod n) 
factors x = filter (isFactor x) [2..x] 
main = putStrLn $ show $ sum $ factors 10000000 

かかります。

しかし、私はisFactorとして再定義した場合:

isFactor n f = (0 ==) (mod n f) 

は、プログラムは17.7秒かかります。

これはパフォーマンスに大きな違いがあり、私はプログラムが同等であることを期待しています。誰かが私がここで行方不明を知っていますか?

注:これはGHCでコンパイルした場合は発生しません。

+1

私の推測では、runhaskellはほんのわずかな最適化を実行するので、2番目のものは特定の厳密性の問題があります。 – fuz

答えて

9

機能は同じである必要がありますが、適用方法に違いがあります。最初の定義では、isFactorがコールサイトisFactor xで完全に適用されます。 2番目の定義では、isFactorは明示的に2つの引数を取るため、そうではありません。

-O0 -ddump-simplでコンパイルした場合、最適化を行わないと、少なくともghc-7.2.1と違いがあることがGHCによって確認され、両方で同じコードが作成されます。 、他のバージョンのYMMV)。

最初のisFactorでは、GHCは、 "GHC.List.Filter"に述語として渡される単一の関数を作成し、mod 10000000(==)をインラインで呼び出します。 2番目の定義では、代わりにisFactorの呼び出しのほとんどがクラス関数へのレットバインド参照であり、isFactorという複数の呼び出しでは共有されません。したがって、完全に不要な多くの辞書オーバーヘッドがあります。

これはほとんど問題ではありません。デフォルトのコンパイラ設定でもそれを最適化するためですが、runhaskellはあまり効果がないようです。それでも、someFunが部分的に適用されることを知っているため、時折構造化されたコードsomeFun x y = \z ->がありました。これは呼び出し間の共有を維持する唯一の方法です(つまり、GHCのオプティマイザは十分に巧妙ではありませんでした)。

5

私が理解しているように、runhaskellはほとんど最適化を行いません。コードをすばやくロードして実行するように設計されています。最適化をさらに進めた場合、コードの実行に時間がかかります。もちろん、この場合、コードは最適化により高速に実行されます。

私が理解しているように、コードのコンパイル済みバージョンが存在する場合、runhaskellがそれを使用します。パフォーマンスが重要な場合は、まず最適化をオンにしてコンパイルしてください。 (runhaskellにスイッチを渡して最適化を有効にすることもできると思います)。

+0

答えをありがとう!私はrunhaskellが多くの最適化を行っていないことを理解していますが、私の頭の中では、 'foo x = bar x'と' foo = bar'はまったく同じコードを生成すると思います。それが私の混乱の原因です。最適化がなくても、私はなぜこれらの2つが異なる動作をするのかを理解しようとしています。 – Steve

+1

私は、関数を定義する方法によってサンクがどのように構造化されているかに若干の違いがあると考えています。 1つのバージョンのようなものはすべての呼び出しで1つのサンクを共有し、もう1つは呼び出しごとにコピーを作成し、より多くの割り当てと割り当て解除を行います。それはとにかく私の_guess_だろう。あなたはGHCの開発者に "本当の"理由を尋ねなければなりません。明らかに、最適化をオンにすると、両方の方法で同じコードが生成されるはずです。最適化パスがなければ、それは起こりません。 – MathematicalOrchid

+0

インタプリタはボックス化されていない値をサポートしていないので、ghciとrunhaskell AFAIKの両方で、すべての最適化がオフになっています。 – fuz