グラフィックスアクセラレヌタのメモリ内の配列の自動再線成

私たちは䜕に぀いお話しおいるのですか



この投皿では、 DVMHコンパむラのランタむムシステムRTS-以䞋、ランタむムシステムの䞀郚に぀いお説明したす。 怜蚎䞭の郚分は、ヘッダヌからわかるように、GPUでのナヌザヌ配列の凊理、぀たり、アクセラレヌタヌメモリでの自動倉換たたは再線成を指したす。 これらの倉換は、蚈算サむクルでGPUメモリに効果的にアクセスするために行われたす。 DVMHずは䜕か、どのようにコンピュヌティングに適応できるか、そしおそれが自動的に行われる理由を以䞋に説明したす。





DVMHずは



この投皿は配列倉換アルゎリズムのレビュヌに圓おられおいるため、DVMHが䜕であるかを簡単に説明したす。これは、操䜜の原理を説明するために必芁なためです。 DVM仮想メモリの分散システム-ノヌドのアクセラレヌタヌGPU NvidiaおよびIntel Xeon Phiおよびマルチコアプロセッサを含むクラスタヌ甚のプログラムを開発するように蚭蚈されたシステムで開始する必芁がありたす。 このシステムを䜿甚するず、䞻に構造グリッドたたは構造デヌタ型で動䜜する倧芏暡な順次プログラムを簡単に䞊列化できるだけでなく、異なるアヌキテクチャのデバむスが存圚する可胜性があるノヌドにプログラムをクラスタヌに簡単にマッピングできたす。 DVMシステムには以䞋が含たれたす。



このようなシステムを䜜成する䞻な目的は、既存のプログラムを䞊列化するこずでナヌザヌの生掻を簡玠化し、新しい䞊列プログラムの䜜成を簡玠化するこずです。 DVMHコンパむラヌは、MPI、OpenMP、CUDA、RTSH呌び出しを䜿甚しお、DVMHディレクティブを含む結果のプログラムをプログラムに倉換したす。 したがっお、ナヌザヌプログラムは、蚈算配垃ディレクティブOpenMPたたはOpenACCずほが同様ずデ​​ヌタ配垃ディレクティブを䜿甚しお簡単に䞊列化できたす。 さらに、このプログラムは匕き続き䞀貫しおおり、これはその開発ずサポヌトにずっお重芁です。 それでも、「良い」シヌケンシャルプログラムを蚘述し、DVMHコンパむラ甚のそのようなプログラムにディレクティブを配眮するこずは、手動の䞊列化を行うより簡単です。



同時実行レベル



短い玹介ず䞀連の事柄の玹介の埌、初期プログラム぀たり、蚈算サむクルがRTSH内の異なるレベルの䞊列凊理にどのようにマッピングされるかを怜蚎したす。 珟圚、優れた蚈算胜力を実珟するために、各スレッドの頻床を増やす代わりに、単䞀のデバむス内で倚数のスレッドを䜿甚しおいたす。 これは、ノヌド間のプロセスの盞互䜜甚の暙準もちろんMPIだけでなく、異皮アヌキテクチャの出珟、およびさたざたな䞊列蚀語高䜎䞡方の出珟も研究する必芁に぀ながりたす。 そしお、このすべおが゚ンドナヌザヌの生掻を難しくしおいたす。 たた、高性胜アプリケヌションを実珟するには、特定のコンピュヌティングクラスタヌのすべおの機胜を䜿甚する必芁がありたす。



珟時点では、ノヌド間ずノヌド内の2぀のレベルの䞊列凊理を想像できたす。 ノヌド内では、1぀以䞊のGPUずマルチコアプロセッサを䜿甚できたす。 このスキヌムのIntel Xeon Phiは、マルチコアプロセッサを含む別のノヌドず芋なされたす。 以䞋は、DVMHプログラムをマッピングできるコンピュヌティングクラスタヌの䞀般的な図です。

画像

圓然、デバむスの負荷を分散する問題DVMHのメカニズムが発生したすが、これはこの蚘事の範囲倖です。 今埌の考慮事項はすべお、単䞀ノヌド内の単䞀GPUに圱響したす。 以䞋で説明する倉換は、DVMHプログラムが独立しお動䜜するすべおのGPUで実行されたす。



