C/C++ OpenMP* と SYCL* のコンポービリティー
oneAPI プログラミング・モデルは、OpenMP* オフロードをサポートする LLVM/Clang ベースの統合コンパイラーを提供します。これにより、OpenMP* 構造を使用してホスト側のアプリケーションを並列化するか、ターゲットデバイスにオフロードするシームレスな統合が可能になります。インテル® oneAPI ベース・ツールキットで利用可能なインテル® oneAPI DPC++/C++ コンパイラーは、制限付きで OpenMP* および SYCL* のコンポーザビリティーをサポートします。単一のアプリケーションでは、OpenMP* target 領域または SYCL* 構造を使用して、さまざまな関数やコードセグメントなどのコード領域の実行をデバイスにオフロードできます。
OpenMP* と SYCL* オフロード構造は、別々のファイル、同じファイル、または同じ関数 (制限付き) で使用できます。OpenMP* および SYCL* オフロードコードは、実行可能ファイル、静的ライブラリー、またはさまざまな組み合わせで一緒にバンドルできます。
注
SYCL* は CPU でデバイスコードを実行する場合、インテル® TBB のランタイムを使用します。そのため、CPU で OpenMP* と SYCL* の両方を使用するとスレッドのオーバーサブスクリプションが発生する可能性があります。システムで実行されるワークロードのパフォーマンスを解析することで、この問題が生じているか確認することができます。
制限事項
同じアプリケーションで OpenMP* と SYCL* 構造を混在する場合、いくつかの考慮すべき制限があります。
OpenMP* ディレクティブは、デバイスで実行される SYCL* カーネル内では使用できません。同様に、SYCL* コードは、OpenMP* target 領域内では使用できません。ただし、ホスト CPU で実行される OpenMP* コード内で SYCL* 構造を使用することはできます。
プログラム内の OpenMP* および SYCL* デバイス領域は相互依存関係を持つことができません。例えば、デバイスコードの SYCL* 領域で定義された関数は、同じデバイスで実行される OpenMP* コードから呼び出すことはできません (その逆も同様)。OpenMP* デバイス領域と SYCL* デバイス領域は個別にリンクされ、個別のバイナリーとしてコンパイラーによって生成されるファットバイナリーを構成します。
現時点では、OpenMP* と SYCL* ランタイム・ライブラリー間での直接的な相互作用はサポートされていません。例えば、OpenMP* API で生成されたデバイス・メモリー・オブジェクトは、SYCL* コードからはアクセスできません。OpenMP* で生成されたデバイス・メモリー・オブジェクトを SYCL* コードで使用すると、未定義の動作となります。
例
次のコードは、SYCL* と OpenMP* オフロード構造を同じアプリケーションで使用する例を示します。
#include <CL/sycl.hpp>
#include <array>
#include <iostream>
float computePi(unsigned N) {
float Pi;
#pragma omp target map(from : Pi)
#pragma omp parallel for reduction(+ : Pi)
for (unsigned I = 0; I < N; ++I) {
float T = (I + 0.5f) / N;
Pi += 4.0f / (1.0 + T * T);
}
return Pi / N;
}
void iota(float *A, unsigned N) {
cl::sycl::range<1> R(N);
cl::sycl::buffer<float, 1> AB(A, R);
cl::sycl::queue().submit([&](cl::sycl::handler &cgh) {
auto AA = AB.template get_access<cl::sycl::access::mode::write>(cgh);
cgh.parallel_for<class Iota>(R, [=](cl::sycl::id<1> I) {
AA[I] = I;
});
});
}
int main() {
std::array<float, 1024u> Vec;
float Pi;
#pragma omp parallel sections
{
#pragma omp section
iota(Vec.data(), Vec.size());
#pragma omp section
Pi = computePi(8192u);
}
std::cout << "Vec[512] = " << Vec[512] << std::endl;
std::cout << "Pi = " << Pi << std::endl;
return 0;
}サンプルコードをコンパイルするには、次のコマンドを使用します: icpx -fsycl -fiopenmp -fopenmp-targets=spir64 offloadOmp_dpcpp.cpp
ここで、
-fsyclオプションは SYCL* を有効にします-fiopenmp -fopenmp-targets=spir64オプションは OpenMP* を有効にします
次はサンプルコードからの出力例となります。
./a.out
Vec[512] = 512
Pi = 3.14159注
コードに OpenMP* オフロードが含まれておらず、通常の OpenMP* コードのみが含まれる場合、 -fopenmp-targets を省いた次のコマンドを使用します:
icpx -fsycl -fiopenmp omp_dpcpp.cpp