8

でプログラムを実行したときに観察されるパフォーマンス異常を理解しようとしていrunhaskellます。

問題のプログラムは次のとおりです。

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

これを実行すると、1.18 秒かかります。

ただし、次のように再定義isFactorすると:

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

その場合、プログラムは 17.7 秒かかります。

これはパフォーマンスの大きな違いであり、プログラムは同等であると期待しています。ここで何が欠けているか知っている人はいますか?

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

4

2 に答える 2

9

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

最小限の最適化でも、GHC がこれを見抜いて両方の同一のコードを作成するのに十分ですが-O0 -ddump-simpl、最適化なしでコンパイルすると、これが違いを生むことがわかります (少なくとも ghc-7.2.1 では、YMMV では他のバージョンで)。

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

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

于 2012-02-17T12:48:53.627 に答える
5

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

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

于 2012-02-17T09:23:20.587 に答える