| #!/bin/bash |
| |
| # Copyright 2020 Google LLC |
| # |
| # 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 |
| # |
| # https://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. |
| |
| # This script assumes Debian Linux. It could easily be modified to run on any Unix, |
| # as the Debian assumption is only to automatically install some packages, |
| # (Tracy dependencies). With a bit more work it could be made to support other OSes |
| # such as Windows, but that's not at all handled yet. |
| |
| function print_status { |
| echo -e "\e[96m$@\e[39m" |
| } |
| |
| # Environment variables. They can be set manually, or will use the following defaults. |
| # IREE_ROOT and ANDROID_NDK are empty by default, must be defined by the user. |
| # |
| # To make this script very easy to play with, this script will also default to |
| # benchmarking a specific NN model (currently MobileBert encoder), and will take |
| # care of generating its input MLIR form and of setting the right --function_inputs |
| # and --entry_function for it. To use this script on any other specific NN model, |
| # define the following environment variables: |
| # IREE_INPUT_MLIR |
| # FUNCTION_INPUTS |
| # ENTRY_FUNCTION |
| |
| DEFAULT_IREE_INPUT_MLIR="/tmp/iree/modules/MobileBertSquad/iree_input.mlir" |
| |
| : ${IREE_ROOT:=""} |
| : ${IREE_BUILD_ANDROID:="$HOME/iree-build-android"} |
| : ${TRACY_ROOT:="$HOME/tracy"} |
| : ${PYTHON_BIN:=python3} |
| : ${CC:=clang} |
| : ${CXX:=clang++} |
| : ${IREE_INPUT_MLIR:="${DEFAULT_IREE_INPUT_MLIR}"} |
| : ${ANDROID_NDK:=""} |
| : ${IREE_LLVMAOT_LINKER_PATH:="$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang++ -static-libstdc++ -O3"} |
| : ${FUNCTION_INPUTS:=""} |
| : ${ENTRY_FUNCTION:=""} |
| # Validation of the environment variables. |
| |
| if [ -z "${IREE_ROOT}" ] |
| then |
| print_status "Please define IREE_ROOT to point to your IREE git clone." |
| print_status "Example:" |
| print_status " IREE_ROOT=\$HOME/iree $0" |
| exit 1 |
| fi |
| |
| if [ ! -f "${IREE_ROOT}/iree/base/CMakeLists.txt" ] |
| then |
| print_status "FATAL: bad IREE_ROOT (${IREE_ROOT})." |
| exit 1 |
| fi |
| |
| if [ -z "${ANDROID_NDK}" ] |
| then |
| print_status "Please define ANDROID_NDK to point to your Android NDK." |
| print_status "Example:" |
| print_status " ANDROID_NDK=\$HOME/android-ndk-r21d $0" |
| exit 1 |
| fi |
| |
| if [ ! -d "${ANDROID_NDK}/toolchains/llvm" ] |
| then |
| print_status "FATAL: bad ANDROID_NDK (${ANDROID_NDK})." |
| exit 1 |
| fi |
| |
| if ! $CC --version 1>/dev/null 2>/dev/null |
| then |
| print_status "FATAL: Install $CC and set CC to it." |
| exit 1 |
| fi |
| |
| if ! $CXX --version 1>/dev/null 2>/dev/null |
| then |
| print_status "FATAL: Install $CXX and set CXX to it." |
| exit 1 |
| fi |
| |
| if ! $IREE_LLVMAOT_LINKER_PATH --version 1>/dev/null 2>/dev/null |
| then |
| print_status "Bad IREE_LLVMAOT_LINKER_PATH (${IREE_LLVMAOT_LINKER_PATH}). Rerun with it correctly set." |
| exit 1 |
| fi |
| |
| # If we're playing with the default input MLIR, preset some reasonable |
| # FUNCTION_INPUTS and ENTRY_FUNCTION if they haven't been set. |
| # Otherwise, they must be provided. |
| if [[ -z "${FUNCTION_INPUTS}" || -z "${ENTRY_FUNCTION}" ]] |
| then |
| if [[ "${IREE_INPUT_MLIR}" == "${DEFAULT_IREE_INPUT_MLIR}" ]] |
| then |
| FUNCTION_INPUTS="1x384xi32,1x384xi32,1x384xi32" |
| ENTRY_FUNCTION="serving_default" |
| else |
| print_status "Please specify FUNCTION_INPUTS and ENTRY_FUNCTION appropriately for your IREE_INPUT_MLIR (${IREE_INPUT_MLIR})" |
| fi |
| fi |
| |
| print_status "Running with the following environment variables:" |
| print_status "ANDROID_NDK=${ANDROID_NDK}" |
| print_status "IREE_LLVMAOT_LINKER_PATH=${IREE_LLVMAOT_LINKER_PATH}" |
| print_status "IREE_ROOT=${IREE_ROOT}" |
| print_status "IREE_BUILD_ANDROID=${IREE_BUILD_ANDROID}" |
| print_status "TRACY_ROOT=${TRACY_ROOT}" |
| print_status "PYTHON_BIN=${PYTHON_BIN}" |
| print_status "CC=${CC}" |
| print_status "CXX=${CXX}" |
| print_status "IREE_INPUT_MLIR=${IREE_INPUT_MLIR}" |
| print_status "FUNCTION_INPUTS=${FUNCTION_INPUTS}" |
| print_status "ENTRY_FUNCTION=${ENTRY_FUNCTION}" |
| |
| echo |
| |
| print_status "Ensuring that we have Tracy source code..." |
| if [ ! -d ${TRACY_ROOT}/profiler/build/unix/ ] |
| then |
| print_status "Tracy not found at ${TRACY_ROOT}. Either set TRACY_ROOT, or we're going to clone the git repository now." |
| read -p "Press the return key..." |
| git clone https://github.com/wolfpld/tracy "${TRACY_ROOT}" |
| fi |
| |
| echo |
| |
| print_status "Ensuring that the Tracy profiler is built..." |
| if [ ! -x "${TRACY_ROOT}/profiler/build/unix/Tracy-release" ] |
| then |
| print_status "Checking Tracy dependencies - assuming Debian." |
| TRACY_DEPS="libcapstone-dev libtbb-dev libglfw3-dev libfreetype6-dev libgtk-3-dev" |
| TRACY_DEPS_COUNT=5 |
| TRACY_DEPS_INSTALLED="$(apt list $TRACY_DEPS) 2>/dev/null | grep installed | wc -l" |
| if [ $TRACY_DEPS_INSTALLED != $TRACY_DEPS_COUNT ] |
| then |
| print_status "Installing dependencies now - assuming Debian." |
| sudo apt install $TRACY_DEPS |
| fi |
| make -C "${TRACY_ROOT}/profiler/build/unix" -j12 release |
| fi |
| |
| echo |
| |
| print_status "Ensuring that we have the input MLIR file..." |
| if [ ! -f "${IREE_INPUT_MLIR}" ] |
| then |
| print_status "Set IREE_INPUT_MLIR to point to some iree input MLIR file. Not found at current value ${IREE_INPUT_MLIR}." |
| if [ "${IREE_INPUT_MLIR}" == "${DEFAULT_IREE_INPUT_MLIR}" ] |
| then |
| print_status "Okay, we actually know how to generate that file, ${IREE_INPUT_MLIR}, but it will take a while." |
| print_status "Press the return key to continue..." |
| pushd "${IREE_ROOT}" |
| scripts/get_e2e_artifacts.py --test_suites=mobile_bert_squad_tests |
| popd |
| fi |
| fi |
| |
| if [ ! -f "${IREE_INPUT_MLIR}" ] |
| then |
| print_status "FATAL: we should have ${IREE_INPUT_MLIR} by that point." |
| exit 1 |
| fi |
| |
| echo |
| |
| print_status "Building IREE for Android in ${IREE_BUILD_ANDROID}..." |
| |
| mkdir -p "${IREE_BUILD_ANDROID}" |
| pushd "${IREE_BUILD_ANDROID}" |
| |
| cmake -G Ninja ../iree \ |
| -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ |
| -DANDROID_ABI="arm64-v8a" \ |
| -DANDROID_PLATFORM=android-30 \ |
| -DCMAKE_BUILD_TYPE=RelWithDebInfo \ |
| -DIREE_BUILD_COMPILER=OFF \ |
| -DIREE_BUILD_SAMPLES=OFF \ |
| -DIREE_HOST_C_COMPILER=`which "$CC"` \ |
| -DIREE_HOST_CXX_COMPILER=`which "$CXX"` \ |
| -DIREE_ENABLE_RUNTIME_TRACING=ON |
| |
| cmake --build . --target \ |
| iree_tools_iree-translate \ |
| iree_tools_iree-benchmark-module |
| |
| popd |
| echo |
| |
| print_status "Compiling the input MLIR file into a IREE module..." |
| |
| IREE_COMPILED_MODULE=/tmp/android_module.vmfb |
| IREE_LOG=/tmp/iree-translate.log |
| |
| rm -rf "${IREE_COMPILED_MODULE}" |
| |
| IREE_LLVMAOT_LINKER_PATH="${IREE_LLVMAOT_LINKER_PATH}" \ |
| "${IREE_BUILD_ANDROID}/host/bin/iree-translate" \ |
| --iree-hal-target-backends=dylib-llvm-aot \ |
| --iree-mlir-to-vm-bytecode-module \ |
| --iree-llvm-target-triple=aarch64-linux-android \ |
| /tmp/iree/modules/MobileBertSquad/iree_input.mlir \ |
| -o "${IREE_COMPILED_MODULE}" \ |
| 2>"${IREE_LOG}" |
| |
| if [ ! -f "${IREE_COMPILED_MODULE}" ] |
| then |
| print_status "iree-translate failed to produce ${IREE_COMPILED_MODULE}. Log saved in ${IREE_LOG}. First few lines:" |
| # The whole log might be enormous if it contains a big MLIR dump. |
| head -n10 "${IREE_LOG}" |
| print_status "tip: check if IREE_LLVMAOT_LINKER_PATH was correctly set." |
| exit 1 |
| fi |
| |
| echo |
| |
| DEVICE_IREE_COMPILED_MODULE=/data/local/tmp/android_module.vmfb |
| |
| print_status "Pushing the compiled module to the device..." |
| adb push "${IREE_COMPILED_MODULE}" "${DEVICE_IREE_COMPILED_MODULE}" |
| echo |
| |
| print_status "Pushing the IREE benchmarking program to the device..." |
| adb push "${IREE_BUILD_ANDROID}"/iree/tools/iree-benchmark-module /data/local/tmp |
| echo |
| |
| print_status "Setting up TCP port forwarding to let Tracy connect with the benchmark running on the device..." |
| adb forward tcp:8086 tcp:8086 |
| echo |
| |
| print_status "Now you can launch the Tracy UI in another shell and hit \"Connect\", while we run the benchmark on the device in this shell..." |
| print_status "Run this command in another shell:" |
| print_status " ${TRACY_ROOT}/profiler/build/unix/Tracy-release" |
| echo |
| |
| print_status "Running the benchmark... hit Ctrl-C to terminate it after Tracy is done with it." |
| # `TRACY_NO_EXIT=1` is to prevent it from exiting at the end: that's needed for profiling |
| # short-running tasks. |
| # `taskset 80` selects which CPU core to run on. On Pixel4, `taskset 80` gives the biggest |
| # core, which can get some reproducibility, as long as we don't run into thermal issues. |
| # `taskset 0f` would give the little cores, avoiding thermal issues but running slower and |
| # requiring different optimization work to be efficient on. |
| # `--driver=dylib` is to use the LLVM AOT generated code backend. |
| adb shell \ |
| TRACY_NO_EXIT=1 \ |
| taskset 80 \ |
| data/local/tmp/iree-benchmark-module \ |
| --driver=dylib \ |
| --module_file="${DEVICE_IREE_COMPILED_MODULE}" \ |
| --function_inputs="${FUNCTION_INPUTS}" \ |
| --entry_function="${ENTRY_FUNCTION}" |