2

私は C++ AMP でアルゴリズムを書き直していますが、アトミックな書き込み、より具体的にはatomic_fetch_addの問題に遭遇しました。これはどうやら整数専用ですか?

アトミックな方法で double_4 (または必要に応じて float_4) を追加する必要があります。C++ AMP のアトミックでそれを達成するにはどうすればよいですか?

私のコードが書き込みを制御するために使用できるロック変数を持つことは、本当に最善/唯一の解決策ですか? 実際には、出力 double の長いリストに対してアトミックな書き込みを行う必要があるため、基本的にすべての出力に対してロックが必要になります。

パフォーマンスを向上させるためにこれをタイル化することを既に検討しましたが、今は最初の繰り返しに過ぎません。

編集:すでに与えられた素早い回答に感謝します。ただし、質問を簡単に更新します。

次のロックを試みましたが、ワープ内の 1 つのスレッドがロックを通過すると、同じワープ内の他のすべてのスレッドがタグ付けされるようです。最初のワープスレッドがロックを取得することを期待していましたが、何かが欠けているに違いありません (cuda の時代からかなりの年月が経過しているため、私はばかになってしまったことに注意してください)。

parallel_for_each(attracting.extent, [=](index<1> idx) restrict(amp)
{
   .....
   for (int j = 0; j < attracted.extent.size(); j++)
   {
      ...
      int lock = 0; //the expected lock value
      while (!atomic_compare_exchange(&locks[j], &lock, 1));
      //when one warp thread gets the lock, ALL threads continue on
      ...
      acceleration[j] += ...; //locked write
      locks[j] = 0; //leaving the lock again
   }
});

最初は共有変数に書き込み、タイル内のすべてのスレッドが完了した後にのみグローバル メモリに書き込む必要があるため、それほど大きな問題ではありませんが、この動作がわかりません。

4

3 に答える 3

2

すべてのアトミック add ops は整数型専用です。float_4 の場合でも、128 ビットの CAS (比較とスワップ) 操作を使用して、ロックなしで必要なことを実行できます (これは 4 つの浮動小数点数であると想定しています)。あなたがしなければならないことは、メモリからfloat_4をアトミックに読み取り、通常の方法でfloatの追加を実行し、CASを使用して値が元の値であるかどうかをテストおよび交換するループを作成することです(そうでない場合はループ、つまり他のスレッド読み取りと書き込みの間で値を変更しました)。128 ビット CAS は 64 ビット アーキテクチャでのみ使用可能であり、データを適切に配置する必要があることに注意してください。

于 2014-06-20T12:22:09.093 に答える
1

重要なコードが短い場合は、アトミック操作を使用して独自のロックを作成できます。

int lock = 1;

while(__sync_lock_test_and_set(&lock, 0) == 0) // trying to acquire lock
{
 //yield the thread or go to sleep
} 

//critical section, do the work

// release lock
lock = 1;

利点は、OS ロックのオーバーヘッドを節約できることです。

于 2014-06-20T12:31:47.937 に答える
0

質問は他の人によって回答されており、その答えは、二重のアトミックを自分で処理する必要があるということです。ライブラリにはそのための機能はありません。

また、他の人が同じ失敗したロックでここに来る場合に備えて、私自身の編集について詳しく説明したいと思います。

次の例では、交換が失敗したときに実際に期待値が変更されたことを認識していなかったことが私の誤りです。したがって、最初のスレッドはロックがゼロであることを期待し、それに 1 を書き込みます。次のスレッドは 0 を期待し、1 の書き込みに失敗しますが、交換は期待値を保持する変数に 1 を書き込みます。これは、次にスレッドが交換を試みたときに、ロックに 1 が必要であることを意味します! これを取得すると、ロックを取得したと考えられます。

&lock が exchange マッチの失敗で 1 を受け取るとは、まったく知りませんでした。

parallel_for_each(attracting.extent, [=](index<1> idx) restrict(amp)
{
   .....
   for (int j = 0; j < attracted.extent.size(); j++)
   {
      ...
      int lock = 0; //the expected lock value

      **//note that, if locks[j]!=lock then lock=1
      //meaning that ACE will be true the next time if locks[j]==1
      //meaning the while will terminate even though someone else has the lock**
      while (!atomic_compare_exchange(&locks[j], &lock, 1));
      //when one warp thread gets the lock, ALL threads continue on
      ...
      acceleration[j] += ...; //locked write
      locks[j] = 0; //leaving the lock again
   }
});

修正が行われるようです

parallel_for_each(attracting.extent, [=](index<1> idx) restrict(amp)
{
   .....
   for (int j = 0; j < attracted.extent.size(); j++)
   {
      ...
      int lock = 0; //the expected lock value

      while (!atomic_compare_exchange(&locks[j], &lock, 1))
      {
          lock=0; //reset the expected value
      };
      //when one warp thread gets the lock, ALL threads continue on
      ...
      acceleration[j] += ...; //locked write
      locks[j] = 0; //leaving the lock again
   }
});
于 2014-06-20T16:22:17.033 に答える