Utilizzo della compilazione AOT

Cos'è tfcompile?

tfcompile è uno strumento autonomo che ahead-of-time (AOT) compilazioni tensorflow grafici in codice eseguibile. Può ridurre la dimensione binaria totale e anche evitare alcuni overhead di runtime. Un tipico caso d'uso di tfcompile è compilare un grafico inferenza in codice eseguibile per i dispositivi mobili.

Il grafico TensorFlow viene normalmente eseguito dal runtime di TensorFlow. Ciò comporta un sovraccarico di runtime per l'esecuzione di ciascun nodo nel grafico. Ciò porta anche a una dimensione binaria totale maggiore, poiché il codice per il runtime di TensorFlow deve essere disponibile, oltre al grafico stesso. Il codice eseguibile prodotto da tfcompile non utilizza il runtime tensorflow, e ha solo dipendenze kernel che vengono effettivamente utilizzati nel calcolo.

Il compilatore è costruito sulla base del framework XLA. Il codice colmare tensorflow alle risiede quadro XLA sotto tensorflow / compilatore .

Cosa fa tfcompile?

tfcompile prende un sottografo, identificata dai concetti tensorflow di feed e recuperi, e genera una funzione che implementa che sottografo. Le feeds sono gli argomenti di input per la funzione, ei fetches sono gli argomenti di uscita per la funzione. Tutti gli input devono essere completamente specificati dai feed; il sottografo tagliato risultante non può contenere nodi Segnaposto o Variabile. È comune specificare tutti i segnaposto e le variabili come feed, il che garantisce che il sottografo risultante non contenga più questi nodi. La funzione generata è confezionato come cc_library , con un file di intestazione esportazione della firma funzione, e un file oggetto contenente l'attuazione. L'utente scrive il codice per richiamare la funzione generata in modo appropriato.

Usando tfcompile

Questa sezione descrive passaggi di alto livello per la generazione di un binario eseguibile con tfcompile da un sottografo tensorflow. I passaggi sono:

  • Passaggio 1: configurare il sottografo da compilare
  • Fase 2: Utilizzare la tf_library macro build per compilare il sottografo
  • Passaggio 3: scrivi il codice per richiamare il sottografo
  • Passaggio 4: creare il file binario finale

Passaggio 1: configurare il sottografo da compilare

Identificare i feed e i recuperi che corrispondono agli argomenti di input e output per la funzione generata. Poi configurare le feeds e fetches in un tensorflow.tf2xla.Config proto.

# Each feed is a positional input argument for the generated function.  The order
# of each entry matches the order of each input argument.  Here “x_hold” and “y_hold”
# refer to the names of placeholder nodes defined in the graph.
feed {
  id { node_name: "x_hold" }
  shape {
    dim { size: 2 }
    dim { size: 3 }
  }
}
feed {
  id { node_name: "y_hold" }
  shape {
    dim { size: 3 }
    dim { size: 2 }
  }
}

# Each fetch is a positional output argument for the generated function.  The order
# of each entry matches the order of each output argument.  Here “x_y_prod”
# refers to the name of a matmul node defined in the graph.
fetch {
  id { node_name: "x_y_prod" }
}

Passaggio 2: utilizzare la macro build tf_library per compilare il sottografo

Questo passaggio converte il grafico in un cc_library usando l' tf_library macro accumulo. La cc_library costituito da un file oggetto contenente il codice generato dal grafico, insieme a un file di intestazione che dà accesso al codice generato. tf_library UTILIZZA tfcompile per compilare il grafico tensorflow in codice eseguibile.

load("//tensorflow/compiler/aot:tfcompile.bzl", "tf_library")

# Use the tf_library macro to compile your graph into executable code.
tf_library(
    # name is used to generate the following underlying build rules:
    # <name>           : cc_library packaging the generated header and object files
    # <name>_test      : cc_test containing a simple test and benchmark
    # <name>_benchmark : cc_binary containing a stand-alone benchmark with minimal deps;
    #                    can be run on a mobile device
    name = "test_graph_tfmatmul",
    # cpp_class specifies the name of the generated C++ class, with namespaces allowed.
    # The class will be generated in the given namespace(s), or if no namespaces are
    # given, within the global namespace.
    cpp_class = "foo::bar::MatMulComp",
    # graph is the input GraphDef proto, by default expected in binary format.  To
    # use the text format instead, just use the ‘.pbtxt’ suffix.  A subgraph will be
    # created from this input graph, with feeds as inputs and fetches as outputs.
    # No Placeholder or Variable ops may exist in this subgraph.
    graph = "test_graph_tfmatmul.pb",
    # config is the input Config proto, by default expected in binary format.  To
    # use the text format instead, use the ‘.pbtxt’ suffix.  This is where the
    # feeds and fetches were specified above, in the previous step.
    config = "test_graph_tfmatmul.config.pbtxt",
)

Per generare il proto GraphDef (test_graph_tfmatmul.pb) per questo esempio, eseguire make_test_graphs.py e specificare il percorso di uscita con la bandiera --out_dir.

Grafici tipici contengono Variables che rappresentano i pesi che vengono apprese attraverso la formazione, ma tfcompile non possono compilare un sottografo che contengono Variables . Il freeze_graph.py strumento converte variabili in costanti, utilizzando i valori memorizzati in un file di checkpoint. Per comodità, il tf_library macro sostiene il freeze_checkpoint argomento, che esegue lo strumento. Per ulteriori esempi vedere tensorflow / compilatore / AOT / test / build .

