2011-10-10 7 views
25

私はmyConcatconcatの私の独自のバージョンを定義します。GHCインタプリタでスペースリークが発生するのは、concat !! nは

module Eh where 

myConcat []   = [] 
myConcat ([]:os)  = myConcat os 
myConcat ((x:xs):os) = x : myConcat (xs:os) 

(!!!) :: [a] -> Int -> a 
xs  !!! n | n < 0 = error "negative index" 
[]  !!! _   = error "index too large" 
(x:_) !!! 0   = x 
(_:xs) !!! n   = xs !!! (n-1) 

私はGHCインタプリタでmyConcat <some huge list> !! nをすれば、それは300メガバイト/秒で私の記憶を盗む、と私はそれがOOMを召喚することができます前に、それを殺さなければなりませんキラー。ここでは、私はEhを "解釈済み"として読み込みます。読み込む前にコンパイルしません。

 
code run in the GHC interpreter  space leak? 
myConcat (repeat [1,2,3,4]) !! (10^8) Yes 
concat (repeat [1,2,3,4]) !! (10^8) No 
myConcat (repeat [1,2,3,4]) !!! (10^8) No 
concat (repeat [1,2,3,4]) !!! (10^8) No 

は、今私はEhghc --make -O2 Eh.hs)をコンパイルした場合、その後、インタプリタでそれをロードし、それらのどれもスペースリーク、これらのテストを再実行します。インタプリタで実行するのではなく、各テストケースをコンパイルするのと同じです。

何が起こっているのですか?


私はGHC 6.12.3を実行しています。

+0

ghciのどのバージョンを使用していますか?私はマシン上で4つすべてのケースで一定の記憶を持っています。私はghc '7.0.3'かそれに似ています。 – fuz

+0

私は6.12.3を実行しています。テストをありがとう! 6.12.3は古くなっていますか? –

+2

それほど... – fuz

答えて

1

ここでの問題は厳密さです。ハスケルでの評価は厳密ではないので、計算は通常、その結果が本当に必要な場合にのみ実行されます。代わりに、実行されていない計算を表すいわゆるサンクが作成されます。

しかしながら、コンパイラは、とにかく計算の結果が必要となることを検出することができるため、実際の計算によってサンクの作成を置き換えます。

Haskell Wikiはおそらくこれをよりよく説明します。

myConcat機能を修正するには、手動で厳密な評価を強制して何百万というサンクを作成しないようにする必要があります。

myConcat []   = [] 
myConcat ([]:os)  = myConcat os 
myConcat ((x:xs):os) = ((:) $! x) myConcat (xs:os) 
+0

私はそれがここの問題だとは思わない。リークはghc-6.12でのみ発生し、解釈されたコードでのみ発生します。それがちょうど厳格であった場合、他のバージョンや(最適化されていない)コンパイルされたコードでもリークが発生するはずです。 6.12のバイトコードジェネレータのコーナーケースのバグに似ています。 –

+0

おそらく、私のバージョンの 'myConcat'はなぜ動作しますか?唯一の変更は厳しい適用です。 – bseibold

+0

もちろん、怠惰には部分があり、厳密な厳密な注釈を導入することで、漏れを避けることができます。私は、通常、ghciで生成されたバイトコードは、元の定義をうまく扱うため、6.12のバイトコードジェネレータの問題ではなく、リークポイントの表現が、定義そのもの。 –

関連する問題