2016-05-31 2 views
-1

私は反復関数を反復型に変換してからParallel.ForEachを使用しましたが、VTuneで実行しているときに実行時間の大部分で2つの論理コアしか使用していませんでした。Parallel.ForEachからマルチスレッドへの移行

私が代わりに管理し、スレッドを使用しようとすることを決定し、このコードを変換:この中へ

for (int N = 2; N <= length; N <<= 1) 
{ 
    int maxThreads = 4; 
    var workGroup = Enumerable.Range(0, maxThreads); 

    Parallel.ForEach(workGroup, i => 
    { 
     for (int j = ((i/maxThreads) * length); j < (((i + 1)/maxThreads) * length); j += N) 
     { 
      for (int k = 0; k < N/2; k++) 
      { 
       int evenIndex = j + k; 
       int oddIndex = j + k + (N/2); 

       var even = output[evenIndex]; 
       var odd = output[oddIndex]; 

       output[evenIndex] = even + odd * twiddles[k * (length/N)]; 
       output[oddIndex] = even + odd * twiddles[(k + (N/2)) * (length/N)]; 
      } 
     } 
    }); 
} 

for (int N = 2; N <= length; N <<= 1) 
{ 
    int maxThreads = 4; 

    Thread one = new Thread(() => calculateChunk(0, maxThreads, length, N, output)); 
    Thread two = new Thread(() => calculateChunk(1, maxThreads, length, N, output)); 
    Thread three = new Thread(() => calculateChunk(2, maxThreads, length, N, output)); 
    Thread four = new Thread(() => calculateChunk(3, maxThreads, length, N, output)); 

    one.Start(); 
    two.Start(); 
    three.Start(); 
    four.Start(); 
} 

public void calculateChunk(int i, int maxThreads, int length, int N, Complex[] output) 
{ 
    for (int j = ((i/maxThreads) * length); j < (((i + 1)/maxThreads) * length); j += N) 
    { 
     for (int k = 0; k < N/2; k++) 
     { 
      int evenIndex = j + k; 
      int oddIndex = j + k + (N/2); 
      var even = output[evenIndex]; 
      var odd = output[oddIndex]; 

      output[evenIndex] = even + odd * twiddles[k * (length/N)]; 
      output[oddIndex] = even + odd * twiddles[(k + (N/2)) * (length/N)]; 
     } 
    } 
} 

問題はNの最後の反復の4番目のスレッドでありますループ私は、インデックスは、length相当のアクセスしようとしている出力配列の範囲外の例外を取得します。

私はデバッグを使用して原因を特定できませんが、スレッドと関係があると思います。スレッドなしでコードを実行し、意図したとおりに動作しました。

コードのいずれかを変更する必要がある場合は、私は通常、編集を推奨する人が少数です。あなたの助けてくれてありがとう、私はそれを自分自身でソートしようとしていて、スレッドの問題で問題が発生していることをかなり確信していますが、

PS:意図した目的は、このコードセグメントを並列化することです。

+1

[Parallel.ForEach](https://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach(v = vs.110).aspx)には多くのオーバーロードがあります。オプションで並列度を制御します。 – davidshen84

+1

'Parallel.ForEach'を内側のループから元のスニペットの外側のループに移動するだけで、目的の結果を達成することができます(もちろん' workGroup'よりも多くの要素があると思われます)。これは 'Parallel.ForEach'のセットアップとティアダウンのコストを減らし、ロードバランサの仕事をより良くすることを可能にし、Nコアまで拡大することを期待しています。スレッドに固執している場合、私は 'Join'sをどこかに見ることを期待しています。そうしないと、前のバッチが終了する前に各ループの繰り返しでスレッドがますます多くなり始めます。 –

+0

アルゴリズムは正しいですか?私の理解のところでは、 'for(int j =((i/maxThreads)* length); j <(((i + 1)/ maxThreads)* length); j + = N) [0、maxThreads-1]の範囲では、int j =((i/maxThreads)* length'は常に0になります(これは整数除算です)/maxThreads)* length) '最後のものを除いてすべての' i'値に対して 'false'になるので、最後に内部ループは何スレッドで使用されても1回だけ入力されます – qbik

答えて

1

観測された動作は、ほとんどの場合、取得されたループ反復変数Nの使用によるものです。私は簡単なテストで自分の状況を再現することができます簡単に言えば

ConcurrentBag<int> numbers = new ConcurrentBag<int>(); 

for (int i = 0; i < 10000; i++) 
{ 
    Thread t = new Thread(() => numbers.Add(i)); 

    t.Start(); 
    //t.Join(); // Uncomment this to get expected behaviour. 
} 

// You'd not expect this assert to be true, but most of the time it will be. 
Assert.True(numbers.Contains(10000)); 

、あなたのforループはNの値がcalculateChunk呼び出しを実行デリゲートでコピーすることができます前に、Nをインクリメントするためにレースをされています。その結果、calculateChunkNのランダム値がlength <<= 1に達していることを確認しています。これがあなたの原因です。IndexOutOfRangeException

出力値はNという値に頼ることができないので、ゴミ箱になります。

安全により多くのコアを使用するように元のコードを書き換える場合は、Parallel.ForEachを内側ループから外側ループに移動します。外側のループ反復の回数が多い場合、ロードバランサは適切にジョブを実行できます(現時点ではworkGroupが4であり、要素数が少なすぎます)。

+1

** safe **を重視するために豚を重ねるだけです。このコードはTPL( 'Parallel.ForEach')上に簡単に置かなければなりません。未加工の「糸」に走ることは危険なNIHです。 –

+0

Nの各インクリメントは、以前の反復からの計算データを使用するので、パラレル化できません。データ依存性:( –

+0

@OliverGiess、今理解しています。以前の結果をスキャンする必要があるために面白い並列化の問題がありました。これはプロデューサ - コンシューマのアプローチと障壁でどのように解決できるのか分かりますが、並列化によるパフォーマンス向上が同期オーバーヘッドよりも大きいとは確信していません。 –

関連する問題