Le costanti che vengono visualizzate nel sottografo compilato vengono compilate direttamente nel codice generato. Per passare le costanti alla funzione generata, invece di averle compilate, passale semplicemente come feed.

Per i dettagli sulla tf_library macro di compilazione, vedere tfcompile.bzl .

Per dettagli sul sottostante tfcompile strumento, vedere tfcompile_main.cc .

Passaggio 3: scrivi il codice per richiamare il sottografo

Questo passaggio utilizza il file di intestazione ( test_graph_tfmatmul.h ) generato dal tf_library macro accumulo nel passaggio precedente per richiamare il codice generato. Il file di intestazione si trova nella bazel-bin directory corrispondente al pacchetto di build, e prende il nome in base al set di attributi nome sulla tf_library macro build. Ad esempio, l'intestazione generato per test_graph_tfmatmul sarebbe test_graph_tfmatmul.h . Di seguito è riportata una versione abbreviata di ciò che viene generato. Il file generato, in bazel-bin , contiene ulteriori commenti utili.

namespace foo {
namespace bar {

// MatMulComp represents a computation previously specified in a
// TensorFlow graph, now compiled into executable code.
class MatMulComp {
 public:
  // AllocMode controls the buffer allocation mode.
  enum class AllocMode {
    ARGS_RESULTS_AND_TEMPS,  // Allocate arg, result and temp buffers
    RESULTS_AND_TEMPS_ONLY,  // Only allocate result and temp buffers
  };

  MatMulComp(AllocMode mode = AllocMode::ARGS_RESULTS_AND_TEMPS);
  ~MatMulComp();

  // Runs the computation, with inputs read from arg buffers, and outputs
  // written to result buffers. Returns true on success and false on failure.
  bool Run();

  // Arg methods for managing input buffers. Buffers are in row-major order.
  // There is a set of methods for each positional argument.
  void** args();

  void set_arg0_data(float* data);
  float* arg0_data();
  float& arg0(size_t dim0, size_t dim1);

  void set_arg1_data(float* data);
  float* arg1_data();
  float& arg1(size_t dim0, size_t dim1);

  // Result methods for managing output buffers. Buffers are in row-major order.
  // Must only be called after a successful Run call. There is a set of methods
  // for each positional result.
  void** results();


  float* result0_data();
  float& result0(size_t dim0, size_t dim1);
};

}  // end namespace bar
}  // end namespace foo

La classe generata C ++ è chiamato MatMulComp nella foo::bar spazio dei nomi, perché quello era il cpp_class specificato nel tf_library macro. Tutte le classi generate hanno un'API simile, con l'unica differenza che sono i metodi per gestire arg e buffer dei risultati. Tali metodi diversi a seconda del numero e tipo di buffer, che sono stati specificati dal feed e fetch argomenti al tf_library macro.

Ci sono tre tipi di buffer gestite all'interno della classe generata: args rappresentano gli ingressi, results rappresentano le uscite e le temps rappresentano buffer temporanei utilizzati internamente per eseguire il calcolo. Per impostazione predefinita, ogni istanza della classe generata alloca e gestisce tutti questi buffer per te. AllocMode argomento del costruttore può essere usata per modificare questo comportamento. Tutti i buffer sono allineati ai limiti di 64 byte.

La classe C++ generata è solo un wrapper per il codice di basso livello generato da XLA.

Esempio di invocare la funzione generata sulla base tfcompile_test.cc :

#define EIGEN_USE_THREADS
#define EIGEN_USE_CUSTOM_THREAD_POOL

#include <iostream>
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/compiler/aot/tests/test_graph_tfmatmul.h" // generated

int main(int argc, char** argv) {
  Eigen::ThreadPool tp(2);  // Size the thread pool as appropriate.
  Eigen::ThreadPoolDevice device(&tp, tp.NumThreads());


  foo::bar::MatMulComp matmul;
  matmul.set_thread_pool(&device);

  // Set up args and run the computation.
  const float args[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
  std::copy(args + 0, args + 6, matmul.arg0_data());
  std::copy(args + 6, args + 12, matmul.arg1_data());
  matmul.Run();

  // Check result
  if (matmul.result0(0, 0) == 58) {
    std::cout << "Success" << std::endl;
  } else {
    std::cout << "Failed. Expected value 58 at 0,0. Got:"
              << matmul.result0(0, 0) << std::endl;
  }

  return 0;
}

Passaggio 4: creare il file binario finale

Questa fase combina la libreria generata da tf_library nel passaggio 2 e il codice scritto nel passaggio 3 per creare un binario finale. Di seguito è riportato un esempio bazel file di costruire.

# Example of linking your binary
# Also see //tensorflow/compiler/aot/tests/BUILD
load("//tensorflow/compiler/aot:tfcompile.bzl", "tf_library")

# The same tf_library call from step 2 above.
tf_library(
    name = "test_graph_tfmatmul",
    ...
)

# The executable code generated by tf_library can then be linked into your code.
cc_binary(
    name = "my_binary",
    srcs = [
        "my_code.cc",  # include test_graph_tfmatmul.h to access the generated header
    ],
    deps = [
        ":test_graph_tfmatmul",  # link in the generated object file
        "//third_party/eigen3",
    ],
    linkopts = [
          "-lpthread",
    ]
)