GPUコンテナコアバインド戦略(アフィニティベース)

GPUコンテナコアバインド戦略(アフィニティベース)

CPUとGPUパフォーマンス最適化の紹介

ハイパフォーマンスコンピューティングや大規模並列タスク処理において、GPUは不可欠なアクセラレータとなっています。GPUの計算能力を最大限に活用するには、CPUコアをGPUに適切に割り当ててバインドすることで、CPUとGPUの関係を最適化することが重要です。本記事では、ソケットとNUMA(Non-Uniform Memory Access)の概念を掘り下げ、これらのハードウェアアーキテクチャに基づいてCPUとGPUのコアバインドを実装し、最適なシステムパフォーマンスを確保する方法について説明します。

ソケットの概念

ソケットとは?

ソケットは通常、マザーボード上の物理的なCPU取り付けスロットを指します。各ソケットは1つの物理CPUに対応し、通常は複数のコアと1つ以上のキャッシュレベル(例:L1、L2、L3キャッシュ)を含みます。マルチソケットシステム(例:デュアルまたはクアッドサーバー)では、各ソケットに物理CPUが搭載され、高速インターコネクト(例:IntelのQPI、AMDのInfinity Fabric)を介して接続されます。

マルチソケットシステムの特性

マルチソケットシステムでは、各ソケットのCPUは自身のローカルメモリにアクセスできるだけでなく、他のソケットのメモリにもアクセスできます。このメモリアクセスパターンにより、NUMAの概念が導入され、メモリアクセス効率の最適化が図られます。

NUMA(Non-Uniform Memory Access)アーキテクチャ

NUMAとは?

NUMAはNon-Uniform Memory Accessの略で、不均一メモリアクセスを意味します。従来の均一メモリアクセス(UMA)とは異なり、NUMAアーキテクチャではシステムメモリが複数の領域に分割され、各領域は特定のCPU(ソケット)に関連付けられます。ローカルメモリ(同じソケット内)へのアクセスはリモートメモリ(別のソケット)へのアクセスよりも高速であり、その結果レイテンシが低くなります。

NUMAノードとメモリアクセス遅延

NUMAシステムでは、各ソケットとそれに直接接続されたメモリがNUMAノードを形成します。同じNUMAノード内のメモリアクセスは高速ですが、ノード間のメモリアクセスは追加のバス伝送によりレイテンシが高くなります。メモリとCPUのアフィニティを最適化し、タスクを同じNUMAノード内で実行させることは、重要なパフォーマンス最適化のステップです。

CPUとGPUの物理的関係

GPUのハードウェアアーキテクチャ

GPUは通常、PCIe(Peripheral Component Interconnect Express)バスを介してCPUと通信します。マルチソケットシステムでは、GPUは通常1つのソケット(およびその対応するNUMAノード)にのみ接続され、ソケット間をまたぐことはありません。つまり、実際の動作では、GPUは接続されたソケットのCPUコアおよびメモリとの間でより高い帯域幅と低いレイテンシを持ちます。

CPUとGPUのアフィニティ

CPUとGPUの通信は主にデータ転送に依存します。データはCPUからGPUへ転送され、GPUからCPUへ戻ってきます。これにはメモリアクセス操作が伴い、パフォーマンスに大きな影響を与えます。GPUにバインドされたCPUコアがGPUと同じNUMAノード内にある場合、データ転送のレイテンシが大幅に低減されます。そのため、CPUコアをGPUにバインドすることは、重要なパフォーマンス最適化のステップです。

アフィニティに基づくGPUとCPUコアのバインド戦略

Dockerコンテナ化されたデプロイメントでは、GPUとCPUのアフィニティバインディングを実装することが、コンテナ化されたタスクのパフォーマンス向上に不可欠です。DockerのCPUおよびGPUリソース制御機能を使用すると、コンテナが使用するCPUコアとGPUを精密に制御できます。

コンテナのCPUおよびGPUリソース割り当て

Dockerコンテナでは、割り当てるCPUおよびGPUリソースを精密に制御できます。コンテナが使用するCPUコアとGPUデバイスを指定することで、アフィニティバインディングを実装し、パフォーマンスを最適化できます。

DockerのCPU設定

Dockerでは、以下のパラメータを使用してCPUリソース割り当てを制御できます:

  • --cpuset-cpus:コンテナが使用できる物理CPUコアを指定します。例:--cpuset-cpus="0-3" はコンテナをCPUコア0~3に制限します。
  • --cpu-shares:コンテナのCPU使用率の重みを制御しますが、特定のコア使用を制限するものではありません。
  • --cpus:コンテナが使用できるCPUコアの総数(仮想コア単位)を制限します。

DockerのGPU設定

以下のDockerパラメータを使用してGPUデバイスのバインディングを実現できます:

  • --gpus:コンテナがアクセスできるGPUデバイスを指定します。例:--gpus '"device=0"' はGPU 0をコンテナに割り当てます。

DockerでのGPUとCPUコアバインディングの実装

DockerコンテナでGPUとCPUコアのバインディングを実装するには、CPU設定とGPU設定を組み合わせます。以下は、Dockerコンテナを起動する際に特定のCPUコアとGPUをバインドする例です:

docker run --cpuset-cpus="0-3" --gpus '"device=0"' --memory="8g" my_gpu_container

このコマンドでは:

  • --cpuset-cpus="0-3" はコンテナをCPUコア0~3にバインドします。これらはGPU 0と同じNUMAノード内にあるべきです。
  • --gpus '"device=0"' はGPU 0をコンテナに割り当てます。
  • --memory="8g" はコンテナのメモリ使用量を8GBに制限し、メモリ割り当てもCPU/GPUアフィニティに沿うようにします。

最適なバインディングの実現

コンテナで最適なバインディングを確保するには、まずマシンの物理トポロジを確認します。nvidia-smi topo -m を使用してマシンのトポロジを表示します:

nvidia-smi topo -m

出力から、マシンのNUMA構成とGPU割り当てを確認できます。たとえば、マシンに2つのNUMAノードがあり、NUMA 0に4つのGPU、NUMA 1に4つのGPUがある場合、各NUMAノードのGPUとCPUのアフィニティを特定できます。

アフィニティ計算の疑似コード

以下は、アフィニティを計算し、対応するGPUおよびCPU IDを決定するための簡略化された疑似コードです:

type Affinity struct {
}

// アフィニティが満たされているかを計算し、対応するGPUおよびCPU IDを返す
func calAffinity(affinity *Affinity, cpuUse []int, gpuUse []int, gpuReq int, cpuReq int) (bool, []int, []int) {
    return true, []int{}, []int{}
}

このメソッドを使用して、ノードが最適なバインディング要件を満たしているかどうかを判定できます。複数のノードが存在する場合、このメソッドを使用して評価し、最適なノードを選択できます。Novita AIは、次世代AIコンピューティング向けにカスタマイズされたコンテナエンジンを開発し、アルゴリズムを動的に調整してハードウェア使用状況をリアルタイムで監視し、エンドツーエンドの最適化を実行します。ユーザーはNUMAの技術的詳細を気にすることなく、最高の計算性能を享受できます。

ユーザーはコントロールパネルでさらに高度なNUMA設定を行うこともできます。関連するご要望がございましたら、お気軽にDiscordにてお問い合わせください!

詳細は Novita AI をご覧ください!

single numa