なぜこのようなものがハスケルで非常に遅く走るのですか?なぜ[x | x < - [1..10]]メソッドがHaskellで遅いのですか?
test = [x|a<-[1..100],b<-[1..100],c<-[1..100],d<-[1..100],let x = a]
print $ length test
実行するための唯一の程度10^8
数字がありますが、それは瞬く間に行われるべきであるが、それは永遠に、ほとんど墜落し実行しているように思えます。
なぜこのようなものがハスケルで非常に遅く走るのですか?なぜ[x | x < - [1..10]]メソッドがHaskellで遅いのですか?
test = [x|a<-[1..100],b<-[1..100],c<-[1..100],d<-[1..100],let x = a]
print $ length test
実行するための唯一の程度10^8
数字がありますが、それは瞬く間に行われるべきであるが、それは永遠に、ほとんど墜落し実行しているように思えます。
これはghciまたはコンパイルされたプログラムで実行していますか?それは大きな違いをもたらします。
ghciの場合、ghciは後で使用する場合のために計算された値test
を保持します。通常、これは良い考えですが、この場合はtest
がとにかく再計算するのに安い巨大な値ではありません。どのくらい巨大ですか?初心者は10^8要素のリストです(64ビットシステムでは)、リストあたりの要素数は24バイトなので、すでに2.4Gです。次に、値そのものの領域使用があります。値はすべて[1..100]
から取られていると考えるかもしれないので、それらは共有され、合計で無視できる量のスペースを使用する必要があります。しかし、リスト内の値は実際にはx
という形式になります。a
、b
、c
、d
、length
は、リスト内の値を調べることはありません。したがって、各要素は、a
、b
、c
およびd
を参照するクロージャとして表現されます。これは少なくとも8 *(4 + 1)= 40バイトを要し、合計6.4Gになります。
ガーベッジコレクタは、6.4Gのデータを割り当てたときにガーベッジコレクタがかなり多くのコピーを実行しなければならず、すべてが永続的に生きています。それは実際にはリストやその長さを計算するのではなく、時間がかかります。
あなたがプログラムに
test = [x|a<-[1..100],b<-[1..100],c<-[1..100],d<-[1..100],let x = a]
main = print $ length test
をコンパイルする場合、test
は、その長さは明らかにそれを再び使用する予定はありませんされると、計算されているとして、ライブに保持する必要はありません。だからGCにはほとんど何もすることができず、プログラムは数秒で実行されます(の〜10^8リストノードの割り当てと計算には妥当です)。
あなたはループを10^8回実行しているだけでなく、10^8個の要素でリストを作成しています。 length
を使用しているので、Haskellはリスト全体を実際に評価してその長さを返す必要があります。リストの各要素は1語を取ります。これは32ビットでも64ビットでも構いません。 32ビット(4バイト)という楽観的な仮定では、400 MB(約381.5 MiB)のメモリを割り当てたばかりです。 64ビットの場合、割り当てたメモリは800 MB(約763 MiB)です。あなたのシステム上で何が起こっているかに応じて、スワップファイル/スワップパーティションをヒットさせるには、あまりにも多くのRAMをチャンクに割り当てます。
他の微妙なことが起こっている場合、私はそれらを認識していませんが、メモリ使用量がこれがなぜとても遅いのかについての最初の疑念です。
最終的にこれに割り当てられるメモリの合計量を過小評価している可能性があります。しかし、私はそれが問題だとは本当に思っていません。1GBはあなたに現代のシステムを交換させることはありません。それでも、常に最大で10^6個の要素をメモリ内に保持する必要があります。 GHCの割り当て担当者は、朝食に100万の配分を食べるでしょう。シンプルで短期間実行されているプログラムのプロファイリング出力に何ギガバイトの割り当て/割り当て解除が表示されるかを確認するまで待ちます。 –
間違いありません。私はハスケルの専門家ではありません(私はF#についてもっとよく知っていますし、そこにも私はまだおしゃべりしていません)。 @CYC、Daniel Wagnerの言葉が私に矛盾しているとすれば、私にではなく、彼に聞きなさい。私は主に推測している。 – rmunn
@DanielWagnerなぜなら、いつでも10^6個の要素をメモリに保持するだけで済むでしょうか? – immibis
これは "なぜ"の質問に答えるのではなく、最適化でコンパイルします。私はghciの25を観察します。 '-O0'でコンパイルされたときの20秒。 '-O2'でコンパイルした場合は0.3になります。 –
'let x = a'という代入があるので、最終的な答えが10^8でないのはなぜですか? – Kamel
@Kamel 'let x = a'はすべてのバインドの後にあるので、' x'はまだ10^8回複製されなければなりません。 –