この記事は、https://www.oneapi.io/spec/ で 2023年9月14日に公開された『oneAPI 1.3 Provisional Specification Rev. 1』 (HTML、PDF) をベースにしています。原文は2000 ページ近くあり、翻訳の時間とリソースも限られるため、全文翻訳ではなく、記事形式で区切った仕様とその解説を提供することにしました。
この回では、『oneAPI 1.3 Provisional Specification Rev. 1』の「oneDNN」の「Graph extension」の節を取り上げています。
グラフ拡張
oneDNN グラフ拡張機能は、スケーラブルな方法で操作の融合を最大化する柔軟なグラフ・インターフェイスです。oneDNN グラフ API は、完全な計算グラフを入力として受け入れ、エンジン対応のグラフのパーティション化を実行します。ここで、融合の候補となる操作のサブグラフがグループ化されます。これらのパーティションはコンパイルされ、融合された操作として実行されます。
共通定義
ここでは、すべてもしくは複数のグラフ操作で使用される一般的なタイプと定義を示します。
共通定義 については、こちら (英語) をご覧ください。
プログラミング・モデル
oneDNN グラフ・プログラミング・モデルを使用すると、ユーザーは計算グラフを渡すことでパーティションを取得できます。次に、ユーザーはパーティションをコンパイルし、テンソルデータをバインドして、コンパイルされたパーティションを実行します。パーティションは oneDNN の実装によって決定され、スケーラビリティー (新しい融合パターンの利点を得るためコードを変更する必要はありません) とプラットフォーム対応のパーティション化が可能になります。
このプログラミング・モデルは、ディープラーニング (DL) フレームワークまたは推論エンジンをサポートすることを想定しています。DL フレームワークには、計算グラフ固有の表現があります。oneDNN グラフ API は、フレームワーク・グラフからグラフ・パーティションをオフロードまたは高速化するために使用されます。次の説明では、「グラフ」は oneDNN グラフ実装で構築されたグラフを指し、「フレームワーク・グラフ」は DL フレームワークによって構築されるグラフを指します。
ディープラーニング計算グラフは、ディープ・ニューラル・ネットワーク (DNN) 操作で構築されます。DNN 操作は、入力データを受け取り、出力データを返す関数です。入力データと出力データは、テンソルと呼ばれる多次元配列です。DNN 操作は複数のテンソルから複数のテンソルを生成する場合もあります。テンソルは一度の操作で生成する必要があり、複数の操作で消費される場合があります。
oneDNN グラフ API は、論理テンソル、操作 (OP)、およびグラフを使用して計算グラフを表現します。論理テンソルは、要素のデータタイプ、形状、レイアウトなどテンソルのメタデータを表します。操作 (OP) は計算グラフ上の操作を表します。OP には、種別、属性、入力および出力論理テンソルがあります。OP はグラフに追加されます。OP と論理テンソルには一意の ID が含まれるため、グラフは論理テンソルを介して生産 OP を消費 OP に接続する方法を認識できます。構築されたグラフは不変的です。グラフ・オブジェクトを作成する目的は、パーティションを取得することです。パーティションが作成されると、グラフ・オブジェクトは使用できなくなります。パーティションを取得した後は、グラフに OP を追加してはなりません。
oneDNN グラフは操作のセットを定義します。ユーザーは、グラフを構築するため DNN 操作定義を oneDNN グラフ操作に変換する必要があります。oneDNN グラフ操作セットに含まれない操作には、ユーザーはワイルドカード OP を使用できます。ワイルドカード OP はすべての OP を表現できます。入出力論理テンソルを使用すると、oneDNN グラフ実装は完全なグラフを受け取って、完全な解析が可能になります。ユーザーは、グラフの出力テンソルを表現するため特殊な End 操作を使用する必要があります。グラフの実行後にテンソルがアクティブである必要がある場合は、テンソルを消費する End 操作に接続する必要があります。ユーザーは 1 つのグラフに対し、複数の End 操作を持つことができます。ユーザーがグラフに追加する操作は、それぞれ入力と出力論理テンソルを記述する必要があります。また、ユーザーは各論理テンソルのデータタイプも記述しなければなりません。テンソルの形状とレイアウトが判明している場合、ユーザーは論理テンソルとともにそれらも記述する必要があります。
パーティションはグラフ内で接続されたサブグラフです。oneDNN グラフ実装ではグラフを解析し、パーティションの数を返します。返されたパーティションは、グラフのすべての操作を完全にカバーし、トポロジーの順番に従います。パーティションは通常複数の操作を含んでいます。ワイルドカード操作やサポートされない操作など、パーティションに操作が 1 つのみ含まれる場合があります。パーティションには、そのパーティションがサポートされるかどうか (コンパイルおよび実行できるか) を示すフラグが含まれています。ユーザーはパーティションを使用する前にフラグをチェックする必要があります。
パーティションの入力と出力はポートとも呼ばれます。ポートは、グラフの構築中に渡された論理テンソルの情報を記録します。論理テンソル ID を使用することで、パーティション間の生産と消費の関係を追跡できます。ポートは、対応する論理テンソルのデータタイプも記録します。
ユーザーに返されたパーティションは依存関係を持っていてはなりません。例えば、グラフが 3 つの操作、A、B、および C を含んでいる場合について考えます。C が A の出力を消費し、B の入力を生成する場合、oneDNN グラフ実装では A と B を同一パーティションに含めてはなりません。ただし、C がグラフに追加されない場合、C は oneDNN グラフ実装には現れないため、返されるパーティションには A と B が含まれる可能性があります。この場合、依存関係サイクルを特定するのはユーザーの責任です。ユーザーが完全なグラフを作成して渡した後は、oneDNN グラフが返すパーティション間の依存関係サイクルを確認する必要はありません。
パーティションは実行前にコンパイルする必要があります。コンパイルすることにより、計算ロジックがハードウェア ISA レベルにブレークダウンされバイナリーコードが生成されます。生成されたコードは、入力および出力テンソルのメタデータに特化されています。ユーザーは、コンパイル API を使用して完全なメタデータを渡すため、新しい論理テンソルを作成する必要があります。論理テンソルは、ID、データタイプ、形状 (出力の場合不完全にすることも可能)、およびレイアウトを完全に指定する必要があり、コンパイルは成功するはずです。コンパイル時に渡される論理テンソルは、ID とパーティションのポートを一致させなければなりません。論理テンソルは、同じ ID のポートと同じデータタイプを持つ必要があります。
出力論理テンソルでは、各テンソルの次元サイズとストライドによってパブリックレイアウトを指定するか、oneDNN グラフ実装を要求してターゲット固有のレイアウトを決定する必要があります。入力論理テンソルの場合、パブリックレイアウトを指定するか、先行するパーティションのコンパイルで生成されたターゲット固有のレイアウトを使用する必要があります。論理テンソルがターゲット固有のレイアウトを持つ場合、パーティションによって作成され、そのパーティションでのみ使用されなければなりません。
コンパイルされたパーティションは、ターゲット・ハードウェアに特化して生成されたコードと、コンパイル API で渡されるテンソルデータを表します。ユーザーは、コンパイルされたパーティションをキャッシュすることで、反復のコンパイルコストを償却することができます。テンソルのメタデータが同一である場合、前の反復で生成されたコンパイル済みのパーティションが再利用される可能性があります。実装では、コンパイルされたパーティションを内部的にキャッシュすることで、パーティションのコンパイルコストを削減できます。このような最適化はこの仕様ではカバーされません。
コンパイルされたパーティションを実行するには、入力テンソルと出力テンソルを渡す必要があります。入力テンソルは、入力データバッファーを論理テンソルにバインドしなければなりません。ユーザーは、コンパイル済みパーティションの出力データバッファーのサイズを照会できます。サイズが判明している場合、出力データバッファーを割り当てて、出力テンソルにバインドできます。サイズが不明の場合、oneDNN グラフ実装にアロケーターを提供して、出力テンソルバッファーを割り当てなければなりません。実行 API は、コンパイルされたパーティションと入力テンソルを受け取り、更新されたデータバッファーの出力テンソルを返します。
エンジンは、システム内のターゲットデバイスとコンテキストを返します。それらは、パーティションのコンパイル・パラメーターして渡す必要があります。ストリームは、ターゲットデバイスのハードウェア実行リソースを抽象化します。これは、コンパイル済みのパーティションを実行する際に必要となります。
上図は、主要なプログラミングの概念と、それらが相互にどのように作用するかまとめたものです。矢印は、デスティネーションがソース・オブジェクトを含むか、使用していることを示します。例えば、操作 (OP) には論理テンソルが含まれており、コンパイル済みパーティションはパーティションを使用します。
論理テンソル
「論理テンソル」は、要素のデータタイプ、次元数、各次元のサイズ、レイアウトなどの入力または出力テンソルのメタデータを記述します。
論理テンソルは、oneDNN グラフ実装によるグラフの構築を支援するだけでなく、ユーザーと oneDNN グラフ実装間でテンソルのメタデータ情報を交換する役割を果たします。ユーザーは入力テンソルの形状情報を渡し、パーティションから出力テンソルの推論形状を取得します。ユーザーは、論理テンソルをコンパイル API に渡して形状とレイアウト情報を指定します。また、ユーザーは特殊な論理テンソルを使用して、oneDNN グラフ実装が出力テンソルのレイアウトを決定できるようにします。コンパイルが終了すると、ユーザーはコンパイル済みパーティションに対して出力テンソルの形状、レイアウト、およびサイズを照会できます。
それぞれの論理テンソルは ID を持っています。テンソルのメタデータには、実行中のフレームワーク・グラフの新しい形状の情報が含まれることがあります。論理テンソルは変更できないため、ユーザーは同じ ID を使用して新しい論理テンソル作成し、新しい追加情報を oneDNN グラフ実装に渡す必要があります。また、ユーザーは論理テンソル ID が、論理テンソルが属するグラフ内で一意であることを保証しなければなりません。
操作
操作 (または OP) は、ディープ・ニューラル・ネットワークの操作を記述します。OP は、種別、属性、入力および出力論理テンソルの形状とプロパティーを含みます。特に、アクティブ化および重みテンソルの形状は、操作の属性として指定されます。
各操作には一意の ID があり、ユーザーは OP が追加されるグラフ内での一意性を保証する必要があります。
グラフ
「グラフ」は、一連の操作 (OP) を含みます。dnnl::graph::graph::add_op()
は、OP と論理テンソルをグラフに追加します。oneDNN グラフ実装は、OP と論理テンソルを累積し、内部状態としてグラフを構築および検証します。dnnl::graph::graph::add_op()
中にターゲット OP はそのスキーマに対し保証されます。検証か失敗すると、API から例外がスローされます。allow_exception=false
が指定されると、dnnl::graph::graph::add_op()
呼び出しはステータスを返します。API の戻り値を確認したり、例外を処理することでエラーを処理するのはユーザーの責任です。
同じ論理テンソルは、生産 OP と消費 OP とともに渡されるため、dnnl::graph::graph::add_op()
呼び出しで 2 回以上現れる可能性があります。oneDNN グラフ実装は、同じ ID を持つ論理テンソルがグラフ構築時に同一であることを保証します。
グラフが記述されたら dnnl::graph::graph::finalize()
を呼び出す必要があります。これにより、ほかの操作が追加されなくなり、そのグラフのパーティションのセットを取得するため dnnl::graph::graph::get_partitions()
の呼び出しが可能になります。パーティション化後のグラフは、ユーザーから見ると何の意味も持ちませんが、ユーザーが解放する必要があります。
グラフに追加されたすべての OP は、返されたパーティションのいずれかに含まれます。OP が oneDNN グラフ API 実装でサポートされない場合、対応するパーティションは 「not supported (サポートされない)」 とマークされます。ユーザーは、dnnl::graph::partition::is_supported
を介してパーティションのサポート状況を確認できます。パーティションは、グラフ内で循環する依存関係を持ってはなりません。ユーザーが完全なグラフを提供できない場合、oneDNN グラフ実装に渡されないパーティションと操作間の依存関係サイクルを検出するのはユーザーの責任です。
グラフの構築で渡されるテンソルには、不完全な情報が含まれている可能性があります。次元や形状に関する情報は空間的には既知です。完全な情報は必須ではありませんが、完全であれば oneDNN グラフが適切なパーティションを決定するのに役立ちます。操作 (op) をグラフに追加するのはスレッドセーフではありません。ユーザーは、同じスレッドでグラフを作成し、op を追加して、パーティションを取得する必要があります。
パーティション
「パーティション」は、コンパイルと実行の基本単位である oneDNN グラフ実装が識別する OP のコレクションを表します。これには、OP、入力ポート、出力ポートのリスト、およびパーティションがサポートされるかを示すフラグが含まれます。パーティションが作成されると ID が割り当てられます。oneDNN グラフの実装では、パーティション ID がグローバルで一意であることを保証する必要があります。
ユーザーは、不完全な形状 (DNNL_GRAPH_UNKNOWN_NDIMS
または DNNL_GRAPH_UNKNOWN_DIM
を含む) を含む出力論理テンソルをパーティションのコンパイル API に渡すことができます。oneDNN グラフの実装では、指定された入力形状と OP のスキーマに従って出力形状を計算する必要があります。コンパイルが終了すると、入力および出力論理テンソルの完全な形状情報を含むパーティションが生成されます。ユーザーは、コンパイル済みパーティションに対して出力論理テンソルを照会し、形状を取得できます。
パーティションをコンパイルして、compiled_partition
を生成できます。これは、パーティションの計算を実行する実行可能オブジェクトです。ユーザーは、追加のテンソルメタデータをパラメーターとしてコンパイル API に渡すため、入力論理テンソルリストと出力論理テンソルリストを作成する必要があります。入力および出力論理テンソルは、グラフのパーティション化中に論理テンソルの情報を取得するパーティションのポートと ID に一致する必要があります。通常、パーティション・ステップの前に多くの情報 (次元数やテンソル次元など) を指定すると、コンパイル済みパーティションのコードのパフォーマンスは最も高くなります。
ユーザーは、パラメーター論理テンソルの layout_type
として、strided
、any
、または opaque
を指定する必要があります。ユーザーが論理テンソルに any
を指定すると、そのテンソルは出力テンソルである必要があり、oneDNN グラフ実装はコンパイル済みパーティションの最高のパフォーマンスをもたらすレイアウトを決定します。strided
の場合、論理テンソルで記述されたパブリック・データ・レイアウトを使用する必要があります。opaque
では、パラメーター論理テンソルにターゲット固有のレイアウトが含まれており、これはテンソルを生成する先行パーティションのコンパイルで決定される必要があります。行優先のレイアウトが連続していると、コンパイルは成功します。レイアウトにストライドが含まれる場合、コンパイルの成功は実装に依存します。特定の形状やランクの次元が不明である場合、コンパイルの成功は実装に依存します。不明な次元やランクのコンパイルが成功すると、コンパイル済みパーティションは実行時にその次元やランクの任意の値を処理できなければなりません。
テンソル
「テンソル」は、コンパイル済みパーティションの実行に必要な多次元の入出力データを抽象化したものです。テンソルには、論理テンソル、エンジン、データハンドルが含まれます。
ユーザーは、テンソルのライフサイクルを管理する責任があり、割り当てられたリソースを使用しなくなったら解放しなければなりません。
コンパイル済みパーティション
「コンパイル済みパーティション」は、ターゲット・ハードウェアに特化して生成されたコードと、パラメーター論理テンソルで記述されたメタデータを表します。コンパイル済みパーティションには、ターゲット固有のコンパイル済みオブジェクトであることを示すパーティションとハンドルが含まれます。
コンパイル API を呼び出した後、ユーザーはコンパイル済みパーティションの論理出力テンソルを照会して、出力テンソルのレイアウト ID とサイズを取得する必要があります。レイアウト ID は、ターゲット固有のレイアウトを持つ不明瞭な識別子です。ユーザーは、次のパーティションのコンパイルにレイアウト ID を渡して、特定の入力レイアウトをターゲットに最適化できます。そのサイズを使用して、実行向け出力テンソルのメモリーバッファーを割り当てることができます。
フレームワークは、パラメーターとしてテンソルとコンパイル済みパーティションを実行 API に渡します。パラメーター論理テンソルは、コンパイル API に渡す際に同じ順番でなければならず、その ID はコンパイル済みパーティションの内部論理テンソルと一致する必要があります。それぞれのテンソルのレイアウトは、strided
または opaque
である必要があります。
コンパイル済みパーティションは、メモリー容量を削減してデータの局所性を高めるため、出力テンソルの入力テンソル・データ・バッファーを再利用するインプレース最適化をサポートする必要があります。ユーザーは、コンパイル済みパーティションごとに入力と出力ポートのペアを取得できます。入力と出力ポートのペアは、実行 API と入力テンソルおよび出力テンソルを渡す際に同じメモリーバッファーを使用できます。インプレース最適化はオプションであり、ユーザーが出力テンソルに異なるメモリーバッファーを使用する場合、oneDNN グラフは出力テンソルを更新する必要があります。
ユーザーがデータ・バッファー・ポインターを含むテンソルを出力に配置すると、バックエンドはユーザーが提供するデータバッファーを使用します。
ユーザーは、パブリックレイアウトのパラメーター・テンソルをコンパイル済みパーティションで予測されるターゲット固有のレイアウトに変換できます。ディープラーニング推論における一般的な最適化は、ユーザーがコンパイル済みパーティションに必要なターゲット固有のレイアウトに対し重みを事前にパックして、後で使用するため並べ替えられた重みをキャッシュすることです。
エンジン
「エンジン」 (dnnl::engine
) は、計算デバイスを抽象化します。さらに、グラフ拡張機能を使用することで、特定のホスト/デバイス・アロケーターを備えたエンジンを作成して、グラフ API 呼び出し内のメモリーを容易に管理できるようになります。
ストリーム
ストリーム (dnnl::stream
) は、特定のエンジンに関連付けられた実行コンテキストをカプセル化します。
一般的な API の注意
oneDNN グラフ・オブジェクトの動作には次のような前提条件があります。
論理テンソルは自明なタイプと同様に動作します。
他のすべてのオブジェクトは共有ポインターのように動作します。そしてコピーは常にシャロ―です。
エラー処理
C++ API はエラー処理の例外をスローします。
法務上の注意書き
The content of this oneAPI Specification is licensed under the Creative Commons Attribution 4.0 International License (英語). Unless stated otherwise, the sample code examples in this document are released to you under the MIT license (英語).
This specification is a continuation of Intel’s decades-long history of working with standards groups and industry/academia initiatives such as The Khronos Group*, to create and define specifications in an open and fair process to achieve interoperability and interchangeability. oneAPI is intended to be an open specification and we encourage you to help us make it better. Your feedback is optional, but to enable Intel to incorporate any feedback you may provide to this specification, and to further upstream your feedback to other standards bodies, including The Khronos Group SYCL* specification, please submit your feedback under the terms and conditions below. Any contribution of your feedback to the oneAPI Specification does not prohibit you from also contributing your feedback directly to other standard bodies, including The Khronos Group under their respective submission policies.
By opening an issue, providing feedback, or otherwise contributing to the specification, you agree that Intel will be free to use, disclose, reproduce, modify, license, or otherwise distribute your feedback at its sole discretion without any obligations or restrictions of any kind, including without limitation, intellectual property rights or licensing obligations.
This document contains information on products, services and/or processes in development. All information provided here is subject to change without notice.
© Intel Corporation. Intel、インテル、Intel ロゴ、その他のインテルの名称やロゴは、Intel Corporation またはその子会社の商標です。
* その他の社名、製品名などは、一般に各社の表示、商標または登録商標です。