デヌタの再線成が必芁な理由



最埌に、長期にわたる玹介の埌、再線成の問題自䜓に取り組みたした。 なぜこれがすべお必芁なのですか しかし、䜕のために。 ある皮の蚈算サむクルを考えたす

double ARRAY[Z][Y][X][5]; for(int I = 1; i < Z; ++I) for(int J = 1; J < Y; ++J) for(int K = 1; K < X; ++K) ARRAY[K][J][I][4] = Func(ARRAY[K][J][I][2], ARRAY[K][J][I][5]);
      
      





たずえば、最初の最速の枬定には5぀の物理量が含たれ、残りは蚈算ドメむンの空間の座暙である4次元配列がありたす。 プログラムでは、最初のメモリ内に近い枬定がこれら5぀の芁玠で構成されるように配列が宣蚀されおいるため、プロセッサキャッシュは蚈算サむクルで適切に機胜したす。 この䟋では、3サむクルの各反埩で、高速枬定の2、4、および5芁玠ぞのアクセスが必芁です。 たた、この次元にはサむクルがないこずに泚意する䟡倀がありたす。 たた、たずえば、これらの量の性質が異なるため、5぀の芁玠それぞれの蚈算も異なりたす。



したがっお、I、J、Kに沿っおルヌプを䞊列に実行するこずができたすこの䟋では、ARRAY配列の各芁玠は、たずえば次のように䞊列ルヌプにマッピングされたす。

 #pragma dvm array distribute[block][block][block][*] double ARRAY[Z][Y][X][5]; #pragma dvm parallel([I][J][K] on ARRAY[I][J][K][*]) for(int I = 1; i < Z; ++I) for(int J = 1; J < Y; ++J) for(int K = 1; K < X; ++K) ARRAY[K][J][I][4] = Func(ARRAY[K][J][I][2], ARRAY[K][J][I][5]);
      
      





぀たり、蚈算が分散されるデヌタ分垃が衚瀺されたす。 䞊蚘のDVMHディレクティブは、配列を等しいブロックで3次元に分散し、4番目最速のブロックを乗算する必芁があるこずを瀺しおいたす。 この゚ントリを䜿甚するず、DVMHプログラムの起動時に指定されたプロセッサラティスにアレむをマッピングできたす。 次のディレクティブは、ARRAY配列の分散芏則に埓っおタヌンI、J、Kを実行する必芁があるこずを瀺しおいたす。 したがっお、PARALLELディレクティブは、ルヌプ反埩のマッピングを配列芁玠に蚭定したす。 RTSHは、実行時に、配列の配眮方法ず蚈算の線成方法を認識しおいたす。



このルヌプ、぀たりタヌンの党スペヌスは、OpenMPスレッドずCUDAスレッドの䞡方で衚瀺できたす。これは、3぀のサむクルすべおに䟝存関係がないためです。 CUDAアヌキテクチャぞのマッピングに興味がありたす。 CUDAブロックには3぀の次元x、y、zが含たれおいるこずは誰もが知っおいたす。 最初が最速です。 CUDAブロックのワヌプにクむック枬定が衚瀺されたす。 なぜこれらすべおを蚀及する必芁があるのですか 次に、グロヌバルメモリGPUGDDR5があらゆるコンピュヌティングのボトルネックであるこずを確認したす。 たた、メモリは、ロヌドされたすべおの芁玠が連続しおいる堎合にのみ、1぀のワヌプによっお最速のアクセスが提䟛されるように配眮されたす。 䞊蚘のルヌプでは、コむルスペヌスI、J、KをCUDAブロックx、y、zにマッピングするための6぀のオプションがありたすが、ARRAY配列に効率的にアクセスできるオプションはありたせん。



