- Mooreの法則が限界に直面し、単一コアの高速化から マルチコア・並列処理 を中心とする方向へハードウェアが進化している
- 並列処理 はデータ並列性、モデル並列性、パイプライン並列性など複数の形態に分けられ、現代のディープラーニングシステムで混合的に使われている
- SIMD(命令レベルのデータ並列性)、スレッド/コア並列性、GPUの大規模並列 など、さまざまな階層で並列化が行われている
- 並列処理のための言語・ライブラリ・ツール は、多くが既存の逐次言語に「付け足された」拡張型であり、並列性を言語にネイティブ統合する流れ(Mojoなど)が注目されている
- キャッシュライン共有(不要な相互作用)の最適化、効率的なメモリ分割、自動ベクトル化 など、実践的な性能最適化が重要な課題である
並列性の動機とハードウェア進化
- 初期にはトランジスタの微細化とクロック向上によって性能が自然に改善されたが、発熱/製造プロセスの限界 により物理的限界に達した
- その後 マルチコア構成 が標準となり、1つのCPUに数個から数百個のコアが搭載されるようになった
並列性の一般的な形
- データ並列性: 同じ演算を多数のデータに同時適用する(例: ベクトル和演算)
- モデル並列性: 1つのモデルを複数のデバイスに分散配置する
- パイプライン並列性: 計算を複数段階に分割し、各段階を同時に動作させる
SIMD(単一命令・複数データ)とベクトル化
- SIMD は1つの命令で複数のデータ(ベクトル)を処理する方式で、ARM NEON、x86 SSE/AVX などさまざまなISAでサポートされている
- C/C++ intrinsics によってベクトル演算を明示的に制御でき、コンパイラの 自動ベクトル化 も利用できるが限界がある
- 実運用では、ベクトル長ぶんを繰り返し処理した後、残りのデータはスカラ演算で補正する
CPUでの並列化
- スレッド を活用してマルチコア上で並列実行し、言語ごとのAPIとOSスケジューラの支援を受ける
- スレッドの生成/終了コストは大きいため、データサイズに対して適切な(コア数に近い)スレッド数で作業を分割するのが効率的である
- キャッシュラインの「false sharing」(別スレッドが同じキャッシュライン内の独立変数にアクセスした際の性能低下)の最適化が重要で、C++17の
std::hardware_destructive_interference_size などを活用できる
- 各スレッドが別々のデータ領域を書き込むように、パディング/アラインメント 処理が必要である
GPUでの並列化
- GPUは数千個の小さなコアを通じて 大規模データ並列処理 に特化している
- CUDA/OpenCL: カーネル関数を数十〜数万個のスレッド/ブロック単位で実行し、内部的には SIMT(単一命令・複数スレッド)モデルで動作する
- ワークグループ/ワープ 単位で動作し、分岐(branch divergence)を最小化することが性能に非常に重要である
- メモリ階層構造: スレッドレジスタ、ブロック共有メモリ、全体グローバルメモリなど、階層的な最適化が必要である
Triton: PythonベースのGPUカーネルDSL
- Triton はPythonに組み込まれたDSLで、JITコンパイルおよび複数バックエンド(MLIR/LLVM/PTX など)をサポートする
- カーネルコードを高水準のPythonで記述でき、自動並列化・分割・マスキングなどをサポートする
- NVIDIA cuDNN水準と比べて75〜90%の性能を出しつつ、開発の複雑さを大幅に減らせる
並列性のネイティブ言語化: Mojo
- Mojo はLLVM/MLIRの開発者 Chris Lattner が作った新しい言語で、並列性とハードウェア特化コンパイルを言語レベルでサポートする
- SIMDベクトル型、ベクトル化関数、ホスト/デバイスメモリの区別など、型システムと言語構造に並列性が組み込まれている
- Pythonスタイルのループも自動ベクトル化され、明示的な低レベル制御なしで性能を確保できる
結論と展望
- 現代の並列プログラミングは、多様なハードウェアと並列性モデルの組み合わせで成り立っており、言語自体の支援がますます重要になっている
- Mojo、Triton、JAX など 次世代の並列言語・ツール の台頭により、並列化はより直感的かつ生産的な方向へ進化している
- 並列プログラミングでは、ハードウェア構造、メモリ最適化、言語支援が有機的に結び付いてこそ、実際の性能を最大化できる
まだコメントはありません。