0

cudaで行列乗算をしようとしています。私の実装は cuda の例とは異なります。

cuda の例 (cuda サンプルから) は、最初の行列の行の各値を 2 番目の行列の列の各値で乗算し、積を合計して、出力ベクトルのインデックスのインデックスに格納することにより、行列の乗算を実行します。最初の行列からの行。

私の実装では、最初の行列の列の各値に 2 番目の行列の行の単一の値を乗算します。ここで、行インデックス = 列インデックスです。次に、グローバル メモリに出力ベクトルがあり、その各インデックスが更新されます。

cuda の実装例では、単一のスレッドで出力ベクトルの各インデックスを更新できますが、私の実装では複数のスレッドで各インデックスを更新できます。

得られた結果には、一部の値のみが表示されます。たとえば、更新を 4 回繰り返しても、2 回または 1 回しか実行されません。

スレッドはすべてグローバルメモリ内のベクトルの同じインデックスに書き込もうとしているため、スレッドが互いに干渉している可能性があると思います。では、1 つのスレッドがインデックスに書き込みを行っている間に、もう 1 つのスレッドがその値を挿入してインデックスを更新できないのではないでしょうか?

この評価に意味があるのか​​疑問です。

例えば。次の 2 つの行列を乗算するには、次のようにします。

[3 0 0 2         [1       [a
 3 0 0 2    x     2   =    b
 3 0 0 0          3        c
 0 1 1 0]         4]       d]

Cuda サンプルは、a、b、c、d がグローバル メモリに格納されている 4 つのスレッドを使用して、次の方法で行列乗算を実行します。

Thread 0:   3*1 + 0*2 + 0*3 + 2*4 = a
Thread 1:   3*1 + 0*2 + 0*3 + 2*4 = b
Thread 2:   3*1 + 0*2 + 0*3 + 0*4 = c
Thread 3:   0*1 + 1*2 + 1*3 + 0*4 = d

私の実装は次のようになります。

a = b = c = d = 0

Thread 0:
3*1 += a
3*1 += b
3*1 += c
0*1 += d

Thread 1:
0*2 += a
0*2 += b
0*2 += c
1*2 += d

Thread 2:
0*3 += a
0*3 += b
0*3 += c
1*3 += d

Thread 3:
2*4 += a
2*4 += b
0*4 += c
0*4 += d

したがって、一度に 4 つのスレッドすべてがインデックスの 1 つを更新しようとする可能性があります。

4

1 に答える 1

1

この問題を修正するために、atomicAddを使用して+=操作を実行しました。スレッドが操作3*1 + = aを実行する場合(たとえば)、3つのことを実行します。

  1. 以前の値を取得します
  2. 3 *1+以前の値を実行して値を更新します
  3. 次に、新しい値をに保存します

atomAddを使用することにより、これらの操作が他のスレッドからの中断なしにスレッドによって実行できることが保証されます。atomAddが使用されていない場合、thread0はaの以前の値を取得でき、thread0が値を更新している間に、thread1はaの以前の値を取得して、独自の更新を実行できます。このように、スレッドは操作を終了できないため、 +=操作は発生しません。

atomAdd (&a、3 * 1)の代わりに+ = 3 * 1が使用されている場合、thread0が実行を終了する前に、thread1が干渉してthread0の値を変更する可能性があります。競合状態を作成します。

atomAdd+=操作です。次のコードを使用して操作を実行します。

__global__ void kernel(){    
int a = 0;   
atomicAdd(&a, 3*1);  //is the same as a += 3*1
}
于 2012-12-28T14:49:20.143 に答える