From 27655815555b388306dfccb970fe5ca42a5b2e6d Mon Sep 17 00:00:00 2001 From: RJ Ascani Date: Fri, 8 Sep 2023 21:05:37 +0000 Subject: [PATCH] Create Make helpers for running codegen The codegen process is a multi-step process that requires compiling, executing code under simulation, and executing python scripts. To simplify this workflow, this commit adds Make helper functions for generating inference source code from a model and creating a binary with it. It also updates the hello world example to use these helpers and adds an update script for keeping the checked in generated source in sync. --- codegen/examples/hello_world/Makefile.inc | 14 ++--- codegen/examples/hello_world/README.md | 47 +++++----------- .../hello_world/update_example_source.sh | 29 ++++++++++ tensorflow/lite/micro/tools/make/Makefile | 4 +- .../micro/tools/make/helper_functions.inc | 53 +++++++++++++++++++ 5 files changed, 103 insertions(+), 44 deletions(-) create mode 100755 codegen/examples/hello_world/update_example_source.sh diff --git a/codegen/examples/hello_world/Makefile.inc b/codegen/examples/hello_world/Makefile.inc index 56e2da712f6..6dfcf18d4e5 100644 --- a/codegen/examples/hello_world/Makefile.inc +++ b/codegen/examples/hello_world/Makefile.inc @@ -1,10 +1,10 @@ -CODEGEN_HELLO_WORLD_SRCS := \ -$(TENSORFLOW_ROOT)codegen/examples/hello_world/hello_world.cc \ -$(TENSORFLOW_ROOT)codegen/examples/hello_world/hello_world_model.cc +CODEGEN_HELLO_WORLD_MODEL := \ +$(TENSORFLOW_ROOT)tensorflow/lite/micro/examples/hello_world/models/hello_world_int8.tflite -CODEGEN_HELLO_WORLD_HDRS := \ -$(TENSORFLOW_ROOT)codegen/examples/hello_world/hello_world_model.h +CODEGEN_HELLO_WORLD_SRCS := \ +$(TENSORFLOW_ROOT)codegen/examples/hello_world/hello_world.cc # Builds a standalone binary. -$(eval $(call microlite_test,codegen_hello_world,\ -$(CODEGEN_HELLO_WORLD_SRCS),,)) +$(eval $(call codegen_model_binary,codegen_hello_world,hello_world_model,\ +$(CODEGEN_HELLO_WORLD_MODEL),$(CODEGEN_HELLO_WORLD_SRCS),,)) + diff --git a/codegen/examples/hello_world/README.md b/codegen/examples/hello_world/README.md index c5c0945e185..d3c805e8ff5 100644 --- a/codegen/examples/hello_world/README.md +++ b/codegen/examples/hello_world/README.md @@ -1,51 +1,28 @@ # Codegen Hello World Example -This is a code-generated example of the hello world model. The process is -currently somewhat involved: +This is a code-generated example of the hello world model. The generated source +is checked in for now so that it can be reviewed during the prototyping stage. -## Build the preprocessor for your target +## Building the example executable +Please note that this will execute Bazel from make as part of the process. -This creates a target-specific preprocessor binary capable of performing the -init and prepare stages of the Interpreter and serializing the output. This -binary can be re-used for multiple models. - -### x86 ``` -make -f tensorflow/lite/micro/tools/make/Makefile codegen_preprocessor +make -f tensorflow/lite/micro/tools/make/Makefile codegen_hello_world ``` -## Run the preprocessor +## Running the example -The preprocessor will take the provided model, create a TFLM Interpreter, and -allocate tensors. It will then capture and serialize the resulting data -structures needed for inference. For embedded targets, this should be run under -simulation. +TODO(rjascani): The command works, but it'll just crash as we don't have all of +the data structures fullypopulated yet. -### x86 ``` -./gen/linux_x86_64_default/bin/codegen_preprocessor \ - $(pwd)/tensorflow/lite/micro/examples/hello_world/models/hello_world_int8.tflite \ - $(pwd)/gen/linux_86_64_default/genfiles/hello_world_int8.ppd +make -f tensorflow/lite/micro/tools/make/Makefile run_codegen_hello_world ``` -## Generate the inference code +## Updating the generated sources +To update the generated source, you can execute this make target: -To generate the inference code at `codegen/example/hello_world_model.h/.cc`: - -### x86 ``` -bazel run codegen:code_generator -- \ - --model $(pwd)/tensorflow/lite/micro/examples/hello_world/models/hello_world_int8.tflite \ - --preprocessed_data $(pwd)/gen/linux_86_64_default/genfiles/hello_world_int8.ppd \ - --output_dir $(pwd)/codegen/examples/hello_world \ - --output_name hello_world_model +./codegen/examples/hello_world/update_example_source.sh ``` -## Compile the generated inference code - - To compile the generated source, you can use the Makefile: - -### x86 -``` -make -f tensorflow/lite/micro/tools/make/Makefile codegen_hello_world -``` diff --git a/codegen/examples/hello_world/update_example_source.sh b/codegen/examples/hello_world/update_example_source.sh new file mode 100755 index 00000000000..df5e2ace365 --- /dev/null +++ b/codegen/examples/hello_world/update_example_source.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Copyright 2023 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + +# +# Syncs the generated example source code in the repository. +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR=${SCRIPT_DIR}/../../.. +cd "${ROOT_DIR}" + +make -j8 -f tensorflow/lite/micro/tools/make/Makefile codegen_hello_world +cp ./gen/linux_x86_64_default/genfiles/hello_world_model.h ${SCRIPT_DIR} +cp ./gen/linux_x86_64_default/genfiles/hello_world_model.cc ${SCRIPT_DIR} diff --git a/tensorflow/lite/micro/tools/make/Makefile b/tensorflow/lite/micro/tools/make/Makefile index c059792ccde..cbcd85fc83e 100644 --- a/tensorflow/lite/micro/tools/make/Makefile +++ b/tensorflow/lite/micro/tools/make/Makefile @@ -294,6 +294,8 @@ MICRO_LITE_BENCHMARKS := $(wildcard $(TENSORFLOW_ROOT)tensorflow/lite/micro/tool MICROLITE_BENCHMARK_SRCS := \ $(wildcard $(TENSORFLOW_ROOT)tensorflow/lite/micro/tools/benchmarking/*benchmark.cc) +CODEGEN_PREPROCESSOR_PATH := $(BINDIR)codegen_preprocessor + MICRO_LITE_CODEGEN_PREPROCESSOR := $(TENSORFLOW_ROOT)codegen/preprocessor/Makefile.inc MICRO_LITE_CODEGEN_EXAMPLES := $(shell find $(TENSORFLOW_ROOT)codegen/examples/ -name Makefile.inc) @@ -868,8 +870,6 @@ integration_tests: $(MICROLITE_INTEGRATION_TEST_TARGETS) generated_micro_mutable_op_resolver: $(MICROLITE_GEN_OP_RESOLVER_TEST_TARGETS) endif -CODEGEN_PREPROCESSOR_PATH := $(BINDIR)codegen_preprocessor - codegen_preprocessor: $(CODEGEN_PREPROCESSOR_PATH) $(CODEGEN_PREPROCESSOR_PATH): $(CODEGEN_PREPROCESSOR_SRCS) $(MICROLITE_LIB_PATH) diff --git a/tensorflow/lite/micro/tools/make/helper_functions.inc b/tensorflow/lite/micro/tools/make/helper_functions.inc index ad3d44c45e5..ca268d9204a 100644 --- a/tensorflow/lite/micro/tools/make/helper_functions.inc +++ b/tensorflow/lite/micro/tools/make/helper_functions.inc @@ -117,3 +117,56 @@ endef # 2 - File pattern, e.g: *.h recursive_find = $(wildcard $(1)$(2)) $(foreach dir,$(wildcard $(1)*),$(call recursive_find,$(dir)/,$(2))) +# Generates code capable of performing inference without an interpreter. It run +# the codegen preprocessor and the code generator. +# +# Arguments are: +# 1 - Name of target +# 2 - Generated source basename +# 3 - Model +# Calling eval on the output will create the targets that you need. +define codegen_model + +$(1)_MODEL := $(abspath $(3)) +$(1)_PREPROCESSOR_OUTPUT := $(abspath $(GENERATED_SRCS_DIR)/$(2).ppd) +$(1)_GENERATED_SRC_DIR := $(abspath $(GENERATED_SRCS_DIR)) + +$(1)_GENERATED_SRCS := $$($(1)_GENERATED_SRC_DIR)/$(2).cc +$(1)_GENERATED_HDRS := $$($(1)_GENERATED_SRC_DIR)/$(2).h + +$$($(1)_PREPROCESSOR_OUTPUT): $(CODEGEN_PREPROCESSOR_PATH) $$($(1)_MODEL) + @mkdir -p $$(dir $$@) + $$(TEST_SCRIPT) $(CODEGEN_PREPROCESSOR_PATH) $$($(1)_MODEL) $$($(1)_PREPROCESSOR_OUTPUT) + +$$($(1)_GENERATED_SRCS) $$($(1)_GENERATED_HDRS): $$($(1)_MODEL) $$($(1)_PREPROCESSOR_OUTPUT) + bazel run //codegen:code_generator -- \ + --model $$($(1)_MODEL) --preprocessed_data $$($(1)_PREPROCESSOR_OUTPUT) \ + --output_dir $$($(1)_GENERATED_SRC_DIR) --output_name $(2) + +$(1): $$($(1)_GENERATED_SRCS) $$($(1)_GENERATED_HDRS) + +endef # codegen_model + +# Generates and compiles code capable of performing inference without an +# interpreter. +# +# Arguments are: +# 1 - Name of target +# 2 - Generated source basename +# 3 - Model +# 4 - C/C++ source files +# 5 - C/C++ header files +# Calling eval on the output will create the targets that you need. +define codegen_model_binary + +$(1)_LOCAL_SRCS := $(4) +$(1)_LOCAL_HDRS := $(5) + +$(call codegen_model,$(1)_codegen,$(2),$(3)) + +$(1)_LOCAL_SRCS += $$($(1)_codegen_GENERATED_SRCS) +$(1)_LOCAL_HDRS += $$($(1)_codegen_GENERATED_HDRS) + +$(call microlite_test,$(1),$$($(1)_LOCAL_SRCS),$$($(1)_LOCAL_HDRS),,) + +endef # codegen_model_binary