Cómo usar la compilación AOT
¿Qué es tfcompile?
tfcompile
es una herramienta independiente que compila de forma anticipada (AOT) grafos de TensorFlow en código ejecutable. Puede reducir el tamaño binario total y también evitar algunos gastos generales de tiempo de ejecución. Un caso de uso típico de tfcompile
es compilar un grafo de inferencia en código ejecutable para dispositivos móviles.
El grafo de TensorFlow normalmente se ejecuta mediante el tiempo de ejecución de TensorFlow. Esto genera cierta sobrecarga de tiempo de ejecución para la ejecución de cada nodo en el grafo. Esto también conduce a un mayor tamaño binario total, ya que el código para el tiempo de ejecución de TensorFlow debe estar disponible, además del grafo en sí. El código ejecutable que produce tfcompile
no usa el tiempo de ejecución de TensorFlow y solo depende de los núcleos que realmente se usan en el cálculo.
El compilador se basa en el marco de XLA. El código que une TensorFlow con el marco de XLA reside en tensorflow/compiler.
¿Qué hace tfcompile?
tfcompile
toma un subgrafo, identificado por los conceptos de fuentes y extracciones de TensorFlow, y genera una función que implementa ese subgrafo. Las feeds
son los argumentos de entrada de la función y las fetches
son los argumentos de salida de la función. Todas las entradas deben estar completamente especificadas por las fuentes; el subgrafo podado resultante no puede contener nodos de marcador de posición o de variable. Es común especificar todos los marcadores de posición y variables como fuentes, lo que garantiza que el subgrafo resultante ya no contenga estos nodos. La función generada se empaqueta como cc_library
, con un archivo de encabezado que exporta la firma de la función y un archivo objeto que contiene la implementación. El usuario escribe código para invocar la función generada según corresponda.
Cómo usar tfcompile
En esta sección se detallan los pasos de alto nivel necesarios para generar un binario ejecutable con tfcompile
a partir de un subgrafo de TensorFlow. Los pasos son los siguientes:
Paso 1: configure el subgrafo para compilar
Paso 2: use la macro de compilación
tf_library
para compilar el subgrafoPaso 3: escriba el código para invocar el subgrafo
Paso 4: cree el binario final
Paso 1: configure el subgrafo para compilar
Identifique las fuentes y las extracciones que corresponden a los argumentos de entrada y salida de la función generada. Luego, configure feeds
y fetches
en un protocolo tensorflow.tf2xla.Config
.
Paso 2: use la macro de compilación tf_library para compilar el subgrafo
En este paso se usa la macro de compilación tf_library
para convertir el grafo en cc_library
. cc_library
consta de un archivo objeto que contiene el código generado a partir del grafo, junto con un archivo de encabezado que da acceso al código generado. tf_library
usa tfcompile
para compilar el grafo de TensorFlow en código ejecutable.
Para generar el protocolo GraphDef (test_graph_tfmatmul.pb) para este ejemplo, ejecute make_test_graphs.py y especifique la ubicación de salida con la marca --out_dir.
Los grafos típicos contienen Variables
que representan las ponderaciones que se aprenden a través del entrenamiento, pero tfcompile
no puede compilar un subgrafo que contenga Variables
. La herramienta frozen_graph.py usa valores almacenados en un archivo de punto de control para convertir variables en constantes. Para su comodidad, la macro tf_library
admite el argumento freeze_checkpoint
, que ejecuta la herramienta. Para obtener más ejemplos, consulte tensorflow/compiler/aot/tests/BUILD.
Las constantes que aparecen en el subgrafo compilado se compilan directamente en el código generado. Para pasar las constantes a la función generada, en lugar de compilarlas, simplemente páselas como fuentes.
Para obtener más información sobre la macro de compilación tf_library
, consultetfcompile.bzl.
Para obtener más información sobre la herramienta tfcompile
subyacente, consulte tfcompile_main.cc.
Paso 3: escriba el código para invocar el subgrafo
En este paso se usa el archivo de encabezado (test_graph_tfmatmul.h
) generado por la macro de compilación tf_library
en el paso anterior para invocar el código generado. El archivo de encabezado se encuentra en el directorio bazel-bin
correspondiente al paquete de compilación y recibe su nombre a partir del atributo de nombre establecido en la macro de compilación tf_library
. Por ejemplo, el encabezado generado para test_graph_tfmatmul
sería test_graph_tfmatmul.h
. A continuación, se muestra una versión abreviada de lo que se genera. El archivo generado, en bazel-bin
, contiene comentarios útiles adicionales.
La clase C++ generada se llama MatMulComp
en el espacio de nombres foo::bar
, porque esa era la cpp_class
especificada en la macro tf_library
. Todas las clases generadas tienen una API similar, que solo se diferencia por los métodos que usan para manejar los búferes de argumentos y resultados. Esos métodos difieren según el número y los tipos de búferes, que fueron especificados por los argumentos feed
y fetch
de la macro tf_library
.
Hay tres tipos de búferes que se administran dentro de la clase generada: args
que representan las entradas, results
que representan las salidas y temps
que representan búferes temporales que se usan internamente para ejecutar el cálculo. De forma predeterminada, cada instancia de la clase generada asigna y administra todos estos búferes por usted. El argumento del constructor AllocMode
se puede usar para cambiar este comportamiento. Todos los búferes están alineados con límites de 64 bytes.
La clase C++ generada es solo un contenedor del código de bajo nivel generado por XLA.
Ejemplo de invocación de la función generada a partir de tfcompile_test.cc
:
Paso 4: cree el binario final
Este paso combina la biblioteca generada por tf_library
en el paso 2 y el código escrito en el paso 3 para crear un binario final. A continuación, se muestra un ejemplo de archivo BUILD bazel
.