組み込みアルゴリズム移植最適化研究ノート6-CUDAプログラミング
参照:
リンクの説明を追加
CUDA(Compute Unified Device Architecture)の中国語名は、ユニファイドコンピューティングデバイスアーキテクチャと呼ばれます。イメージビジョンの分野の学生は多かれ少なかれCUDAにさらされます。結局のところ、パフォーマンスと速度を最適化するために、CUDAは非常に重要なツールです。CUDAはビジョンの学生がバイパスするのが難しいピットです。あなたはそれを踏まなければなりません。実用的であること。CUDAプログラミングは、始めるのが本当に簡単で、習得するのが難しいです。コンピュータアーキテクチャとC言語プログラミングの知識を持つ学生にとって、CUDAプログラミングを始めるのはそれほど難しいことではありません。この記事は、次の5つの側面を通じて、CUDAプログラミングの最も重要な知識ポイントを理解し、すぐに始めるのに役立ちます。
- GPUアーキテクチャの特徴
- CUDAスレッドモデル
- CUDAメモリモデル
- CUDAプログラミングモデル
- CUDAアプリケーションの小さな例
1.GPUアーキテクチャ機能
まず、シリアルコンピューティングとパラレルコンピューティングについて説明しましょう。ハイパフォーマンスコンピューティングの鍵は、並列コンピューティングにマルチコアプロセッサを使用することです。
コンピュータプログラムのタスクを解くとき、私たちの自然な考えは、タスクを一連の小さなタスクに分解し、これらの小さなタスクを1つずつ完了することです。シリアル計算では、プロセッサに一度に1つの計算タスクを処理させ、1つの計算タスクを処理した後、すべての小さなタスクが完了するまで次のタスクを計算し、この大きなプログラムタスクも終了するという考え方です。次の図に示すように、これは、シリアルプログラミングのアイデアを使用して問題を解決する方法のステップです。
しかし、シリアルコンピューティングの欠点は非常に明白です。マルチコアプロセッサがある場合、マルチコアプロセッサを使用して複数のタスクを同時に処理でき、これらの小さなタスクは関連していません(相互に依存する必要はありません)。たとえば、私のコンピューティングタスクは計算結果を使用しません)、なぜシリアルプログラミングを使用するのですか?大規模なタスクの計算速度をさらに高速化するために、いくつかの独立したモジュールを異なるプロセッサに割り当てて同時計算し(これは並列です)、最後にこれらの結果を統合してタスク計算を完了することができます。次の図は、大きなコンピューティングタスクを小さなタスクに分解し、独立した小さなタスクを異なるプロセッサに割り当てて並列コンピューティングを行い、最後にシリアルプログラムを使用して結果を要約し、コンピューティングタスク全体を完了します。
したがって、プログラムが並列計算を実行できるかどうか、重要なのは、プログラムが分割できる実行モジュール、これらの実行モジュールのどれが独立しているか、そしてどの実行モジュールが強い結合に強く依存しているかを分析する必要があるということです。並列コンピューティングを設計し、マルチコアプロセッサの利点を最大限に活用して、コンピューティングタスクをさらに高速化します。強力な結合モジュールでは、シリアルプログラミングを使用し、シリアル+並列プログラミングのアイデアを使用して高性能コンピューティングを完成させます。
次に、CPUとGPUの違いとそれぞれの特徴についてお話します。パラレルコンピューティングとシリアルコンピューティングについて話すときに、「マルチコア」の概念について何度も話しました。それでは、「コア」の観点からこのトピックを始めましょう。 "。。まず、CPUは、順次シリアル処理用に最適化された複数のコアで構成されています。GPUは、複数のタスクを同時に処理するように特別に設計された、より小さく、より効率的な数千のコアで構成されており、並列タスクを効率的に処理できます。つまり、CPUの各コアは非常に高性能ですが、タスクの処理には非常に強力ですが、コアの数が少なく、並列コンピューティングでは十分に機能しません。一方、GPUは、コアあたりの計算能力は強力ではありませんが、利点は、複数のコンピューティングタスクを同時に処理でき、並列コンピューティングをサポートする上で優れた機能を発揮できるコアが多数あることです。
GPUとCPUの異なるハードウェア特性により、アプリケーションシナリオが決まります。CPUはコンピューターの操作と制御の中核であり、GPUは主にグラフィックスと画像処理に使用されます。コンピューターに表示される画像の形式は行列です。画像の処理は実際にはさまざまな行列を操作して計算することであり、多くの行列演算を実際に並列化できるため、画像処理が高速になります。したがって、GPUはグラフィックスの分野にあります。と画像。その筋肉を曲げる機会もあります。次の図は、マルチGPUコンピューターハードウェアシステムを示しています。GPUメモリには多くのSPとさまざまなタイプのメモリがあります。これらのハードウェアは、GPUが効率的な並列計算を実行するための基盤です。
それでは、データ処理の観点からCPUとGPUの特性を比較してみましょう。CPUは、整数や浮動小数点数などのさまざまなデータ型を処理するための強力な汎用性が必要であり、論理的な判断によって引き起こされる多数の分岐ジャンプや割り込み処理に優れている必要があるため、CPUは実際には非常に有能なストロングガイであり、彼は多くのことを適切に処理できます。もちろん、彼が使用するために多くのリソース(さまざまなハードウェア)を提供する必要があります。これにより、CPUのコア数(総数)が多すぎることも不可能になります。コアの数は16)を超えません。GPUは、高度に統合された独立した大規模データと、中断する必要のない純粋なコンピューティング環境に直面しています。GPUには多くのコアがあります(Fermiアーキテクチャには512コアがあります)が、そのコアの能力はCPUのコアよりもはるかに強力ではありませんが、より多くの
利点があります。単純なコンピューティングタスクを処理するときに「多くの人とより多くのパワー」の利点を示します。これが並列コンピューティングの魅力です。
2つの特徴を要約すると次のとおりです。
- CPU:プロセス制御とロジック処理、不規則なデータ構造、予測できないストレージ構造、シングルスレッドプログラム、ブランチ集約型アルゴリズムに優れています
- GPU:データ並列コンピューティング、通常のデータ構造、予測可能なストレージモードに優れています。
現在のコンピュータアーキテクチャでは、CUDA並列コンピューティングを完了するために、GPUだけではコンピューティングタスクを完了できません。CPUを使用して協調し、高性能の並列コンピューティングタスクを完了する必要があります。
一般的に、パラレル部分はGPUで実行され、シリアル部分はCPUで実行されます。これは、ヘテロジニアスコンピューティングです。具体的には、ヘテロジニアスコンピューティングとは、異なるアーキテクチャのプロセッサが互いに協力してコンピューティングタスクを完了することを意味します。CPUはプログラムフロー全体を担当し、GPUは特定の計算タスクを担当します。GPUスレッドが計算タスクを完了すると、GPU側で計算された結果をCPU側にコピーして、計算タスクを完了します。
したがって、GPUを使用してアクセラレーションを実現するアプリケーションの全体的な分業は次のとおりです。集中的な計算コード(コード量の約5%)はGPUによって完了され、残りのシリアルコードはCPUによって実行されます。
2.CUDAスレッドモデル
以下に、CUDAのスレッド構成構造を紹介します。まず第一に、スレッドがプログラム実行の最も基本的な単位であることは誰もが知っています。CUDAの並列計算は、数千のスレッドの並列実行によって実現されます。次の構造図は、GPU構造のさまざまなレベルを示しています。
CUDAのスレッドモデルは次のように要約されています。
1.スレッド:スレッド、並列処理の基本単位
2.スレッドブロック:スレッドブロック、相互に連携するスレッドグループ。スレッドブロックには次の特性があります。
- 相互に同期できるようにする
- 共有メモリを介してデータをすばやく交換できます
- 1、2、または3次元で整理する
3.グリッド:スレッドブロックのセット
- 1次元と2次元で整理する
- 共有グローバルメモリ
カーネル:GPUで実行されるコアプログラム。このカーネル関数は特定のグリッドで実行されます。
1つのカーネル<-> One Gridの
各ブロックとスレッドには独自のIDがあり、対応するインデックスから対応するスレッドとスレッドブロックを見つけます。
threadIdx、blockIdx
ブロックID:1Dまたは2D
スレッドID:1D、2Dまたは3D
カーネルを理解するには、カーネルのスレッド階層を明確に理解している必要があります。まず第一に、GPUには多くの並列化された軽量スレッドがあります。カーネルがデバイス上で実行されると、実際には多くのスレッドが開始されます。カーネルによって開始されるすべてのスレッドはグリッドと呼ばれます。同じグリッド上のスレッドは同じグローバルメモリスペースを共有します。グリッドは最初のスレッド構造です。レベル、およびグリッドは多くのスレッドブロック(ブロック)に分割できます。スレッドブロックには多くのスレッドが含まれます。これは第2レベルです。ねじ山の2層組織構造を上図に示します。これは、グリッドとブロックの両方が2次元であるねじ山組織です。グリッドとブロックの両方が、タイプdim3の変数として定義されます。Dim3は、3つの符号なし整数(x、y、z)メンバーを含む構造変数と見なすことができます。定義されると、デフォルト値は1に初期化されます。したがって、グリッドとブロックは、1次元、2次元、および3次元の構造として柔軟に定義できます。カーネルが呼び出されるとき、カーネルが使用するグリッドの次元とスレッドも、構成<<< gridを実行して指定する必要があります。 、ブロック>>>ブロックの寸法。たとえば、上の写真を例として、markメソッド<<< grid、block >>>>を使用して必要なスレッドにインデックスを付ける方法を分析してみましょう。CUDAの<<< grid、block >>>は、実際にはマルチレベルのインデックス作成方法です。最初のレベルのインデックスは(grid.xIdx、grid.yIdy)であり、上の図の対応する例は(1、1)です。このスレッドブロックの場所を見つけることができます。次に、セカンダリインデックス(block.xIdx、block.yIdx、block.zIdx)を開始して、指定されたスレッドを見つけます。これが私たちのCUDAスレッド組織構造です。
ここで私はSPとSM(ストリームプロセッサ)について話したいと思います、多くの人々はこれらの2つの専門用語に混乱するでしょう。
-
SP:最も基本的な処理ユニットであるストリーミングプロセッサ。CUDAコアとも呼ばれます。最終的な特定の指示とタスクはすべてSPで処理されます。GPUは並列計算を実行します。つまり、多くのSPが同時に処理を実行します。
-
SM:複数のSPとその他のリソースがストリーミングマルチプロセッサを形成します。GPUコアとも呼ばれ、ワープスケジューラ、レジスタ、共有メモリなどの他のリソース。SMは(CPUコアと比較して)GPUの心臓部と見なすことができ、レジスタと共有メモリはSMのリソースが不足しています。CUDAは、これらのリソースをSMに存在するすべてのスレッドに割り当てます。したがって、これらの限られたリソースにより、各SMのアクティブワープには非常に厳しい制限があり、並列機能も制限されます。
各SMに含まれるSPの数はGPUアーキテクチャによって異なることに注意してください。FermiアーキテクチャGF100は32、GF10Xは48、Keplerアーキテクチャは192、Maxwellは128です。
つまり、SPはスレッド実行のハードウェアユニットです。SMには複数のSPが含まれます。GPUには複数のSM(たとえば、16)を含めることができます。最後に、GPUには数千のSPが含まれる場合があります。非常に多くのコアが「同時に実行」されるため、速度が想像できます。この引用符は、実際には、ソフトウェアロジックでは、すべてのSPが並列であるが、物理的にすべてのSPが同時に計算を実行できるわけではないことを示しています(たとえば、8つのSMには、処理のスケジュールが必要な1024のスレッドブロックしかありません)。これは、GPUスレッドのスケジューリングに関連する、一時停止、準備完了、およびその他の状態になるものがあるためです。
次の図は、ハードウェアの観点とソフトウェアの観点からCUDAのスレッドモデルを説明しています。
- 各スレッドは、各スレッドプロセッサ(SP)によって実行されます。
- スレッドブロックはマルチコアプロセッサ(SM)によって実行されます
- カーネルは実際にはグリッドによって実行され、カーネルは一度に1つのGPUでのみ実行できます
ブロックはソフトウェアの概念です。ブロックは1つのsmでのみスケジュールできます。開発時に、プログラマーはブロックの属性を設定することで、GPUハードウェアにスレッドの数とスレッドの編成方法を伝えることができます。特定のスケジューリングはSMワープスケジューラの責任です。ブロックがSMに割り当てられると、ブロックは実行が終了するまでSMに残ります。SMは同時に複数のブロックを持つことができますが、シーケンスの実行が必要です。次の図は、GPU内のハードウェアアーキテクチャを示しています。
3.CUDAメモリモデル
CUDAのメモリモデルは、次のレベルに分けられます。
- 各スレッドは独自のレジスタを使用します
- 各スレッドには独自のローカルメモリ(ローカルメモリ)があります
- 各スレッドブロックには独自の共有メモリ(共有メモリ)があり、すべてのスレッドブロックのすべてのスレッドがこのメモリリソースを共有します
- 各グリッドには独自のグローバルメモリ(グローバルメモリ)があり、異なるスレッドブロックのスレッドで使用できます。
- 各グリッドには独自の定数メモリ(定数メモリ)とテクスチャメモリ(テクスチャメモリ)があり、異なるスレッドブロックのスレッドで使用できます。
スレッドがこれらのタイプのメモリにアクセスする速度は、レジスタ>ローカルメモリ>共有メモリ>グローバルメモリです。
次の図は、コンピュータアーキテクチャにおけるこれらのメモリのレベルを示しています。
4.CUDAプログラミングモデル
上記でハードウェア関連の知識のポイントについて多く話しましたが、ついにCUDAがプログラムを作成する方法について話し始めることができます。
一般的なCUDA用語を見てみましょう:
マスターする最初のプログラミングキー:キーワード
GPUで実行できるプログラムまたは関数をどのように作成しますか?
キーワードを使用して、特定のプログラムがCPUで実行されているかGPUで実行されているかを示すことができます。たとえば、次の表に示すように、__ global__を使用してカーネル関数を定義します。カーネル関数はCPUで呼び出され、GPUで実行されます。__global__関数の戻り値はvoidに設定する必要があることに注意してください。
2番目のプログラミングポイント:データ送信
CPUとGPU間のデータ転送を書く方法は?
まず、GPUメモリ割り当てとメモリの再利用の機能インターフェイスを紹介します。
cudaMalloc(): 在设备端分配global memory
cudaFree(): 释放存储空间
CPUデータとGPUデータ間のデータ転送の関数インターフェースは同じです。渡された関数パラメーター(列挙型)を使用して、送信方向を示します。
cudaMemcpy(void *dst, void *src, size_t nbytes,enum cudaMemcpyKind direction)
列挙型cudaMemcpyKind:
cudaMemcpyHostToDevice(CPU到GPU)
cudaMemcpyDeviceToHost(GPU到CPU)
cudaMemcpyDeviceToDevice(GPU到GPU)