10

私はF#で計算集中的な仕事をしています。 .Net Task Parallel Libraryを使用するArray.Parallel.mapのような関数は、私のコードを指数関数的にスピードアップし、実際には最小限の努力をしています。F#PSeq.iterはすべてのコアを使用していないようです

しかし、メモリの問題から、私はシーケンス式の中で怠惰に評価できるようにコードの部分を作り直しています。それは評価する時間が来たときに私が使用:

// processor and memory intensive task, results are not stored 
let calculations : seq<Calculation> = seq { ...yield one thing at a time... } 

// extract results from calculations for summary data 
PSeq.iter someFuncToExtractResults results 

の代わりに:

​​

私ははっきりと(ギアに私のコンピュータキック上のすべてのコアを見ることができますArray.Parallel機能のいずれかを使用して〜 100%CPU使用率)。ただし、余分なメモリが必要なため、プログラムは終了しませんでした。

私がプログラムを実行すると、PSeq.iterのバージョンでは、約8%のCPU使用率(および最小RAM使用率)しかありません。

So:PSeqのバージョンが非常に遅くなる理由はありますか?それは怠惰な評価のためですか?私は行方不明のいくつかの魔法は "並行する"ものですか?

おかげで、

その他のリソース、両方のソースコードの実装(彼らは.NETの異なるパラレルライブラリを使用するように見える):

https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/array.fs

https://github.com/fsharp/powerpack/blob/master/src/FSharp.PowerPack.Parallel.Seq/pseq.fs

EDIT:さらに追加コード例と詳細の詳細

コード:

  • 配列

    // processor and memory intensive task, results are not stored 
    let calculations : seq<Calculation> = 
        seq { 
         for index in 0..data.length-1 do 
          yield calculationFunc data.[index] 
        } 
    
    // extract results from calculations for summary data (different module) 
    PSeq.iter someFuncToExtractResults results 
    
  • アレイ

    // processor and memory intensive task, storing these results is an unnecessary task 
    let calculations : Calculation[] = 
        Array.Parallel.map calculationFunc data 
    
    // extract results from calculations for summary data (different module) 
    Array.Parallel.map someFuncToExtractResults calculations 
    

詳細:

  • intermediatを記憶しますeアレイバージョンは10分以内に速く実行されますが、クラッシュする前に~70GBのRAMを使用します(64GB物理、残りのページング)
  • seqバージョンは34分を要し、RAMの一部を使用します(約30GB)
  • 私は計算している10億の値があります。したがって、10億倍(それぞれ64bits)= 7.4505806GBです。もっと複雑な形式のデータがあります。不要なコピーがいくつかありますので、現在の大量のRAM使用量を掃除しています。
  • はいアーキテクチャーはあまり良くありません。プログラムの最適化やデータをより小さなチャンクにまとめようとする私の最初の部分は遅延評価です。
  • 小さなデータセットでは、結果。
  • @pad、私はあなたが提案したPSeqを試しました。計算[]を与えたときに正しく動作しているように見えましたが、RAMの問題が残っていました(最終的にはクラッシュしました)
  • コードの要約部分と計算部分の両方がCPU集中です私はちょうどあなたの更新された情報に基づいて、一度
+1

レイジー評価は並列実行ではうまくいきません。公正であるには、同じ 'Calculation [] 'を' PSeq.iter'と 'Array.Parallel.map'に渡します。 'Calculation'と' someFuncToExtractResults'の詳細がなくてもその理由を伝えることは不可能です。 – pad

+0

提案してくれてありがとうございました。私はこれを試しましたが、PSeqはレイジーシーケンスで配列を与えられたときにうまく動作します...しかし、RAMの問題は解決しません。 –

答えて

5

を並列化することを目指し配列のバージョンで

  • )大規模なデータセットの、私はちょうど関連部分への私の答えを短くしています。あなただけの代わりに、あなたが現在持っているもので、これを必要とする:

    let result = data |> PSeq.map (calculationFunc >> someFuncToExtractResults) 
    

    そして、あなたはPSeq.mapArray.Parallel.mapを使用するかどうか、これは同じように動作します。

    しかし、実際の問題は解決されません。この問題は、CPU使用率を100%にするために必要な並列作業に達したときに、プロセスをサポートするための十分なメモリがないと述べることができます。

    これはどのように解決されないのでしょうか。順番に(CPU効率は低いがメモリ効率は)順番に処理することも、並列処理することもできます(CPU効率は上がりますが、メモリが不足します)。

    オプションは、以下のとおりです。

    1. 変更あなたの記憶を爆破しません何かにこれらの機能が使用する並列度:

      let result = data 
            |> PSeq.withDegreeOfParallelism 2 
            |> PSeq.map (calculationFunc >> someFuncToExtractResults) 
      
    2. 変更のための基盤となるロジックcalculationFunc >> someFuncToExtractResultsのように、より効率的な単一の関数であり、結果にデータをストリームします。より詳細なことがわからなければ、これがどのように行われるかを見るのは簡単ではありません。しかし、内部的には、いくつかの遅延ロードが可能かもしれません。

  • +0

    両方とも集中的です 私はあなたのセカンドポイント、あなたは詳細を教えていただけますか? –

    +0

    @AnthonyTruskinger:あなたが提供した追加情報に基づいて重要なアップデートを行いました。アルゴリズムを変更したくない場合は、どこかでトレードオフを選択する必要があります(アルゴリズムを変更せずに100%CPUと効率的なメモリを得ることはできません)。あなたがアルゴリズムを変更することができれば、よく、私の答えを見てください。 – yamen

    3

    Array.Parallel.mapPSeqPLINQ薄いラッパであるボンネットの下Parallel.For使用。しかし、彼らがここで異なって行動する理由は、seq<Calculation>が連続していて新しい結果を生むには遅すぎる場合には、PSeq.iterの作業負荷が不十分であるということです。

    中間のseqまたは配列を使用する考えはありません。

    // Should use PSeq.map to match with Array.Parallel.map 
    PSeq.map (calculationFunc >> someFuncToExtractResults) data 
    

    Array.Parallel.map (calculationFunc >> someFuncToExtractResults) data 
    

    あなたはあまりにも多くのメモリを消費しないようとつながる一箇所に集中的な計算を持っている:1つの場所ですべての計算を移動し、入力配列すべきdataが進むべき道であると仮定並列実行の効率を向上させます。

    関連する問題