SPECIALIZE
プラグマをいつ使用するか、どのようなパフォーマンスが得られるかなど。
(型クラス) ポリモーフィック関数がある場合、コンパイラに関数を特化させ、クラスの 1 つまたはいくつかのインスタンスで頻繁に呼び出されることを期待します。
特殊化により、使用されている辞書検索が削除され、多くの場合、さらなる最適化が可能になります。クラス メンバー関数は、多くの場合インライン化でき、厳密性分析の対象となります。どちらも潜在的に大きなパフォーマンスの向上をもたらします。可能な唯一の最適化が辞書ルックアップの排除である場合、一般に利益は大きくありません。
{-# INLINABLE #-}
GHC-7 の時点では、関数にプラグマを与える方がおそらくより便利です。これにより、(ほとんど変更されず、正規化と脱糖が実行されます) ソースがインターフェイス ファイルで利用できるようになります。呼び出しサイト。
使用する場所RULES
。発砲しない特定のルールについて人々が取っていると聞きましたか? どうやってそれを確認しますか?
-ddump-rule-firings
コマンド ライン オプションを使用して、どのルールが実行されたかを確認できます。これは通常、多数の起動済みルールをダンプするため、独自のルールを少し検索する必要があります。
ルールを使用します
特殊なタイプの関数のより効率的なバージョンがある場合、たとえば
{-# RULES
"realToFrac/Float->Double" realToFrac = float2Double
#-}
一部の関数をより効率的な特別な引数のバージョンに置き換えることができる場合。
{-# RULES
"^2/Int" forall x. x ^ (2 :: Int) = let u = x in u*u
"^3/Int" forall x. x ^ (3 :: Int) = let u = x in u*u*u
"^4/Int" forall x. x ^ (4 :: Int) = let u = x in u*u*u*u
"^5/Int" forall x. x ^ (5 :: Int) = let u = x in u*u*u*u*u
"^2/Integer" forall x. x ^ (2 :: Integer) = let u = x in u*u
"^3/Integer" forall x. x ^ (3 :: Integer) = let u = x in u*u*u
"^4/Integer" forall x. x ^ (4 :: Integer) = let u = x in u*u*u*u
"^5/Integer" forall x. x ^ (5 :: Integer) = let u = x in u*u*u*u*u
#-}
一般的な法則に従って式を書き直すと、最適化したほうがよいコードが生成される場合があります。
{-# RULES
"map/map" forall f g. (map f) . (map g) = map (f . g)
#-}
RULES
後者のスタイルでの の広範な使用は、フュージョン フレームワーク (text
ライブラリなど) で行われます。 のリスト関数ではbase
、ルールを使用して異なる種類のフュージョン (foldr/build
フュージョン) が実装されます。
関数の引数を厳密にするのはいつで、それが役立つのはいつですか? 引数を厳密にすると、引数が通常の形式に評価されることを理解していますが、すべての関数引数に厳密性を追加しないのはなぜですか? どうやって決めるの?
引数を strict にすると、通常の形式ではなく、弱いヘッドの通常の形式に評価されることが保証されます。
すべての引数を厳密にするわけではありません。一部の関数は、一部の引数が完全に機能するために非厳密でなければならず、一部の関数はすべての引数が厳密であると効率が低下するためです。
たとえば、 partition
無限リストで機能するには、2 番目の引数が厳密でない必要があります。より一般的には、無限リストで機能するために、使用されるすべての関数foldr
が 2 番目の引数で厳密でない必要があります。有限リストでは、2 番目の引数に非正格関数を指定すると、劇的に効率が向上します ( foldr (&&) True (False:replicate (10^9) True)
)。
とにかく価値のある作業を行う前に引数を評価する必要があることがわかっている場合は、引数を厳密にします。多くの場合、GHC の厳密性アナライザーは単独でそれを行うことができますが、もちろんすべてではありません。
非常に典型的なケースは、ループまたは末尾再帰のアキュムレータです。厳密性を追加すると、途中で巨大なサンクが構築されなくなります。
どこに厳密性を追加するかについての厳密なルールはわかりません。私にとっては経験の問題です.
経験則として、小さなデータ ( など) を評価し続けることは理にかなっていますInt
が、例外もあります。
プログラムにスペース リークがあることを確認するにはどうすればよいですか? スペースリークを構成する一般的なパターンは何ですか?
最初のステップは、+RTS -s
オプションを使用することです (プログラムが rtsopts を有効にしてリンクされている場合)。これは、全体でどれだけのメモリが使用されたかを示しており、多くの場合、それによってリークがあるかどうかを判断できます。オプションを指定してプログラムを実行すると、より有益な出力が得られ+RTS -hT
ます。これにより、スペース リークの特定に役立つヒープ プロファイルが生成されます (また、プログラムを有効な rtsopts とリンクする必要があります)。
さらに分析が必要な場合は、プロファイリングを有効にしてプログラムをコンパイルする必要があります (-rtsops -prof -fprof-auto
古い GHC では、この-fprof-auto
オプションは使用できませんでした。この-prof-auto-all
オプションは最も近い対応です)。
次に、さまざまなプロファイリング オプションを指定して実行し、生成されたヒープ プロファイルを確認します。
スペース リークの最も一般的な 2 つの原因は次のとおりです。
3 番目はおそらく不要な共有によって占められています。GHC は一般的な部分式の削除をほとんど行いませんが、不要な場所でも長いリストを共有することがあります。
リークの原因を見つけるための厳密なルールはありません。また、ある場所に厳密性を追加したり、別の場所に遅延性を追加したりすることで、リークを修正できる場合もあります。
あまりにも怠惰で問題があるかどうかを確認するにはどうすればよいですか? ヒープ プロファイリングはいつでも確認できますが、怠惰が害を及ぼす一般的な原因、例、およびパターンを知りたいですか?
一般に、遅延は、結果を段階的に構築できる場合に必要とされ、処理が完了する前に結果の一部を配信できない場合には望ましくありません。たとえば、左折畳みや末尾再帰関数などです。