C++ AMP

C++ AMP (C++ Accelerated Massive Parallelism) とは、マイクロソフトが中心になって開発していた、演算アクセラレータを活用した並列プログラミングのためのC++言語拡張およびライブラリである。バックエンドとしてDirectCompute (DirectX) やOpenCLなどを使用した実装が存在している。C++ AMPによって、DirectComputeやOpenCLよりも高レベルの並列プログラミングAPIが提供される。
概要.
マイクロソフトはWindows上においてGPGPUを標準化するために、DirectX 11にてDirectComputeを導入した。しかし、DirectComputeはDirect3D APIを直接操作する必要があるため、OpenMPを利用した一般的なCPU向けのC++並列プログラミングなどと比較してハードルが高い。マイクロソフトはより抽象化された純粋C++プログラミングに近いGPU並列プログラミングモデルを提供するAPIおよび言語拡張として、2012年にC++ AMPの仕様を公開した。また、マイクロソフトによるC++ AMPの公式プロプライエタリ実装として、Microsoft Visual C++ 2012はDirectComputeをバックエンドとした最初のC++ AMP対応コンパイラとなっている。
C++ AMPのライブラリおよびC++ AMPに対応したVisual C++コンパイラが付属しているのはVisual Studio 2012以降であるが、Visual Studio 2013ではC++ AMP v1.2規格に対応し、さらにコンピュートシェーダー以外のシェーダーステージとの相互運用やテクスチャフィルタリング（サンプラー）などの機能が強化されている 。しかしVisual C++ 2022以降、C++ AMPは非推奨となった。
なお、C++ AMP自体はDirectCompute/Windows専用というわけではなく、またGPU専用というわけでもない。AMDとマイクロソフトは、OpenCLなどをバックエンドとするLinuxやOS X向けのC++ AMP v1.2オープン実装を発表した。その後、同コンパイラはMulticoreWareを経てRadeon Open Compute (ROCm) プラットフォームに移管され、"Clamp", "Kalmar", "HCC"と名前を変えながら開発が続けられていたが、2019年6月予定の最終リリースをもってHCCの開発が終了することになった。HCCでは、OpenCLのカーネル記述言語"OpenCL C"や中間言語"SPIR"、およびHSA (Heterogeneous System Architecture) の中間言語"HSAIL"への変換層が提供され、Visual C++版よりもさらにヘテロジニアス環境向けを意識した実装となっている。
MSDNブログではCUDA、OpenCL、DirectComputeプログラマー向けに、各APIに対応するC++ AMP相当機能の比較資料が公開されていた。
コード例.
C++ AMPのコード例を示す。OpenMPのcodice_1のように、並列処理するコード単位すなわちカーネルを、C++11のラムダ式を使ってインラインで記述している部分が特徴的である。デフォルトのアクセラレーターを使用する場合、OpenCLやDirectComputeのような冗長なデバイス準備処理の記述も不要となる。
なおVisual C++実装では、実行環境にDirectX 11対応のGPUが搭載されている場合、codice_2句が指定されたコードの実行にはGPUが使用される。対応GPUが搭載されていない場合は、Direct3D WARPデバイスやREFデバイスなどによるCPUエミュレーション実行（フォールバック）が提供される。
等差数列を作成する例.
using namespace concurrency;
int main()
 const int ElemCount = 100;
 int ary[ElemCount];
 array_view<int, 1> av(ElemCount, ary);
 parallel_for_each(extent<1>(ElemCount),
 [=](index<1> idx) restrict(amp) 
 int i = idx[0];
 av[i] = i;
 av.synchronize();
多次元配列の例.
using namespace concurrency;
using namespace concurrency::graphics;
int main()
 const int width = 10;
 const int height = 3;
 const int depth = 2;
 const int elemCount = width * height * depth;
 std::vector<float_3> srcHost(elemCount);
 std::vector<float_3> dstHost(elemCount);
 for (int z = 0; z < depth; ++z)
 for (int y = 0; y < height; ++y)
 for (int x = 0; x < width; ++x)
 srcHost[z * height * width + y * width + x] = float_3(float(x), float(y), float(z));
 array_view<float_3, 3> srcView(depth, height, width, srcHost);
 array_view<float_3, 3> dstView(depth, height, width, dstHost);
 parallel_for_each(
 dstView.extent,
 [=](index<3> idx) restrict(amp)
 const auto globalx = idx[2];
 const auto globaly = idx[1];
 const auto globalz = idx[0];
 auto temp = srcView[idx];
 if (globaly == 0)
 temp += 0.5f;
 if (globalz == 0)
 temp *= -1;
 dstView[idx] = temp;
 dstView.synchronize();
 for (int z = 0; z < depth; ++z)
 for (int y = 0; y < height; ++y)
 for (int x = 0; x < width; ++x)
 const auto val = dstHost[z * height * width + y * width + x];
 printf("[%d][%d][%d] = (%.1f, %.1f, %.1f)\n", z, y, x, val.x, val.y, val.z);