DPC++ ポリシーの使用方法

DPC++ の使用方法を示します。

Parallel STL の使用方法

次の手順に従って、アプリケーションに Parallel STL を追加します。

  1. コードに #include <oneapi/dpl/execution> を追加します。次に、使用するアルゴリズムに応じて、次のヘッダーファイルを 1 つ以上インクルードします。
    • #include <oneapi/dpl/algorithm>
    • #include <oneapi/dpl/numeric>
    • #include <oneapi/dpl/memory>

    C++ 標準ライブラリーとの共存性を高めるには、標準 C++ ヘッダーファイルの前にインテル® oneDPL ヘッダーファイルをインクルードします。

  2. oneapi::dpl::execution 名前空間で定義されているインテル® oneDPL 実行ポリシー・オブジェクトを並列アルゴリズムに渡します。
  3. C++ 標準の実行ポリシーを使用します。
    1. OpenMP* のベクトル化プラグマを有効にするオプションを指定してコードをコンパイルします。
    2. 並列処理を行うにはインテル® oneTBB やインテル® TBB のダイナミック・ライブラリーとリンクします。
  4. DPC++ 実行ポリシーを使用します。
    1. SYCL* 2020 のサポートを有効にするオプションを指定してコードをコンパイルします。

#include <oneapi/dpl/execution>
#include <oneapi/dpl/algorithm>
#include <vector>

int main()
{
    std::vector<int> data( 1000 );
    std::fill(oneapi::dpl::execution::par_unseq, data.begin(), data.end(), 42);
    return 0;
}

DPC++ 実行ポリシーの使用

DPC++ 実行ポリシーは、並列アルゴリズムを実行する場所と方法を定義します。SYCL* デバイスまたはキューをカプセル化し、オプションのカーネル名を設定することができます。DPC++ 実行ポリシーは、実行ポリシーをサポートするすべての標準 C++ アルゴリズムで使用できます。

ポリシーを使用するには、一意のカーネル名のクラス型を示すテンプレート引数と次のいずれかのコンストラクター引数を指定してポリシー・オブジェクトを作成します。

  1. SYCL* キュー
  2. SYCL* デバイス
  3. SYCL* デバイスセレクター
  4. 異なるカーネル名の既存のポリシー・オブジェクト

使用されるコンパイラーが SYCL* カーネル関数の暗黙名をサポートしている場合、ポリシーのカーネル名の指定はオプションです。インテル® oneAPI DPC++/C++ コンパイラーはデフォルトでサポートしています。ほかのコンパイラーでは、-fsycl-unnamed-lambda などのコンパイラー・オプションを使用して有効にする必要があります。詳細は、コンパイラーのドキュメントを参照してください。

oneapi::dpl::execution::dpcpp_default オブジェクトは、device_policy クラスの事前定義オブジェクトです。デフォルトのカーネル名とデフォルトのキューで作成されています。カスタマイズされたポリシー・オブジェクトの作成に使用したり、アルゴリズムを呼び出す際に直接渡すことができます。

dpcpp_default が複数のアルゴリズムに直接渡される場合、コンパイル時に暗黙のカーネル名を有効にする必要があります (上記参照)。

make_device_policy 関数テンプレートは、device_policy の作成を簡素化します。

使用例

以下のコード例では、ポリシークラスと関数を参照する際に、名前空間 oneapi::dpl::execution;sycl; ディレクティブを使用することを仮定しています。

FPGA ポリシーの使用

fpga_policy クラスは、FPGA ハードウェア・デバイス上で並列アルゴリズムのパフォーマンスを向上するように調整された DPC++ ポリシーのです。

FPGA ハードウェア・デバイスや FPGA エミュレーション・デバイスでアプリケーションを実行する場合に使用します。

  1. FPGA デバイスで実行するには ONEDPL_FPGA_DEVICE マクロを、FPGA エミュレーション・デバイスで実行するには ONEDPL_FPGA_EMULATOR マクロを定義します。
  2. コードに #include <oneapi/dpl/execution> を追加します。
  3. アンロール係数 (以下の を参照) と一意のカーネル名のクラス型を示すテンプレート引数 (どちらもオプション)、および次のいずれかのコンストラクター引数を指定してポリシー・オブジェクトを作成します。
    1. FPGA セレクター 用に構築された SYCL* キュー (その他のキューでは動作は未定義)
    2. カーネル名、アンロール係数、または両方が異なる既存の FPGA ポリシー・オブジェクト
  4. 作成したポリシー・オブジェクトを並列アルゴリズムに渡します。

fpga_policy のデフォルトのコンストラクターは、ONEDPL_FPGA_EMULATOR が定義されている場合、fpga_selector または fpga_emulator_selector 用に構築された SYCL* キューを持つオブジェクトを作成します。

oneapi::dpl::execution::dpcpp_fpga は、デフォルトのアンロール係数とデフォルトのカーネル名で作成された fpga_policy クラスの定義済みオブジェクトです。カスタマイズされたポリシー・オブジェクトの作成に使用したり、アルゴリズムを呼び出す際に直接渡すことができます。

ポリシーのアンロール係数を指定すると、アルゴリズムの実装でループアンロールが有効になります。デフォルト値は 1 です。最適な値の選択については、『インテル® oneAPI DPC++ FPGA 最適化ガイド』 (英語) のunroll プラグマ」 (英語) と「ループ解析」 (英語) を参照してください。

make_fpga_policy 関数テンプレートは、fpga_policy の作成を簡素化します。

FPGA ポリシーの使用例

以下のコード例では、ポリシーには名前空間 oneapi::dpl::execution; を、キューとデバイスセレクターには sycl; を使用することを仮定しています。