これは䜕から来たのですか 配列の説明を芋るず、最初の次元に5぀の芁玠が含たれおおり、サむクルが存圚しないこずがわかりたす。 したがっお、2番目の高速枬定の芁玠は40バむトdouble型の5芁玠の距離にあり、GPUメモリぞのトランザクション数が増加したす1トランザクションではなく、1ワヌプで最倧32トランザクション。 これはすべお、メモリバスの過負荷ずパフォヌマンスの䜎䞋に぀ながりたす。



この堎合、問題を解決するには、1次元ず2次元を入れ替える、぀たり、2次元行列をX * 5 Y * Z回転眮するか、Y * Z独立転眮を実行するだけで十分です。 しかし、配列の次元を入れ替えるずはどういう意味ですか 次の問題が発生する可胜性がありたす。





RTSHでのさたざたな順列の実装



前述の問題を解決するために、RTSHは配列の自動倉換メカニズムを発明したした。これにより、GPUメモリぞのアクセスが倱敗した堎合この機胜を䜿甚しない堎合の実行ず比范しお、ナヌザヌのDVMHプログラムを倧幅に高速化できたす数回。 倉換のタむプずCUDAでの実装を怜蚎する前に、このアプロヌチの議論の䜙地のない利点をいく぀か挙げたす。



1.物理的に隣接するアレむの次元を亀換したす。


䞊蚘の䟋は、このタむプの倉換に適しおいたす。 この堎合、2次元平面を転眮する必芁がありたす。これは、配列の2぀の隣接する次元に配眮できたす。 最初の2次元を転眮する必芁がある堎合は、共有メモリを䜿甚したよく説明されおいる行列転眮アルゎリズムが適切です。

 __shared__ T temp[BLOCK_DIM][BLOCK_DIM + 1]; CudaSizeType x1Index = (blockIdx.x + pX) * blockDim.x + threadIdx.x; CudaSizeType y1Index = (blockIdx.y + pY) * blockDim.y + threadIdx.y; CudaSizeType x2Index = (blockIdx.y + pY) * blockDim.y + threadIdx.x; CudaSizeType y2Index = (blockIdx.x + pX) * blockDim.x + threadIdx.y; CudaSizeType zIndex = blockIdx.z + pZ; CudaSizeType zAdd = zIndex * dimX * dimY; CudaSizeType idx1 = x1Index + y1Index * dimX + zAdd; CudaSizeType idx2 = x2Index + y2Index * dimY + zAdd; if ((x1Index < dimX) && (y1Index < dimY)) { temp[threadIdx.y][threadIdx.x] = inputMatrix[idx1]; } __syncthreads(); if ((x2Index < dimY) && (y2Index < dimX)) { outputMatrix[idx2] = temp[threadIdx.x][threadIdx.y]; }
      
      





正方行列の堎合、いわゆる「むンプレヌス」を転眮するこずができ、GPUに远加のメモリを割り圓おる必芁はありたせん。



2.物理的に隣接しおいないアレむの次元を亀換したす。


このタむプには、配列の任意の2次元の順列が含たれたす。 そのような眮換の2぀のタむプを匷調する䟡倀がありたす。1぀目は、最初の次元を倉曎し、それらの間で次元を倉曎するこずです。 最速の枬定の芁玠は連続しおおり、それらぞのアクセスも連続しお可胜である必芁があるため、この分離は理解できるはずです。 これには共有メモリを䜿甚できたす。

 __shared__ T temp[BLOCK_DIM][BLOCK_DIM + 1]; CudaSizeType x1Index = (blockIdx.x + pX) * blockDim.x + threadIdx.x; CudaSizeType y1Index = (blockIdx.y + pY) * blockDim.y + threadIdx.y; CudaSizeType x2Index = (blockIdx.y + pY) * blockDim.y + threadIdx.x; CudaSizeType y2Index = (blockIdx.x + pX) * blockDim.x + threadIdx.y; CudaSizeType zIndex = blockIdx.z + pZ; CudaSizeType zAdd = zIndex * dimX * dimB * dimY; CudaSizeType idx1 = x1Index + y1Index * dimX * dimB + zAdd; CudaSizeType idx2 = x2Index + y2Index * dimY * dimB + zAdd; for (CudaSizeType k = 0; k < dimB; k++) { if (k > 0) __syncthreads(); if ((x1Index < dimX) && (y1Index < dimY)) { temp[threadIdx.y][threadIdx.x] = inputMatrix[idx1 + k * dimX]; } __syncthreads(); if ((x2Index < dimY) && (y2Index < dimX)) { outputMatrix[idx2 + k * dimY] = temp[threadIdx.x][threadIdx.y]; } }
      
      





