インテルの CPU と GPU 向けの LLVM と GCC のベクトル化

同カテゴリーの次の記事

インテル® oneAPI ポーティング・ガイド (dpcpp/icx) 日本語版公開

この記事は、The Parallel Universe Magazine 47 号に掲載されている「Vectorization in LLVM and GCC for Intel CPUs and GPUs」の日本語参考訳です。原文は更新される可能性があります。原文と翻訳文の内容が異なる場合は原文を優先してください。


parallel_v47_01

近年の CPU や GPU のコアは、SIMD (Single Instruction, Multiple Data) 実行ユニットを利用して、より高いパフォーマンスと電力効率を実現しています。SIMD ハードウェアは、インテル® SSE、インテル® AVX、インテル® AVX2、インテル® AVX-512、およびインテル® Xe アーキテクチャー (第 12 世代) ISA などの命令を介して利用されます。これらを直接使用することも可能ですが、低レベルのため移植性が著しく制限され、ほとんどのプロジェクトでは推奨されません。

ここでは、移植性が高く、使いやすいインターフェイスをプログラマーに提供するため、自動ベクトル化、言語構造やプラグマなどのヒントを使用した SIMD ベクトル化、および SIMD データ並列ライブラリー・アプローチの 3 つの手法を検討します。これらの手法の概要を説明し、コード例を使用して LLVM および GCC コンパイラーにおける SIMD ベクトル化の進化を紹介します。また、インテル® Xeon® プロセッサーおよびインテル® Xe アーキテクチャー GPU で最適なパフォーマンスを実現するため、LLVM および GCC コンパイラーにおける 2 つのベクトル化手法を検討します。

LLVM と GCC の拡張

インテルは、LLVM と GCC コンパイラーの両方のベクトル化機能を強化することを目標としており、オープンソースへの貢献は重要な設計上の考慮事項でした。VPlan ベクトライザーと関連する VectorABI は、LLVM と GCC[13] の両方のオプティマイザーに統合できるように設計されています。

VPlan ベクトライザーのフレームワークは、LLVM トランクに統合される可能性があります。インテルの VectorABI[12] は公開されており、LLVM および GCC コミュニティーによって関数のベクトル化に利用されています。VPlan ベクトライザーは、インテル® Xeon® プロセッサー用のインテル® コンパイラーが以前提供していた結果を上回っています。

SIMD の利用

近年の CPU は SIMD 実行をサポートしています。SIMD とは、1 つの命令で複数のデータ要素を並列に実行するハードウェア機能です。制御フローが似ており (ベクトルの発散が少ない)、メモリーに依存しない複数のデータを一度に操作する場合に有効です。残念ながら、SIMD ISA を直接利用するプログラムを記述することは容易ではなく、移植性にも限界があります。この状況を改善するため、自動ベクトル化、ヒントや言語構造を使用したプログラマーによる SIMD ベクトル化、および C++ SIMD データ並列ライブラリーの利用という 3 つのアプローチを説明します。

自動ベクトル化

データと制御の依存関係解析を自動的に行い、組込みコストモデルに基づいてスカラープログラムをベクトル形式に変換することを自動ベクトル化[4][5] と呼びます。この方法は生産性や移植性に優れており、プログラマーにとって魅力的ですが、ループ境界やメモリー・アクセス・パターンなど、コンパイル時に未知な部分があるため、自動ベクトル化は必ずしも最適なコードを生成するわけではありません。

プログラマーによる SIMD ベクトル化

OpenMP* (バージョン 4.0 以降) には、ベクトルレベルの並列処理をサポートする SIMD 構文があります[7]。これらの構文は、標準化されたベクトル化構文のセットを提供するため、プログラマーは移植性のないベンダー固有の組込み関数やディレクティブを使用する必要がありません[6]。さらに、これらの構文はコンパイラーにコードの構造に関する追加のヒントを提供し、並列化とうまく融合した、より優れたベクトル化を可能にします[5]

C++ SIMD データ並列ライブラリー

ISO C++ にはデータ並列ライブラリーに関する提案が提出されており[3]、その目的は、SIMD レジスターや命令、共通の命令デコーダーで実行される実行ユニットなどのデータ並列実行リソースによる高速化をサポートすることです。これらの実行リソースが利用できない場合、インターフェイスはシーケンシャル実行への透過的なフォールバックをサポートします。図 1 に、C++ SIMD データ並列ライブラリーを使用した SIMD memcpy の例を示します。この例をコンパイルして、core-avx512 用の LLVM ベクトル中間表現 (IR) とバイナリーを生成できます。

namespace stdsimd = std::experimental;
void simd_memcpy(
     stdsimd::native_simd<float> x,
     stdsimd::native_simd<float> y,
     void *p)
{
   auto cmp = x < y;
   memcpy(p, &cmp, cmp.size()*4);
}
define void @_Z11simd_memcpy_Pv(<16 x float> %x.coerce,
                                <16 x float> %y.coerce, i8*
                                nocapture %p)
{ 
entry:
  %0 = fcmp fast olt <16 x float> %x.coerce, %y.coerce
  %cmp.sroa.0.sroa.0.0.p.sroa_cast = bitcast i8* %p to <16 x i1>*
  store <16 x i1> %0, <16 x i1>* %cmp.sroa.0.sroa.0.0.p.sroa_cast
  ret void
}

図 1. C++ SIMD データ並列ライブラリーの例

SIMD ベクトル化は、どのベクトル化手法を使用して SIMD コードを生成するかにかかわらず、最新の CPU および GPU 上で計算集約型ワークロードの最適なパフォーマンスを実現するために非常に重要です。次のセクションでは、いくつかのコード例とともに、CPU と GPU に関する最近の LLVM SIMD ベクトル化の進歩を紹介します。


製品とパフォーマンス情報

1実際の性能は利用法、構成、その他の要因によって異なります。詳細については、www.Intel.com/PerformanceIndex (英語) を参照してください。

関連記事