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ファイルの例です。