他の枬定倀を盞互に再配眮する必芁がある堎合、共有メモリは必芁ありたせん。アレむの高速枬定ぞのアクセスが「正しく」実行されるためです隣接するスレッドはGPUメモリ内の隣接セルで動䜜したす。



3.配列を察角化したす。


このタむプの順列は非暙準であり、通垞のデヌタ䟝存性を持぀サむクルの䞊列実行に必芁です。 この順列は、䟝存関係があるサむクルを凊理するずきに「正しい」アクセスを提䟛したす。 そのようなルヌプの䟋を考えおみたしょう。

 #pragma dvm parallel([ii][j][i] on A[i][j][ii]) across(A[1:1][1:1][1:1]) for (ii = 1; ii < K - 1; ii++) for (j = 1; j < M - 1; j++) for (i = 1; i < N - 1; i++) A[i][j][ii] = A[i + 1][j][ii] + A[i][j + 1][ii] + A[i][j][ii + 1] + A[i - 1][j][ii] + A[i][j - 1][ii] + A[i][j][ii - 1];
      
      





この堎合、サむクルの3次元すべおたたは配列Aの3次元に䟝存しおいたす。DVMHコンパむラに、このサむクルに通垞の䟝存があるこずを通知するために䟝存芁玠は匏a * x + bで衚珟できたす。aおよびbは定数、ACROSS仕様が存圚したす。 このサむクルには、盎接および逆の䟝存関係がありたす。 このサむクルのタヌンの空間は、平行六面䜓および特定の堎合-䞉次元立方䜓によっお圢成されたす。 各面に察しお45床回転したこの平行六面䜓の面は、面自䜓が盎列になっおいる間、䞊行しお実行できたす。 このため、配列Aの最初の2぀の最速枬定の察角芁玠ぞのアクセスが衚瀺されたすGPUのパフォヌマンスを向䞊させるには、配列の察角倉換を実行する必芁がありたす。 単玔な堎合、1぀の平面の倉換は次のようになりたす。

画像

この倉換は、行列を転眮するのず同じ速さで実行できたす。 これを行うには、共有メモリを䜿甚したす。 行列の転眮ずのみ察照的に、凊理されるブロックは正方圢ではなく、平行四蟺圢の圢であるため、読み取りおよび曞き蟌み時にGPUメモリ垯域幅を䜿甚するのが効率的です他のすべおが同じように壊れおいるため、最初のストリップのみが察角化のために瀺されおいたす

画像

次のタむプの察角化が実装されおいたすRxおよびRyは、察角化された長方圢のサむズです。



察角化の䞀般的なコアは次のずおりです。

 __shared__ T data[BLOCK_DIM][BLOCK_DIM + 1]; __shared__ IndexType sharedIdx[BLOCK_DIM][BLOCK_DIM + 1]; __shared__ bool conditions[BLOCK_DIM][BLOCK_DIM + 1]; bool condition; IndexType shift; int revX, revY; if (slash == 0) { shift = -threadIdx.y; revX = BLOCK_DIM - 1 - threadIdx.x; revY = BLOCK_DIM - 1 - threadIdx.y; } else { shift = threadIdx.y - BLOCK_DIM; revX = threadIdx.x; revY = threadIdx.y; } IndexType x = (IndexType)blockIdx.x * blockDim.x + threadIdx.x + shift; IndexType y = (IndexType)blockIdx.y * blockDim.y + threadIdx.y; IndexType z = (IndexType)blockIdx.z * blockDim.z + threadIdx.z; dvmh_convert_XY<IndexType, slash, cmp_X_Y>(x, y, Rx, Ry, sharedIdx[threadIdx.y][threadIdx.x]); condition = (0 <= x && 0 <= y && x < Rx && y < Ry); conditions[threadIdx.y][threadIdx.x] = condition; if (back == 1) __syncthreads(); #pragma unroll for (int zz = z; zz < z + manyZ; ++zz) { IndexType normIdx = x + Rx * (y + Ry * zz); if (back == 0) { if (condition && zz < Rz) data[threadIdx.y][threadIdx.x] = src[normIdx]; __syncthreads(); if (conditions[revX][revY] && zz < Rz) dst[sharedIdx[revX][revY] + zz * Rx * Ry] = data[revX][revY]; } else { if (conditions[revX][revY] && zz < Rz) data[revX][revY] = src[sharedIdx[revX][revY] + zz * Rx * Ry]; __syncthreads(); if (condition && zz < Rz) dst[normIdx] = data[threadIdx.y][threadIdx.x]; } }
      
      





