レベルゼロとは

同カテゴリーの次の記事

oneAPI GPU 最適化ガイド日本語版公開

この記事は、The Parallel Universe Magazine 52 号に掲載されている「Solve Heterogeneous Programming Challenges with Fortran and OpenMP*」の日本語参考訳です。原文は更新される可能性があります。原文と翻訳文の内容が異なる場合は原文を優先してください。


parallel_v52_08

この記事では、レベルゼロ (英語) のアプリケーション・インターフェイス、目的、およびビジョンの概要を説明します。レベルゼロの基本的なアーキテクチャーと、計算ユニットリソースへの低レベルのアクセス制御の利点を紹介します。レベルゼロは、OpenMP* (英語) や SYCL* (英語) のような言語拡張とともに使用できます。レベルゼロと SYCL* C++ 言語拡張抽象化レイヤーの間の相互作用がアプリケーション・ソフトウェア開発者にどのように明らかにされるか簡単に説明します。

レベルゼロは、任意の数のオフロードデバイスへのアクセスを設定および管理する低レベル API として設計されています。プログラムフローに干渉することなく、C++ 標準規格に準拠したヘテロジニアス計算を行うことができる抽象化レイヤーも提供します。この機能により、異なるランタイム環境の間でコードを移植できます。レベルゼロ API バックエンドを考慮することにより、SYCL* や OpenMP* 言語拡張の抽象化を超えて、制御のレベルを向上できます。

レベルゼロ・インターフェイス (英語) は、oneAPI 仕様 (英語) の一部です。CPU、GPU、およびアクセラレーターへのベアメタルアクセスにより、oneAPI の API ベースのプログラミング・モデルとダイレクト・プログラミング・モデルを補完します。インテル® oneAPI ベース・ツールキットの一部としてインテル® GPU をターゲットにしたインテルのリファレンス実装 (英語) とインテル® oneAPI DPC++/C++ コンパイラーでの使用法も文書化されています。この記事を読めば、レベルゼロランタイムの使用を開始したり、独自のランタイムを検討するために必要なことが分かるでしょう。

ヘテロジニアス・コンピューティングの能力を引き出す

レベルゼロのインテルの最初の実装はインテル® GPU をターゲットにしていますが、レベルゼロのビジョンと可能性は非常に広範であり、特定のデバイス要件に合わせて調整された抽象化を作成することが可能です。関数ポインター、メモリー、I/O など、幅広い言語機能をサポートするように適合させることができます。API は、CPU、GPU、フィールド・プログラマブル・ゲート・アレイ (FPGA)、その他のアクセラレーター・アーキテクチャーを含む、さまざまな計算デバイスで動作するように設計されています。

レベルゼロは、OpenCL* や Vulkan* などのほかの低レベル API と共存できます。ただし、高レベルの oneAPI および SYCL* 開発者の経験がハードウェアに依存せず、可能な限りアーキテクチャーに依存しないで柔軟になるように、独立して進化することを意図しています。SYCL* を利用する高レベルのランタイム API とライブラリーで必要となる明示的な制御も提供します。レベルゼロは完全なオープンソースで、その仕様 (英語)、ソース・リポジトリー (英語)、およびインテル® GPU 計算ランタイムの実装 (英語) は、GitHub* で公開されています。

つまり、レベルゼロは、ヘテロジニアス・コンピューティングを広く開放し、オフロード計算の選択を実現する、柔軟なオープン・バックエンドを提供します。システムレベルのインターフェイスを明示的に制御する、次のような機能を提供します。

  • デバイス検出
  • メモリー割り当て
  • ピアツーピア通信
  • プロセス間共有
  • カーネルのサブミット
  • 非同期実行とスケジューリング
  • 同期プリミティブ
  • メトリックレポート
  • システム管理

レベルゼロの主要な概念から始めましょう。

レベルゼロの基本

レベルゼロは、アプリケーション層の下に位置していて、C++ アプリケーションとターゲット・デバイス・プロパティーの間の抽象化インターフェイスとして採用できます (図 1)。CPU やその他の計算デバイスが含まれることがあります。開発者は共有デバイスのリソースとシームレスにやり取りして、レベルゼロドライバーがサポートする特定のデバイスにワークロードをディスパッチできます。ドライバーは、サポートされているデバイスを利用可能なデバイスリストに追加し、任意の SYCL* キューをそのデバイスにマップしてワークを送信できます。特定のデバイス・プロパティーや複数のレベルゼロデバイス間で共有されるリソースにアクセスする必要がない場合、それらのデバイスは異なる SYCL* バックエンド API を使用するほかのデバイスと同じように動作します。


図 1. oneAPI バックエンド・アーキテクチャーのコンテキストのレベルゼロ・インターフェイス

レベルゼロの真の強みは、デバイス固有のメモリー共有または同期コンテキスト・オブジェクトに対する低レベルの制御とサポートです。デバイスの透明性が向上するだけでなく、レベルゼロ API にヘテロジニアス・オフロード・ターゲット・デバイスの構成可能性が追加されます。

レベルゼロデバイスの検出と選択の一連の流れを次に示します。

レベルゼロローダー

オフロードデバイスまたはアクセラレーターへのアクセスは、レベルゼロローダーから開始します。システムのデバイス向けのレベルゼロドライバーを検出します。ローダー・プロジェクトには、レベルゼロ実装をビルドして操作できるようにするレベルゼロヘッダーとライブラリー (英語) も含まれています。ドライバーの初期化と検出のコードサンプルを次に示します。

// ドライバーを初期化
zeInit(0);

// ドライバー・インスタンスをすべて検出
uint32_t driverCount = 0;
zeDriverGet(&driverCount, nullptr);

ze_driver_handle_t* allDrivers = allocate(driverCount *
                                          sizeof(ze_driver_handle_t));
zeDriverGet(&driverCount, allDrivers);

// GPU デバイスを含むドライバー・インスタンスを検索
ze_driver_handle_t hDriver = nullptr;
ze_device_handle_t hDevice = nullptr;
for(i = 0; i < driverCount; ++i)
{
    uint32_t deviceCount = 0;
    zeDeviceGet(allDrivers[i], &deviceCount, nullptr);
    ze_device_handle_t* allDevices = allocate(deviceCount *
                                              sizeof(ze_device_handle_t));
    zeDeviceGet(allDrivers[i], &deviceCount, allDevices);

    for(d = 0; d < deviceCount; ++d)
    {
        ze_device_properties_t device_properties {};
        device_properties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
        zeDeviceGetProperties(allDevices[d], &device_properties);

        if(ZE_DEVICE_TYPE_GPU == device_properties.type)
        {
            hDriver = allDrivers[i];
            hDevice = allDevices[d];
            break;
        }
    }

    free(allDevices);
    if(nullptr != hDriver)
    {
        break;
    }
}

free(allDrivers);
if(nullptr == hDevice)
    return; // GPU デバイスなし

続いて、メモリー、コマンドキュー、モジュール、同期オブジェクトなどを管理するコンテキスト・オブジェクトを作成します。複数のデバイスで共有できるシステムリソースを管理する場合、コンテキストの使用は特に重要になります。共有メモリーのシナリオの簡単な例を次に示します。

関連記事