6

Haskellプログラムにマルチスレッド機能を追加しようとした後、パフォーマンスがまったく向上していないことに気づきました。それを追いかけて、私はスレッドスコープから次のデータを取得しました:

グラフ1 緑は実行中を示し、オレンジはガベージコレクションを示します。 グラフ2 グラフ3 ここで、垂直の緑色のバーはスパークの作成を示し、青色のバーは並列GC要求を示し、水色のバーはスレッドの作成を示します。 グラフ4 ラベルは、スパークの作成、並列GCの要求、スレッドnの作成、およびキャップ2からのスパークの盗用です。

平均して、4コアで約25%のアクティビティしか得られていません。これは、シングルスレッドプログラムではまったく改善されていません。

もちろん、実際のプログラムの説明がなければ、質問は無効になります。基本的に、トラバース可能なデータ構造(ツリーなど)を作成し、その上に関数をfmapしてから、画像書き込みルーチンにフィードします(15秒を過ぎたプログラム実行の最後に明確にシングルスレッドのセグメントを説明します) 。関数の構築とfmappingはどちらも実行にかなりの時間がかかりますが、2番目の方が少し時間がかかります。

上記のグラフは、画像の書き込みによって消費される前に、そのデータ構造にparTraversable戦略を追加することによって作成されました。また、データ構造でtoListを使用してから、さまざまな並列リスト戦略(parList、parListChunk、parBuffer)を使用してみましたが、結果は、さまざまなパラメーター(大きなチャンクを使用した場合でも)で毎回同様でした。
また、関数をfmapする前に、トラバース可能なデータ構造を完全に評価しようとしましたが、まったく同じ問題が発生しました。

以下にいくつかの追加の統計を示します(同じプログラムの別の実行に対する):

   5,702,829,756 bytes allocated in the heap
     385,998,024 bytes copied during GC
      55,819,120 bytes maximum residency (8 sample(s))
       1,392,044 bytes maximum slop
             133 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause 
  Gen  0     10379 colls, 10378 par    5.20s    1.40s     0.0001s    0.0327s
  Gen  1         8 colls,     8 par    1.01s    0.25s     0.0319s    0.0509s

  Parallel GC work balance: 1.24 (96361163 / 77659897, ideal 4)

                        MUT time (elapsed)       GC time  (elapsed)
  Task  0 (worker) :    0.00s    ( 15.92s)       0.02s    (  0.02s)
  Task  1 (worker) :    0.27s    ( 14.00s)       1.86s    (  1.94s)
  Task  2 (bound)  :   14.24s    ( 14.30s)       1.61s    (  1.64s)
  Task  3 (worker) :    0.00s    ( 15.94s)       0.00s    (  0.00s)
  Task  4 (worker) :    0.25s    ( 14.00s)       1.66s    (  1.93s)
  Task  5 (worker) :    0.27s    ( 14.09s)       1.69s    (  1.84s)

  SPARKS: 595854 (595854 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time   15.67s  ( 14.28s elapsed)
  GC      time    6.22s  (  1.66s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time   21.89s  ( 15.94s elapsed)

  Alloc rate    363,769,460 bytes per MUT second

  Productivity  71.6% of total user, 98.4% of total elapsed

回答を支援するために他にどのような有用な情報を提供できるかわかりません。プロファイリングは興味深いものを何も明らかにしません。上記から予想されるように、追加されたIDLEが75%の時間を占めることを除いて、シングルコア統計と同じです。

有用な並列化を妨げているのは何が起こっているのでしょうか?

4

1 に答える 1

4

回答者を支援するためのコードをタイムリーに提供できなかったことをお詫び申し上げます。問題の正確な場所を解明するのに少し時間がかかりました。

問題は次のとおりでした:私は関数をfmapしていました

f :: a -> S b

トラバース可能なデータ構造上

structure :: T a

ここで、SとTは2つのトラバース可能なファンクターです。

次に、parTraversableを使用しているときに、誤って書いていました

Compose (fmap f structure) `using` parTraversable rdeepseq

それ以外の

Compose $ fmap f structure `using` parTraversable rdeepseq

そのため、Compose TSのTraversableインスタンスを誤って使用してマルチスレッドを実行していました(Data.Functor.Composeを使用)。

(これは簡単に見つけられるはずだったようですが、コードから上記の間違いを抽出するのに時間がかかりました!)

これは今でははるかに良く見えます!

グラフ1

グラフ2

于 2012-11-26T07:05:31.457 に答える