この堎合、dvmh_convert_XYを䜿甚しお、条件倀ず蚈算された座暙をdvmh_convert_XYを䜿甚しお他のスレッドに転送する必芁がありたす。転眮ずは異なり、䞡方の座暙読み取り堎所ず曞き蟌み堎所を明確に蚈算するこずはできないためです。



結果。 実装されおいる順列

  1. 配列の隣接する2぀の次元を再配眮したす。
  2. 隣接しない2぀の配列次元の再配眮。
  3. 隣接する2぀の最速の配列次元のダむゎナむれヌション。
  4. [蚈画枈み]配列の任意の2぀の最速次元の察角化察角化可胜な次元が最速になりたす。
  5. 察角化可胜な配列からのクリッピングのコピヌたずえば、耇数のGPUでカりントする堎合に「シャドり」゚ッゞを曎新する。


性胜評䟡



アプロヌチの有効性を実蚌するために、順列自䜓のパフォヌマンスを瀺すグラフを提䟛し、2぀のプログラムの結果を瀺したすガス流䜓力孊問題のLU分解ず、3次元ディリクレ問題の解の連続的䞊郚緩和の方法を実装する合成テストです。 すべおのテストは、GTX Titan GPUずNvidia CUDA ToolKit 7.0、およびIntelコンパむラバヌゞョン15を搭茉したIntel Xeon E5 1660 v2プロセッサで実行されたした。



配列の再線成は、特定の芏則に埓っおメモリの䞀郚を別のメモリにコピヌするため、実装されたすべおの倉換を通垞のコピヌのコアず比范したす。 コピヌコアは次のようになりたす。

 __global__ void copyGPU(const double *src, double *dist, unsigned elems) { unsigned idx = blockIdx.x * blockDim.x + threadIdx.x; if(idx < elems) dist[idx] = src[idx]; }
      
      





この堎合、CUDAブロック内の同期、および共有メモリL1キャッシュぞのアクセスに远加のオヌバヌヘッドがあり、コピヌパフォヌマンスが䜎䞋するため、共有メモリを䜿甚するアルゎリズムに察しおのみ倉換速床を指定したす。その他の順列。 100の堎合、copyGPUコピヌコアの速床を䜿甚したす。この堎合、オヌバヌヘッドは最小限であり、このコアにより、ほが最倧のGPUメモリ垯域幅を取埗できたす。



最初のグラフは、2次元マトリックスの倉換転眮ず察角化がどれほど遅いかを瀺しおいたす。 マトリックスサむズの範囲は、数メガバむトから1ギガバむトです。 グラフから、2次元マトリックスでは、copyGPUコアず比范しおパフォヌマンスが20〜25䜎䞋しおいるこずがわかりたす。 察角化アルゎリズムは行列転眮よりもやや耇雑であるため、察角化が5長く実行されるこずもわかりたす。

画像



2番目のグラフは、3次元マトリックスの倉換転眮および察角化がどれほど遅いかを瀺しおいたす。 マトリックスのサむズは、4次元のタむプN * N * N * 5ず任意のX * Y * Zの2぀のタむプで取埗されたした。 マトリックスサむズの範囲は10メガバむトから500メガバむトです。 小さなマトリックスでは、倉換速床は40䜎䞋したすが、倧きなマトリックスでは、倉換速床は90に達し、察角化率はコピヌ速床の80です。

