AOTコンパイルの利用
tfcompileとは?
tfcompile
は、TensorFlowのグラフを実行コードへ事前に(AOT)コンパイルするための標準ツールです。 全体のバイナリサイズを減らすことに加えて、いくつかの実行時オーバヘッドを無くすことができます。 tfcompile
の典型的な使い方は、推論用の計算グラフをモバイルデバイス向けの実行コードへコンパイルすることです。
TensorFlowのグラフは、通常TensorFlowのランタイムによって実行されます。 これは、グラフの各ノードを実行することになるため、実行時オーバヘッドを招きます。 TensorFlowのグラフやランタイムのコードが必要になるため、全体のバイナリサイズも大きくなります。 tfcompile
よって生成される実行コードは、TensorFlowのランタイムを使わず、実際に計算で使用するカーネルのみに依存します。
コンパイラは、XLAフレームワークの上に作られています。 TensorFlowとXLAフレームワークをつなぐコードは、tensorflow/compiler に存在します。
tfcompileは何をするか?
tfcompile
は、TensorFlowの概念であるfeedとfetchによって形作られるサブグラフを受け取り、サブグラフを満たすファンクションを生成します。 feeds
はファンクションの入力引数、fetches
はファンクションの出力引数です。 刈り込んだ結果のサブグラフは、PlaceholderとVariableノードを含めることができないため、すべての入力はfeedによって指定される必要があります。 すべてのPlacerholderとVariableは、feedとして指定されるのは共通であり、最終的なサブグラフはこれらのノードを一切含みません。 生成されたファンクションは、ファンクションのシグネチャをエクスポートするヘッダファイルと、実装を含むオブジェクトファイルからなる cc_library
としてパッケージ化されます。 ユーザーは、生成されたファンクションを適切に呼び出すコードを書きます。
tfcompileの利用
このセクションでは、tfcompile
を使ってTensorFlowのサブグラフから実行可能バイナリを生成するための、高レベルのステップを詳しく述べます。 ステップは、以下からなります。
ステップ1: コンパイルするサブグラフを構成する
ステップ2: サブグラフをコンパイルするための
tf_library
ビルドマクロを利用するステップ3: サブグラフを呼び出すコードを書く
ステップ4: 最終的なバイナリを作成する
ステップ1: コンパイルするサブグラフを構成する
生成されたファンクションの入力および出力引数に相当する、feedとfetchを決めます。 そして、tensorflow.tf2xla.Config
の feeds
および fetches
を設定します。
ステップ2: サブグラフをコンパイルするためのtf_libraryビルドマクロを利用する
このステップでは、tf_library
ビルドマクロを利用して、グラフを cc_library
に変換します。 cc_library
は、グラフから生成されたコードを含んだオブジェクトファイルと、生成されたコードにアクセスするためのヘッダファイルから構成されます。 tf_library
は、TensorFlowのグラフを実行コードにコンパイルするための tfcompile
を利用しています。
この例で使用するGraphDef (test_graph_tfmatmul.pb)を生成するためには、--out_dirフラグを使って出力場所を指定した状態で make_test_graphs.py を実行してください。
典型的なグラフとして、学習時に学習される重みを表現する Variables
を含んだものがありますが、tfcompile
は Variables
を含んだサブグラフをコンパイルできません。 ツール freeze_graph.py は、チェックポイントファイルに保存された値を使って、Variablesを定数に変換します。 tf_library
マクロは便宜上、このツールを実行する freeze_checkpoint
引数をサポートします。 tensorflow/compiler/aot/tests/BUILD には、より多くの例があります。
コンパイルされたサブグラフに現れた定数は、直接生成されるコードへコンパイルされます。生成されたファンクションに定数を渡すためには、これらをコンパイルしてしまうのではなく、単にfeedとして渡します。
tf_library
ビルドマクロに関する詳細は、tfcompile.bzl を参照してください。
基本となる tfcompile
ツールの詳細については、tfcompile_main.cc を参照してください。
ステップ3: サブグラフを呼び出すコードを書く
このステップでは、生成されたコードを呼び出すために、前ステップにおける tf_library
ビルドマクロによって生成されたヘッダファイル(test_graph_tfmatmul.h
) を使います。 ヘッダファイルは、ビルドパッケージとおなじく bazel-genfiles
に配置され、tf_library
ビルドマクロに設定されたnameアトリビュートに基づいて命名されます。 たとえば、test_graph_tfmatmul
のために生成されるヘッダは、test_graph_tfmatmul.h
になるでしょう。 以下は、生成されたものに関する省略版です。 bazel-genfiles
に生成されたファイルは、役立つ付加的なコメントを含みます。
生成されたC++クラスは、tf_library
マクロの cpp_class
に指定したとおり、名前空間 foo::bar
における MatMulComp
になります。 生成されたすべてのクラスは、引数と結果のためのバッファを扱うメソッドが異なるのみで、おなじAPIを持ちます。 これらのメソッドは、tf_library
マクロの引数である feed
と fetch
に指定するバッファの数や型によって違いが発生します。
生成されたクラスでは、3つのバッファが管理されます。 args
は入力、results
は出力、temps
は計算を実行するために内部で利用する一時的なバッファを表しています。 デフォルトでは、生成されたクラスのそれぞれのインスタンスは、これらのすべてのバッファを確保して管理します。 コンストラクタの引数 AllocMode
はこの振る舞いを変えるために使うことができます。 すべてのバッファは、64バイトの境界にアライメントされています。
生成されたC++クラスは、XLAによって生成された低レベルのコードを単にラッパするクラスです。
tfcompile_test.cc
をもとに生成されたファンクションを呼び出す例:
ステップ4: 最終的なバイナリを作る
このステップでは、ステップ2における tf_library
によって生成されたライブラリと、ステップ3で書いたコードを組み合わせて最終的なバイナリを作ります。 以下は、bazel
BUILDファイルの例です。