Nvidia CUDA

NVIDIA CUDA(Compute Unified Device Architecture)は、NVIDIAが開発した並列計算プラットフォームおよびプログラミングモデルです。CUDAを使用すると、開発者は高性能NVIDIA GPU(Graphics Processing Units)を使用して汎用計算タスクを実行できます。CUDAにより、開発者はGPUの強力な計算能力を活用して大規模な並列計算を実行でき、科学計算、機械学習、画像処理、物理シミュレーションなどの分野でのアプリケーションのパフォーマンスを大幅に向上させます。CUDAはC、C++、Pythonなどの複数のプログラミング言語をサポートし、開発者がGPUアクセラレーテッドコンピューティングを 쉽게實現できる豊富なライブラリおよびツールのセットを提供します。

cuda logo

CUDAプログラミングの基礎

プログラムでGPUリソースを呼び出してコード実行を高速化するにはどうすればよいでしょうか?CUDAを使用すると、GPU内の数千の計算コアを簡単に管理できます。CUDAプログラミングモデルは、CPUとGPUが協調して動作する必要がある異種混合モデルです。CUDAプログラミングでは、コードが実行されるデバイスを区別するために、「host」と「device」という用語が使用されます。

  • Host: CPUとホストメモリ
  • Device: GPUとビデオメモリ

一般的に、メインプログラムはCPUにロードされ、、初期化データをGPUにコピーします。GPUが計算を完了し、結果をCPUにコピーして戻します。

CUDAプログラムでは、上記のプロセスを実装するためにカーネルを使用できます。CUDAでは、カーネルとは、呼び出されるとGPUが多くのスレッドを同時に起動してこのカーネルを実行し、並列性を実現する関数を参照します。各スレッドはスレッドIDを使用してカーネルを実行し、入力データのインデックスに対応させ、すべてのスレッドが同じカーネルを実行するが異なるデータを処理するようにします。

GPUは非常に多くの計算コアを持っており、CUDAはこれらのコアを管理するために2レベルの組織構造を使用します。CUDAプログラミングで導入する必要がある3つの概念を以下に示します:Thread、Block、Grid。

  • Thread: CUDA並列プログラムは多くのスレッドによって実行されます。
  • Block: いくつかのスレッドがグループ化されてブロックを形成します。
  • Grid: 複数のブロックがグリッドに整理されます。

注:同じブロック内のスレッドは、共有メモリを通じて同期および通信できます。

cpu and gpu

CUDAでは、各スレッドにはThreadIdxという一意の識別子があり、GridとBlockの分割方法に応じて変化します。

また、下の図に示すように、CUDAメモリの概要を簡単に説明します。各スレッドには独自のプライベートローカルメモリがあり、各スレッドブロックには、ブロック内のすべてのスレッドがアクセスできる共有メモリが含まれており、その存続期間はスレッドブロックの存続期間と同じです。さらに、すべてのスレッドがグローバルメモリにアクセスできます。読み取り専用のメモリ空間もあります:定数メモリとテクスチャメモリです。メモ構造はプログラムの最適化に関連していますが、ここでは詳しく説明しません。

cpu and gpu

CUDAデモンストレーションケース

では、簡単な例を通じてGPUアクセラレーションを体験しましょう。この例では、2つのベクトルの合計をCPUとGPUの両方で計算し、CPUとGPUの計算速度を比較します。

実行環境の準備

JetPackオペレーティングシステムにはCUDA実行環境がすでに含まれています。jtopツールを使用してCUDAバージョンを確認できます。ターミナルを開き、jtopを実行します。

bash
jtop

jtop

注:システムにjtopがインストールされていない場合は、このガイドを参照してください。

以下では、CUDAコードを記述するためにPythonを使用します。したがって、ターミナルで次のコマンドを実行してNumbaをインストールする必要があります。

bash
pip install numba

サンプルコードの実行

ステップ1: ルートディレクトリにこのコース用のPythonスクリプトを作成します:

bash
mkdir -p ~/reComputer_J/3.4_CUDA
touch ~/reComputer_J/3.4_CUDA/add_compare.py

ステップ2: スクリプトがあるディレクトリでVSCodeを開きます:

bash
cd ~/reComputer_J/3.4_CUDA
code .

ステップ3: 左側のナビゲーションバーにhello_world.pyが表示されます。Pythonファイルに次のコードを入力します:

クリックしてコードを展開
python
from numba import cuda, float32
import numpy as np
import time


@cuda.jit
def add_gpu(a, b, out):
  tx = cuda.threadIdx.x
  ty = cuda.blockIdx.x
  block_size = cuda.blockDim.x
  grid_size = cuda.gridDim.x
  start = tx + ty * block_size
  stride = block_size * grid_size

  for i in range(start, a.shape[0], stride):
      out[i] = a[i] + b[i]

def add_cpu(a, b, out):
  for i in range(a.shape[0]):    
      out[i] = a[i] + b[i]

def main():
  # Prepare test data
  n = 10000000
  a = np.arange(n).astype(np.float32)
  b = np.arange(n).astype(np.float32)
  out = np.empty_like(a)

  # 1. Test CPU computation time
  start = time.time()
  add_cpu(a, b, out)
  print("CPU Time Taken:", time.time()-start)

  # 2. Test GPU computation time
  # Copy data from host memory to device memory
  d_a = cuda.to_device(a)
  d_b = cuda.to_device(b)
  d_out = cuda.device_array_like(out)
  # Define the number of threads per block and the number of blocks
  threads_per_block = 256  
  blocks_per_grid = (n + threads_per_block - 1) // threads_per_block
  # Compute the result
  start_gpu = time.time()
  add_gpu[blocks_per_grid, threads_per_block](d_a, d_b, d_out)
  end_gpu = time.time()
  # Copy the result from device memory back to host memory
  d_out.copy_to_host(out)

  print("GPU Time Taken:", end_gpu-start_gpu)


if __name__ == "__main__":
  main()

ステップ4: 実行ボタンをクリックして、ターミナルでPythonスクリプトの結果を確認します。

launch vscode

ターミナルに出力された結果から、GPUの計算速度が速いことがわかります。

CUDA学習チュートリアル

チュートリアルタイプ説明
Getting Started with CUDA Programming(zh)ドキュメント中国語でCUDAプログラミングを始めるためのミニマリストチュートリアル
CUDA C++ Programming GuideドキュメントCUDAモデルとインターフェイスのプログラミングガイド