Usando compilação AOT
O que é tfcompile?
O tfcompile
é uma ferramenta autônoma que compila grafos do TensorFlow antecipadamente (AOT) em código executável. Ele pode reduzir o tamanho binário total e também evitar algumas sobrecargas de tempo de execução. Um caso de uso típico de tfcompile
é compilar um grafo de inferência em código executável para dispositivos móveis.
O grafo do TensorFlow normalmente é executado pelo runtime do TensorFlow. Isso incorre em alguma sobrecarga de tempo de execução para a execução de cada nó do grafo. Isso também leva a um tamanho total maior de código binário, já que o código do runtime do TensorFlow precisa estar disponível, além do próprio grafo. O código executável produzido por tfcompile
não usa o runtime do TensorFlow e só possui dependências de kernels que são realmente usados na computação.
O compilador é construído sobre o framework XLA. O código que liga o TensorFlow ao framework XLA reside em tensorflow/compiler.
O que o tfcompile faz?
O tfcompile
pega um subgrafo, identificado pelos conceitos de feeds e fetches do TensorFlow, e gera uma função que implementa esse subgrafo. Os feeds
são argumentos de entrada da função e os fetches
são os argumentos de saída da função. Todas as entradas devem ser totalmente especificadas pelos feeds; o subgrafo removido resultante não pode conter nós Placeholder ou Variable. É comum especificar todos os espaços reservados e variáveis como feeds, o que garante que o subgrafo resultante não contenha mais esses nós. A função gerada é empacotada como cc_library
, com um arquivo de cabeçalho exportando a assinatura da função e um arquivo objeto contendo a implementação. O usuário escreve código para chamar a função gerada conforme apropriado.
Usando tfcompile
Esta seção detalha passos de alto nível para gerar um binário executável com tfcompile
a partir de um subgrafo do TensorFlow. Os passos são:
Passo 1: configure o subgrafo para compilar
Passo 2: use a macro de build
tf_library
para compilar o subgrafoPasso 3: escreva o código para chamar o subgrafo
Passo 4: crie o binário final
Passo 1: configure o subgrafo para compilar
Identifique os feeds e fetches que correspondem aos argumentos de entrada e saída da função gerada. Em seguida, configure os feeds
e fetches
em um proto tensorflow.tf2xla.Config
.
Passo 2: use a macro de build tf_library para compilar o subgrafo
Este passo converte o grafo numa cc_library
usando a macro de build tf_library
. A cc_library
consiste num arquivo objeto contendo o código gerado a partir do grafo, junto com um arquivo de cabeçalho que dá acesso ao código gerado. tf_library
utiliza tfcompile
para compilar o grafo do TensorFlow em código executável.
Para gerar o proto GraphDef (test_graph_tfmatmul.pb) para este exemplo, execute make_test_graphs.py e especifique o local de saída com o sinalizador --out_dir.
Grafos típicos contêm Variables
que representam os pesos que são aprendidos por meio de treinamento, mas tfcompile
não pode compilar um subgrafo que contenha Variables
. A ferramenta freeze_graph.py converte variáveis em constantes, usando valores armazenados em um arquivo de checkpoint. Por conveniência, a macro tf_library
suporta o argumento freeze_checkpoint
, que executa a ferramenta. Para mais exemplos, consulte tensorflow/compiler/aot/tests/BUILD .
As constantes que aparecem no subgrafo compilado são compiladas diretamente no código gerado. Para passar as constantes para a função gerada, em vez de compilá-las, simplesmente passe-as como feeds.
Para detalhes sobre a macro de build tf_library
, veja tfcompile.bzl.
Para detalhes sobre a ferramenta tfcompile
nativa, veja tfcompile_main.cc.
Passo 3: escreva o código para chamar o subgrafo
Este passo usa o arquivo de cabeçalho (test_graph_tfmatmul.h
) gerado pela macro de build tf_library
no passo anterior para chamar o código gerado. O arquivo de cabeçalho está localizado no diretório bazel-bin
correspondente ao pacote de build e é nomeado com base no atributo name definido na macro de build tf_library
. Por exemplo, o cabeçalho gerado para test_graph_tfmatmul
seria test_graph_tfmatmul.h
. Abaixo está uma versão abreviada do que é gerado. O arquivo gerado, em bazel-bin
, contém comentários úteis adicionais.
A classe C++ gerada é chamada MatMulComp
no namespace foo::bar
, porque essa era a cpp_class
especificada na macro tf_library
. Todas as classes geradas possuem uma API semelhante, com a única diferença sendo os métodos para lidar com buffers de argumentos e resultados. Esses métodos diferem com base no número e nos tipos de buffers, que foram especificados pelos argumentos feed
e fetch
para a macro tf_library
.
Há três tipos de buffers gerenciados na classe gerada: args
representando as entradas, results
representando as saídas e temps
representando buffers temporários usados internamente para realizar o cálculo. Por padrão, cada instância da classe gerada aloca e gerencia todos esses buffers para você. O argumento do construtor AllocMode
pode ser usado para alterar esse comportamento. Todos os buffers estão alinhados aos limites de 64 bytes.
A classe C++ gerada é apenas um wrapper em torno do código de baixo nível gerado pelo XLA.
Exemplo de chamada da função gerada com base em tfcompile_test.cc
:
Passo 4: crie o binário final
Este passo combina a biblioteca gerada por tf_library
no passo 2 e o código escrito no passo 3 para criar um arquivo binário final. Abaixo está um exemplo de arquivo BUILD do bazel
.