web-dev-qa-db-ja.com

CUDAでは、メモリの合体​​とは何ですか?

CUDAグローバルメモリトランザクションの「結合」とは何ですか? CUDAガイドを読んでも理解できませんでした。どうやってするの? CUDAプログラミングガイドマトリックスの例では、行ごとにマトリックスにアクセスすることを「合体」と呼びます。正しいのはなぜですか?

67
kar

この情報は、計算能力1.xまたはcuda 2.0にのみ適用される可能性があります。最新のアーキテクチャおよびcuda 3.0には、より洗練されたグローバルメモリアクセスがあり、実際、これらのチップの「合体グローバルロード」もプロファイルされていません。

また、このロジックを共有メモリに適用して、バンクの競合を回避できます。


合体メモリトランザクションは、ハーフワープ内のすべてのスレッドが同時にグローバルメモリにアクセスするトランザクションです。これは単純すぎますが、正しい方法は、連続したスレッドが連続したメモリアドレスにアクセスすることです。

したがって、スレッド0、1、2、および3がグローバルメモリ0x0、0x4、0x8、および0xcを読み取る場合、合体読み取りである必要があります。

マトリックスの例では、マトリックスをメモリ内に直線的に配置することに注意してください。必要に応じてこれを行うことができ、メモリアクセスはマトリックスのレイアウトを反映する必要があります。したがって、以下の3x4マトリックス

0 1 2 3
4 5 6 7
8 9 a b

このように、行ごとに実行できます。そのため、(r、c)はメモリ(r * 4 + c)にマップされます。

0 1 2 3 4 5 6 7 8 9 a b

要素に1回アクセスする必要があり、4つのスレッドがあるとします。どのスレッドがどの要素に使用されますか?おそらくどちらか

thread 0:  0, 1, 2
thread 1:  3, 4, 5
thread 2:  6, 7, 8
thread 3:  9, a, b

または

thread 0:  0, 4, 8
thread 1:  1, 5, 9
thread 2:  2, 6, a
thread 3:  3, 7, b

どちらが良いですか?どちらが合体した読み取りをもたらすのか、そしてどちらはそうではないのか?

いずれにしても、各スレッドは3回アクセスします。最初のアクセスを見て、スレッドが連続してメモリにアクセスするかどうかを確認しましょう。最初のオプションでは、最初のアクセスは0、3、6、9です。連続しておらず、合体していません。 2番目のオプションは、0、1、2、3です。連続!合体した!わーい!

最善の方法は、おそらくカーネルを作成し、それをプロファイルして、結合されていないグローバルなロードとストアがあるかどうかを確認することです。

135
jmilloy

メモリ結合は、グローバルメモリ帯域幅の最適な使用を可能にする手法です。つまり、同じ命令を実行する並列スレッドがグローバルメモリ内の連続した場所にアクセスする場合、最も好ましいアクセスパターンが実現されます。

enter image description here

上の図の例は、合体した配置の説明に役立ちます。

図(a)では、n長さのベクトルmは線形に格納されています。ベクトルの要素ijvで示されます j。 GPUカーネルの各スレッドは、1つのm-lengthベクトルに割り当てられます。 CUDAのスレッドはブロックの配列にグループ化され、GPUのすべてのスレッドは_indx=bd*bx+tx_として定義できる一意のIDを持ちます。ここで、bdはブロックの次元を表し、bxはブロックを表しますindexおよびtxは各ブロックのスレッドインデックスです。

垂直矢印は、並列スレッドが各ベクトルの最初のコンポーネント、つまりメモリのアドレス0、m2m ...にアクセスする場合を示しています。図(a)に示すように、この場合、メモリアクセスは連続的ではありません。これらのアドレス間のギャップをゼロにすることにより(上の図に示されている赤い矢印)、メモリアクセスが合体します。

ただし、GPUブロックごとの常駐スレッドの許容サイズがbdに制限されているため、ここでは問題が少し複雑になります。したがって、最初のbdベクトルの最初の要素を連続した順序で格納し、その後に2番目のbdベクトルの最初の要素を格納するなどして、合体データ配置を行うことができます。図(b)に示すように、残りのベクター要素は同様の方法で保存されます。 n(ベクトルの数)がbdの因子ではない場合、最後のブロックの残りのデータに何らかの些細な値を埋め込む必要があります。 0。

図(a)の線形データストレージでは、成分i(0≤i <m)のベクトルindx(0≤indx <n)は、_m × indx +i_によってアドレス指定されます。図(b)の合体されたストレージパターンの同じコンポーネントは、

_(m × bd) ixC + bd × ixB + ixA_、

ここで、ixC = floor[(m.indx + j )/(m.bd)]= bx、_ixB = j_およびixA = mod(indx,bd) = tx

要約すると、サイズmで多数のベクトルを格納する例では、線形インデックス付けは、以下に従って合体インデックス付けにマッピングされます。

_m.indx +i −→ m.bd.bx +i .bd +tx_

このデータの再配置により、GPUグローバルメモリのメモリ帯域幅が大幅に増加する可能性があります。


出典:「非線形有限要素変形解析におけるGPUベースの計算の高速化。」生物医学工学における数値的手法に関する国際ジャーナル(2013)。

10
ramino

ブロック内のスレッドが連続したグローバルメモリの場所にアクセスしている場合、すべてのアクセスはハードウェアによって単一の要求に結合されます(または合体します)。マトリックスの例では、行のマトリックス要素は線形に配置され、その後に次の行が続きます。たとえば、2x2マトリックスとブロック内の2つのスレッドの場合、メモリロケーションは次のように配置されます。

(0,0)(0,1)(1,0)(1,1)

行アクセスでは、スレッド1は(0,0)および(1,0)にアクセスしますが、これらは合体できません。列アクセスでは、thread1は(0,0)および(0,1)にアクセスします。これらは隣接しているため、合体できます。

7
penmatsa

合体の基準は CUDA 3.2プログラミングガイド 、セクションG.3.2に詳しく記載されています。短いバージョンは次のとおりです。ワープ内のスレッドは順番にメモリにアクセスする必要があり、アクセスされるワードは32ビット以上である必要があります。さらに、ワープによってアクセスされるベースアドレスは、32ビット、64ビット、および128ビットアクセスに対して、それぞれ64、128、または256バイトにアライメントされている必要があります。

Tesla2およびFermiハードウェアは、8ビットアクセスと16ビットアクセスを合体させるという大丈夫な仕事をしますが、ピーク帯域幅が必要な場合は避けるのが最善です。

Tesla2およびFermiハードウェアの改善にもかかわらず、合体は廃止されたことに注意してください。 Tesla2またはFermiクラスのハードウェアでさえ、グローバルメモリトランザクションの合体に失敗すると、パフォーマンスが2倍になる可能性があります。 (Fermiクラスのハードウェアでは、ECCが有効になっている場合にのみ、これが当てはまるようです。連続するが解放されていないメモリトランザクションは、Fermiで約20%ヒットします。)

2
ArchaeaSoftware