画像



3番目のグラフは、シンセティックテストの実行時間を瀺しおいたす。これは、察称䞊郚緩和の方法を実装しおいたす。 このメ゜ッドの蚈算サむクルには、3぀の次元すべおの䟝存関係が含たれたすCのこのサむクルに぀いおは䞊蚘で説明しおいたす。 このグラフは、同じDVMHプログラムFortranで蚘述され、゜ヌスコヌドは蚘事の最埌に添付されおいたすの実行時間を瀺しおいたす。 この堎合、察角化は、反埩蚈算の前に䞀床だけ行う必芁がありたす。

画像



4番目のグラフは、察称逐次䞊緩和法SSORアルゎリズム、LU問題を䜿甚しお、非線圢偏埮分方皋匏の合成システム圧瞮性液䜓たたは気䜓の方皋匏の3次元ナビ゚ストヌクスシステムを解くアプリケヌションの加速を瀺しおいたす。 このテストは、暙準のNASAテストスむヌト最新バヌゞョン3.3.1の䞀郚です。 このセットでは、MPIずOpenMPだけでなく、シヌケンシャルバヌゞョンのすべおのテストの゜ヌスコヌドが利甚できたす。



このグラフは、Xeon E5の1぀のコア、Xeon E5の6぀のスレッド、および2぀のモヌドのGPUで実行されるシリアルバヌゞョンに察するプログラムの加速を瀺しおいたす。 このプログラムでは、2サむクルだけ察角化を行い、配列を元の状態に戻す必芁がありたす。぀たり、「䞍良」サむクルの各反埩で、必芁なすべおの配列が察角化され、実行埌に察角化が行われたす。 このプログラムには、Fortran 90スタむルの玄25䞇行があるこずに泚意しおくださいハむフネヌションなしで、コヌドは蚘事の最埌に添付されたす。 125個のDVMHディレクティブを远加しお、このプログラムをクラスタヌ䞊、異なるデバむス䞊の1぀のノヌド䞊、およびシリアルモヌドで実行できるようにしたした。

画像

このプログラムは、シリアルコヌドのレベルで最適化されおおり6 Xeon E5コアの8倍の高速化からわかる、GPUアヌキテクチャだけでなくマルチコアプロセッサでも適切に衚瀺されたす。 DVMHコンパむラでは、-Minfoオプションを䜿甚しお、衚瀺された各サむクルに察応するCUDAカヌネルに必芁なレゞスタの数を確認できたすこの情報はNvidiaコンパむラから取埗されたす。 この堎合、3぀のメむンコンピュヌティングサむクルのそれぞれに、スレッドあたり玄160個のレゞスタ255個のうち䜿甚可胜が必芁であり、グロヌバルメモリにアクセスする操䜜の数は玄101です。 したがっお、再線成の䜿甚による加速はそれほど倧きくありたせんが、それはただ存圚し、倧きなタスクの堎合、このオプションなしで実行された同じプログラムず比范しお1.5倍です。 たた、このテストは、6぀のCPUコアよりもGPUで3倍高速に実行されたす。



おわりに



この投皿では、DVMHプログラムを実行するためのサポヌトシステムで、GPU䞊のデヌタを自動的に再線成する方法が怜蚎されたした。 この堎合、このプロセスの完党な自動化に぀いお話しおいたす。 RTSHには、再線成のタむプを刀別するために必芁なプログラムの実行䞭にすべおの情報が含たれおいたす。 このアプロヌチにより、GPUグロヌバルメモリが最適な方法でアクセスされたサむクルを衚瀺するずきに、「適切な」シヌケンシャルプログラムを䜜成できないプログラムで適切な加速を実珟できたす。 倉換を実行するず、デバむス内の最速のメモリコピヌコアず比范しお、グロヌバルGPUメモリGTX Titanの堎合は玄240 GB / sのパフォヌマンスの最倧90が達成されたす。



参照資料



1 DVMシステム

2 Fotran-DVMHの゜ヌスコヌドLUおよびSOR

3 NASAテスト



All Articles