constexpr auto unroll_factor = 8;
auto fpga_policy_a = fpga_policy<unroll_factor, class FPGAPolicyA>{};
auto fpga_policy_b = make_fpga_policy(queue{intel::fpga_selector{}});
auto fpga_policy_c = make_fpga_policy<unroll_factor, class FPGAPolicyC>();

アルゴリズムにデータを渡す

次のいずれかの方法で、DPC++ ポリシーを使用して実行されるアルゴリズムにデータを渡すことができます。

oneapi::dpl::begin と oneapi::dpl::end 関数の使用

oneapi::dpl::beginoneapi::dpl::end は、SYCL* バッファーを Parallel STL アルゴリズムに渡すことができるヘルパー関数です。これらの関数は、SYCL* バッファーを受け取り、以下の要件を満たす不特定型のオブジェクトを返します。

これらの関数を使用するには、コードに #include <oneapi/dpl/iterator> を追加します。

例:

#include <oneapi/dpl/execution>
#include <oneapi/dpl/algorithm>
#include <oneapi/dpl/iterator>
#include <CL/sycl.hpp>
int main(){
  sycl::buffer<int> buf { 1000 };
  auto buf_begin = oneapi::dpl::begin(buf);
  auto buf_end   = oneapi::dpl::end(buf);
  std::fill(oneapi::dpl::execution::dpcpp_default, buf_begin, buf_end, 42);
  return 0;
}

統合共有メモリー (USM) の使用

以下の例は、Parallel STL アルゴリズムと USM を併用する 2 つの方法を示します。

USM に割り当てられたバッファーがある場合は、バッファーの開始 (先頭) と終了 (最後の後) へのポインターを並列アルゴリズムに渡します。実行ポリシーとバッファーが同じキューに対して作成されていることを確認します。

#include <oneapi/dpl/execution>
#include <oneapi/dpl/algorithm>
#include <CL/sycl.hpp>
int main(){
  sycl::queue q;
  const int n = 1000;
  int* d_head = sycl::malloc_device<int>(n, q);
  
  std::fill(oneapi::dpl::execution::make_device_policy(q), d_head, d_head + n, 42);
  sycl::free(d_head, q);
  return 0;
}

あるいは、std::vector と USM アロケーターを併用できます。

#include <oneapi/dpl/execution>
#include <oneapi/dpl/algorithm>
#include <CL/sycl.hpp>
int main(){
  const int n = 1000;
  auto policy = oneapi::dpl::execution::dpcpp_default;
  sycl::usm_allocator<int, sycl::usm::alloc::shared> alloc(policy.queue());
  std::vector<int, decltype(alloc)> vec(n, alloc);

  std::fill(policy, vec.begin(), vec.end(), 42);

  return 0;
}

ホスト側の std::vector の使用

インテル® oneDPL 並列アルゴリズムは、以下のコード例に示すように、通常の (ホスト側の) イテレーターを使用して呼び出すことができます。この場合、一時的な SYCL* バッファーが作成され、このバッファーにデータがコピーされます。デバイス上で一時バッファーの処理が完了すると、データはホストにコピーバックされます。ホストとデバイス間のデータコピーを軽減するため、SYCL* バッファーを使用することを推奨します。次に例を示します。

#include <oneapi/dpl/execution>
#include <oneapi/dpl/algorithm>
#include <vector>
int main(){
  std::vector<int> v( 1000 );
  std::fill(oneapi::dpl::execution::dpcpp_default, v.begin(), v.end(), 42);
  // each element of vec equals to 42
  return 0;
}

DPC++ 実行ポリシーによるエラー処理

DPC++ のエラー処理モデルは、2 種類のエラーをサポートしています。同期エラーの場合、DPC++ ホスト・ランタイム・ライブラリーは例外をスローしますが、非同期エラーは DPC++ キューに関連付けられたユーザー提供のエラーハンドラーでのみ処理されます。

DPC++ ポリシーで実行されるアルゴリズムでは、同期または非同期にかかわらず、すべてのエラーを処理するのは呼び出し元の責任です。

DPC++ 非同期エラーを処理するためには、エラー・ハンドラー・オブジェクトで DPC++ ポリシーに関連付けられたキューを作成する必要があります。定義済みポリシー・オブジェクト (dpcpp_default など) にはエラーハンドラーがないため、非同期エラーを処理する必要がある場合は使用しないでください。

制約事項

DPC++ 実行ポリシーで使用される場合、インテル® oneDPL アルゴリズムは、以下のような DPC++ と同じ制限を適用します (詳細は DPC++ 仕様と SYCL* 仕様を参照)。

既知の制限

transform_exclusive_scantransform_inclusive_scan アルゴリズムでは、単項演算の結果は、初期値が提供されている場合は初期値の型に、そうでない場合は処理されたデータシーケンスの値の型に変換可能でなければなりません (std::iterator_traits<IteratorType>::value_type)。

oneDPL を使用したコードのビルド

以下の手順に従って、インテル® oneDPL でコードをビルドします。

  1. インテル® oneAPI DPC++/C++ コンパイラーでビルドする場合、詳細は「インテル® oneAPI DPC++/C++ コンパイラー入門」を参照してください。
  2. インテル® oneDPL とインテル® oneTBB の環境を設定します。
  3. デバイス・ポリシー・オブジェクトの名前を明示的に指定しない場合は、–fsycl-unnamed-lambda オプションを追加します。

以下は、Linux*上でインテル® oneDPL 並列アルゴリズムを含むコードをコンパイルするためのコマンドラインの例です (コードによっては [] 内のパラメーターは不要な場合があります)。

dpcpp [–fsycl-unnamed-lambda] test.cpp [-ltbb] -o test