ArrayFire* と oneAPI、各種ライブラリー、OpenCL* の相互運用性

インテル® oneAPI

この記事は、The Parallel Universe Magazine 47 号に掲載されている「ArrayFire Interoperability with oneAPI, Libraries, and OpenCL™ Code」の日本語参考訳です。原文は更新される可能性があります。原文と翻訳文の内容が異なる場合は原文を優先してください。


parallel_v47_03

oneAPI は、ヘテロジニアス・アクセラレーター向けの開発を大幅に簡素化します。一度コードを記述したらどこでも実行できるようにコードを開発する強力な方法を提供します。ArrayFire* (英語) は、多くの計算ドメインで有用な関数の膨大なコレクションを提供している GPU ライブラリーです。ArrayFire* は、oneAPI がソフトウェア開発の世界にもたらす先見性を共有します。この記事では、oneAPI ディープ・ニューラル・ネットワーク (oneDNN) (英語) ライブラリーと SYCL* ベースのデータ並列 C++ (DPC++) プログラミング言語を、既存のコードベースに統合する方法を探ります。そして、プログラマーが oneAPI を活用することで、新しいプログラミング・モデルへの移行時にしばしば必要となるコードの書き換えを回避できるようにします。

SYCL* との相互運用性

oneAPI は、クロスアーキテクチャーの並列プログラミングを簡素化する DPC++ とライブラリーの組み合わせです。ライブラリーは、DPC++ 言語と緊密に統合されています。どちらも、さまざまな方法で OpenCL* 実装との相互運用性を提供します。ベースとなる言語では、ほとんどのユースケースをカバーする 3 つの主要な OpenCL* との相互運用性が提供されています (図 1)。既存のコードから SYCL* へ、またはその逆の相互運用関数のフローがあります。

  1. カーネル文字列からカーネル・オブジェクトを作成して、DPC++ コード内で既存の OpenCL* カーネルを使用します。
  2. 既存の SYCL* オブジェクトから OpenCL* オブジェクトを抽出します。
  3. 既存の OpenCL* オブジェクトから SYCL* オブジェクトを作成します。


図 1. SYCL* と OpenCL* の相互運用性

これらの相互運用オプションによって既存の ArrayFire* コードベースを統合する方法を考えてみます。最初のケースでは、ArrayFire* カーネルを直接再利用することができます (図 1 の左)。

queue q{gpu_selector()}; // GPU をターゲットとするコマンド キューを作成
program p(q.get_context()); // q と同じコンテキストからプログラムを作成
// R" で示される C++ 生文字列として表現された OpenCL* vecAdd カーネルをコンパイル
p.build_with_source(R"( __kernel void existingArrayFireVecAdd(__global int *a,
                                                              __global int *b,
                                                              __global int *c)
    {
        int i = get_global_id(0);
        c[i] = a[i] + b[i];
    } )");
// ここにバッファーを記述 ...
q.submit([&](handler& h) {
    // ここにアクセサーを記述...
    // カーネルへの引数としてバッファーを設定
    h.set_args(A, B, C);
    // p プログラム・オブジェクトから N 要素に対して vecAdd カーネルを起動
    h.parallel_for(range<1> (N), p.get_kernel("vecAdd"));
});

実際には、ArrayFire* カーネルは単純なバッファーよりも複雑なデータ構造を利用するため、CL 文字列をコピー & ペーストするだけでカーネルを再利用できるわけではありません。ほかの 2 つの方法のいずれかで、データ交換を処理する必要があります。

SYCL* オブジェクトから OpenCL* コンポーネントを抽出する 2 つ目の方法 (図 1 の中央) は、既存の SYCL* オブジェクトに .get() を使用するだけの単純なものです。SYCL* オブジェクトの各呼び出しは、対応する OpenCL* オブジェクトを返します。例えば、cl::sycl::queue::get() は OpenCL* の cl_command_queue を返します。

3 つ目の方法 (図 1 の右) は、既存の OpenCL* オブジェクトを取り込み、それを使用して SYCL* オブジェクトを作成します。sycl::queue::queue(cl_command_queue,…) などの SYCL* オブジェクトのコンストラクターを使用します。この場合、コンストラクターは、構築時に OpenCL* リソースの参照カウントを増やすため OpenCL* インスタンスを維持し、SYCL* オブジェクトを破棄する際にインスタンスを解放します。


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

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

タイトルとURLをコピーしました