2011-12-20 9 views
27

これは少しばかりではあるかもしれませんが、私はそれをしばらく疑問に思っています。データ型の厳密なフィールドのメリット

data Foo = Bar !Int !Float 

私はしばしば怠惰は素晴らしいことだと考えている:!と私の知る限りでは、人は必ず値が構築される前にデータコンストラクタのパラメータが評価されていることができます。さて、私がソースを調べると、厳密なフィールドが!のないよりも頻繁に表示されます。

これのメリットは何ですか?なぜ、私はそれをそのまま怠けてはいけないのですか?

答えて

32

IntおよびFloatフィールドに大きな計算を格納していない限り、かなりのオーバーヘッドは、サンクに蓄積される多くの簡単な計算から増やすことができます。たとえば、データ型の怠惰なFloatフィールドに1を繰り返し追加すると、実際にフィールドを強制計算して計算するまで、より多くのメモリが消費されます。

しばしば、高価な計算をフィールドに保存したいとします。しかし、あらかじめそのようなことをしていないことが分かっている場合は、フィールドを厳密にマークして、手動でseqを追加しなくても、効率を上げる必要はありません。フラグが与えられたとき、追加のボーナスとして

は、 -funbox-strict-fields GHCは、直接、彼らが常に評価される知っているので可能であるデータ・タイプ自体に厳密なフィールドのデータ型のを解凍し、従ってないサンクをしなければなりません割り当てられる。この場合、Bar値は、データを含むサンクに対する2つのポインタを含むのではなく、メモリ内のBar値の中にIntおよびFloatを直接含む機械語を含むことになる。

怠惰は非常に有用なことですが、時には、ちょうど途中に入り込んで計算を妨げます。特に、常に見られる(したがって強制される)小さなフィールドや、非常に高価な計算。厳密なフィールドは、データ型のすべての用途を変更することなく、これらの問題を克服するのに役立ちます。

これはレイジーフィールドよりも一般的であるかどうかは、読んでいるコードのタイプによって異なります。機能的なツリー構造が怠惰から大きく恩恵を受けるため、厳密なフィールドを広く使用することはありません。

あなたが中置演算のためのコンストラクタを持つASTを持っているとしましょう:

data Exp = Infix Op Exp Exp 
     | ... 

data Op = Add | Subtract | Multiply | Divide 

そのようにポリシーを適用すると全体ASTが評価されていることを意味するだろうとあなたは、Expフィールドが厳密にしたいではないでしょうあなたが怠惰から恩恵を受けたいと思っているものではない、トップレベルのノードを見るたびに。しかし、Opフィールドには、後で延期したい高価な計算は含まれません。また、実際に深くネストされた解析ツリーがある場合は、中断演算子ごとのサンクのオーバーヘッドが高価になる可能性があります。したがって、インフィクスコンストラクタでは、Opフィールドを厳密にしたいが、2つのフィールドは怠惰なままにしておきたい。

単一コンストラクタタイプのみをアンパックすることができます。

+1

ASTのように厳密にする必要はありませんか? – Lanbo

+0

私はASTを例にしていくつかの精緻化をして私の答えを広げました。 – ehird

4

レイジーに関連するオーバーヘッドがあります。コンパイラは、結果が必要になるまで計算を保存する値のサンクを作成する必要があります。遅かれ早かれ結果が常に必要であることがわかっている場合は、結果を強制的に評価することができます。

4

レイジーはコストがかかります。そうでない場合はすべての言語がそれになります。

コストは2倍である:それはサンクを設定するために時間がかかることがあり

  1. (すなわち、最終的に計算されようとしているときに計算する必要がある内容の説明)の操作を行うことよりも、直ちに。
  2. 他のサンクに厳密な引数として渡される評価されていないサンクは、他のサンクなどに厳密な引数として渡されると、ますます多くのメモリを使用します。残念ながら、それらのチューンは、もはやアクセスできないメモリへの参照、すなわちサンクのみが評価されるときに解放されるメモリを保持し、ガーベジコレクタがその作業を行うことを妨げる可能性がある。例としては、ツリー内の特定の値を更新するはずのサンクがあります。この値が100MB相当の他の値を保持しているとします。古いツリーへの参照がもう存在しない場合、サンクが評価されない限り、このメモリは無駄になります。他の回答によって提供される情報に加えて
5

、心に留めておく:

!と私の知る限りは、値が構築される前に、人は必ずデータコンストラクタのパラメータが評価されていることができます

それはどのようにdeep the parameter is evaluatedを見て面白い - それはseqWHNFに評価さ$!とのようなものです。

WHNFに評価式

let x' = IntFoo $ 1 + 2 + 3 
in x' 

IntFoo 6(==完全に評価し、== NF)を生成するデータ型

data Foo = IntFoo !Int | FooFoo !Foo | BarFoo !Bar 
data Bar = IntBar Int 

考えます。
また、この式WHNFに評価

let x' = FooFoo $ IntFoo $ 1 + 2 + 3 
in x' 

FooFoo (IntFoo 6)(==完全に評価し、== NF)を生成します。
しかし、WHNFに評価され、この式

let x' = BarFoo $ IntBar $ 1 + 2 + 3 
in x' 

は(!=完全に評価、!= NF)値BarFoo (IntBar (1 + 2 + 3))を生成します。

主なポイント:Barのデータコンストラクタは、厳密なパラメータ自体を含んでいない場合!Barパラメータの厳しさが必ずしも助けにはなりません。

+0

非常に明確な例! ghciやその他のツールでパラメータの評価の深さを調べることはできますか? – wenlong

+1

@wenlongもう一つの[サンクスの可視化に関するSOの答え]を参照してください(http://stackoverflow.com/questions/16767028/anyway-to-visualize-a-thunk-function-or-how-to-view-a-function -for-a-general)、特にツール['ghc-vis'](http://felsin9.de/nnis/ghc-vis/#basic-usage) – mucaho

関連する問題