2011-06-12 6 views
8

大きなデータ(〜250 MB〜1 GB)で動作するアルゴリズムを実装しています。このために、ベンチマークを行うためのループが必要でした。しかし、このプロセスでは、F#がいくつかの厄介なことをしていることを知っています。あなたの一部が明確にできることを願っています。ここでF#コンパイラは死んだオブジェクトを生かしておく

は私のコードは、(問題の記述は以下である)である:ここでは

open System 

for i = 1 to 10 do 
    Array2D.zeroCreate 10000 10000 |> ignore  
    printfn "%d" (GC.GetTotalMemory(true)) 

Array2D.zeroCreate 10000 10000 |> ignore 
// should force a garbage collection, and GC.Collect() doesn't help either 
printfn "%d" (GC.GetTotalMemory(true)) 
Array2D.zeroCreate 10000 10000 |> ignore  
printfn "%d" (GC.GetTotalMemory(true)) 
Array2D.zeroCreate 10000 10000 |> ignore  
printfn "%d" (GC.GetTotalMemory(true)) 
Array2D.zeroCreate 10000 10000 |> ignore  
printfn "%d" (GC.GetTotalMemory(true)) 

Console.ReadLine() |> ignore 

出力は以下のようになります:ループF#で、だから、

54000 
54000 
54000 
54000 
54000 
54000 
54000 
54000 
54000 
54000 
400000000 
800000000 
1200000000 

Out of memory exception 

結果を破棄しますが、とき私はループの中にいません。F#は "死んだデータ"へ​​の参照を保持します(私はILを見てきましたが、明らかにクラスのプログラムはこのデータのフィールドを取得します)。どうして?それを修正できますか?

このコードは、Visual Studioおよびリリースモード以外で実行されます。

答えて

17

この動作の理由は、F#コンパイラがローカルスコープとは異なるグローバルスコープで動作するためです。グローバルスコープで宣言された変数は静的フィールドに変換されます。モジュール宣言は、フィールド/プロパティ/メソッドとしてコンパイルされたlet宣言を持つ静的クラスです。

let main() =  
    Array2D.zeroCreate 10000 10000 |> ignore  
    printfn "%d" (GC.GetTotalMemory(true)) 
    Array2D.zeroCreate 10000 10000 |> ignore  
    printfn "%d" (GC.GetTotalMemory(true)) 
    // (...) 
    Console.ReadLine() |> ignore 

main() 

...しかし、コンパイラは、あなたが値を使用していないときにフィールドを宣言しない理由だけignoreそれ:

問題を解決する最も簡単な方法は、関数の中で、あなたのコードを記述するのですか?これはかなり興味深いものです。ignore関数は非常に単純な関数で、使用するとインライン展開されます。宣言はlet inline ignore _ =()です。関数をインライン化すると、コンパイラは(関数の引数を格納するための)いくつかの変数を宣言します。だから、この問題を解決する別の方法はignoreを省略して書くことです

Array2D.zeroCreate 10000 10000 
printfn "%d" (GC.GetTotalMemory(true)) 
Array2D.zeroCreate 10000 10000 
printfn "%d" (GC.GetTotalMemory(true)) 
// (...) 

式の結果はunitではありませんので、あなたは、いくつかのコンパイラの警告を取得しますが、それは動作します。しかし、いくつかの関数を使用し、ローカルスコープでコードを書くことはおそらくより信頼性が高いです。

+0

+1ありがとう、面白い:)最初の解決策は動作しますが、無視して無視するとここでは役立ちません。私はまだそれが何をするのかを調べることにはまだ興味があります。 –

+1

興味深い...最適化を有効にするために '-O'オプションで試してみました。 –

+0

奇妙な。私がVisual Studioの外でそれを実行したときも、ここでもうまくいきました。しかし、今度はループがメモリ不足例外を発生させましたが、最初に "non-loop"バージョンを実行した場合のみ:/私はローカルスコープに固執するでしょう。 –

関連する問題