今回は、ベクトル演算器やSIMD演算器の例についてあげ、関連技術としてNVIDIA社のGPGPUについても簡単に紹介します。
まずSIMD演算ですが、最近の多くのハイエンドCPUにSIMD演算器が適用されており、ニーズ拡大に伴い進歩しています。例えばIntel社はSSE (Streaming SIMD Extensions)からAVX (Advanced Vector Extensions)へ、ARM社はAdvanced SIMD extension (通称NEON)からSVE (Scalable Vector Extension)へと進化しており、それぞれのシリーズの中でも毎年のように進歩しています。
ベクトル演算器としては、ルネサスエレクトロニクス社の車載向けMCU RH850/U2Bに搭載されているNSITEXE社(現デンソー社)のDR1000CにはRISC-Vベクトル演算器が搭載されており(下図左)、またQualcomm社のHexagon NPU (Neural Processing Unit、下図右)にはベクトル演算器が搭載されています。ニューラルネットワーク処理の基本であるニューロンでの演算は積和演算であるため、大規模ニューラルネットワーク処理をベクトル演算器により効率よく実行することができます。
ベクトル演算やSIMD演算と類似の技術としてNVIDIA社のGPGPU (General-Purpose computing on Graphics Processing Units)があり、そのためのプログラム言語としてCUDA (Compute Unified Device Architecture)が知られています。
NVIDIA社のGPUは非常に多く(例えばGeForce RTX 4090では16,384)のCUDAコアとよばれる小型のプロセッサがあり、詳細は省きますがハードウェアとして物理的に階層化されています。ソフトウェアとしては、各CUDAコアが一つのスレッドを逐次的に実行するのですが、複数のスレッドがブロックを構成し、論理的に階層化されています。論理階層と物理階層は対応関係にありますが、物理制約にともなうパラメタは論理的に取得可能であり、プログラマは物理階層を通常気にする必要はありません。
プログラムとしてはスレッドごとの処理をカーネルとよばれる関数として記述します。下のプログラムがカーネル関数の例で、各スレッドにおいて、自分のブロック番号(プログラム内のblockIdx.x)とスレッド番号 (threadIdx.x)とブロック内のスレッド数 (blockDim.x)から自分が計算する配列要素 (idx) を計算し、その配列要素に対する処理を記述します。簡単な制御文も書くことができ、マルチコアとSIMDとの中間的な考え方になっています。
__global__ void assign( int* a_d, int value)
{
int idx = blockDim.x * blockIdx.x + threadIdx.x;
a_d[idx] = value;
}
コメントをお書きください