aboutsummaryrefslogtreecommitdiff
path: root/tests/ExecuteNetwork/evaluate_network.sh
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ExecuteNetwork/evaluate_network.sh')
-rwxr-xr-xtests/ExecuteNetwork/evaluate_network.sh358
1 files changed, 358 insertions, 0 deletions
diff --git a/tests/ExecuteNetwork/evaluate_network.sh b/tests/ExecuteNetwork/evaluate_network.sh
new file mode 100755
index 0000000000..931167dda8
--- /dev/null
+++ b/tests/ExecuteNetwork/evaluate_network.sh
@@ -0,0 +1,358 @@
+#!/bin/bash
+#set -x
+#
+# Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+# This script will run a TfLite model through ExecuteNetwork trying all available backends to measure
+# both speed and accuracy. In addition, it will try some of the performance options that are available.
+#
+# Prerequisites: ExecuteNetwork must be built with:
+# * CpuRef enabled (-DARMNNREF=1)
+# * TfLite delegate enabled (-DBUILD_CLASSIC_DELEGATE=1)
+# * TfLite parser enabled (-DBUILD_TF_LITE_PARSER=1)
+# * Any backend you want to test against. E.g. -DARMCOMPUTENEON=1 -DARMCOMPUTECL=1
+# * The model must be fully supported by Arm NN.
+#
+# Usage:
+# evaluate_network.sh -e <Path to ExecuteNetwork> -m <Tfite model to test>
+#
+# Sample usage:
+# evaluate_network.sh -e ./build/release/armnn/test -m ./my_tflite_model.tflite
+#
+
+CMD=$( basename "$0" )
+
+usage() {
+ echo "Usage: $CMD -e <Path to ExecuteNetwork> -m <Test model>"
+ echo "Options: -e <Path to ExecuteNetwork>"
+ echo " -m <Test model>"
+ exit 1
+}
+
+# Errors if the previous command had a non-zero exit code.
+function AssertZeroExitCode {
+ EXITCODE=$?
+ if [ $EXITCODE -ne 0 ]; then
+ echo -e "Previous command exited with code $EXITCODE"
+ exit 1
+ fi
+}
+
+OPTION_COUNTER=0
+while getopts "e:m:" opt; do
+ ((OPTION_COUNTER+=1))
+ case "$opt" in
+ h|\?) usage;;
+ e) EXECUTE_NETWORK_PATH="$OPTARG";;
+ m) MODEL="$OPTARG";;
+ esac
+done
+shift $((OPTIND - 1))
+
+# Both parameters are mandatory.
+if [ -z "$EXECUTE_NETWORK_PATH" ] || [ -z "$MODEL" ]; then
+ usage
+ exit 1
+fi
+
+# Check the path to execute network will find the executable.
+if [ -x "$EXECUTE_NETWORK_PATH/ExecuteNetwork" ]; then
+ echo -e "Using Execute Network from\t\t\t: $EXECUTE_NETWORK_PATH/ExecuteNetwork"
+ EXECUTE_NETWORK="$EXECUTE_NETWORK_PATH/ExecuteNetwork"
+else
+ echo "Execute Network does not exist at \"$EXECUTE_NETWORK_PATH/ExecuteNetwork\""
+ usage
+ exit 1
+fi
+
+# Check that the model exists and has a supported extension.
+if [ -f $MODEL ]; then
+ if [[ ! $MODEL =~ (tflite)$ ]]; then
+ echo "Only .tflite files are supported."
+ exit 1
+ fi
+else
+ echo Model file: "\"$MODEL\" could not be found."
+ usage
+ exit 1
+fi
+
+# Find out the available backends. Unfortunaltey the list of backends spans multiple lines.
+# This means we have to do this in several steps.
+echo -n -e "Available backends on this executable\t\t:"
+HELP_OUTOUT=`$EXECUTE_NETWORK --help`
+BACKENDS=`echo $HELP_OUTOUT | sed 's/.*: \[//' | sed 's/\].*//' | sed 's/,//g'`
+# Remove the leading space to make it look prettier.
+BACKENDS="${BACKENDS:1}"
+if [ -z "$BACKENDS" ]; then
+ echo ""
+ echo "Execute Network reported no available backends!"
+ exit 1
+else
+ echo " $BACKENDS"
+ # We really need the CpuRef to be in there.
+ if [[ ! $BACKENDS =~ "CpuRef" ]]; then
+ echo ""
+ echo "Fatal: Please recompile ExecuteNetwork to include the CpuRef backend. (-DARMNNREF=1)"
+ exit 1
+ fi
+fi
+
+
+# This is where the real work starts.
+# Model execution can take a long time. Trap ctrl-c and tell the user.
+trap ctrl_c INT
+
+function ctrl_c() {
+ echo -e "Interrupted.\nNo patience eh? Try a smaller model."
+ exit 1
+}
+
+
+# We need to check that the delegate is supported otherwise we can't run through the tf runtime.
+echo -n -e "Is the delegate supported on this executable?\t:"
+TFLITE_EXECUTION=`$EXECUTE_NETWORK -m $MODEL -T tflite -c CpuRef -N`
+# Check for an error message about building with the delegate.
+if [[ $TFLITE_EXECUTION =~ "Tensorflow-Lite delegate support" ]]; then
+ echo ""
+ echo "Fatal: Please recompile ExecuteNetwork with TfLite delegate support enabled. (-DBUILD_CLASSIC_DELEGATE=1)"
+ exit 1
+else
+ echo " Yes"
+fi
+
+# Run through CpuRef to see if Arm NN supports the model.
+echo -n -e "Is the model fully supported by Arm NN?\t\t:"
+REF_EXECUTION=`$EXECUTE_NETWORK -m $MODEL -c CpuRef -N`
+# If it failed look for the most common reason - an unsupported layer.
+if [ $? -ne 0 ]; then
+ if [[ $REF_EXECUTION =~ "is not supported on requested backend CpuRef" ]]; then
+ echo -e " No - One or more layers are not supported by Arm NN"
+ else
+ echo -e " No - Execution using CpuRef backend failed."
+ fi
+ echo -e "The Reported problems were\t:"
+ echo `echo "$REF_EXECUTION" | sed '/Warning\|ERROR\|Fatal/!d'`
+ echo "To recreate this error try: \"$EXECUTE_NETWORK -m $MODEL -c CpuRef\" "
+ exit 1
+fi
+echo " Yes"
+
+# This function will execute the model and return a string representation of the results. This is the
+# first time the model will be executed.
+# Is done wth -c $BACKEND,CpuRef to allow the odd layer to be supported by an unaccelerated backend.
+#
+# Parameters:
+# $1 Backend string like CpuRef.
+# $2 Additional ExecuteNetwork parameters.
+#
+function RunAccuracyOnBackendWithParameters {
+ BACKEND=$1
+ ADDITIONAL_PARAM=$2
+ # Run on BACKEND to check accuracy against TfLite runtime first. This will be a warning not a failure.
+ ACCURACY_RUN=`$EXECUTE_NETWORK -m $MODEL -c $BACKEND $ADDITIONAL_PARAM -A -N`
+ # Start by checking the return code.
+ if [ $? -ne 0 ]; then
+ # Maybe this backend isn't supported.
+ if [[ $ACCURACY_RUN =~ "None of the preferred backends [$BACKEND ] are supported" ]]; then
+ echo -e "\t\t***Is not supported***"
+ return 1
+ elif [[ $ACCURACY_RUN =~ "is not supported on requested backend" ]]; then
+ # One or more layers require a fall back. Run again with CpuRef fall back.
+ ACCURACY_RUN=`$EXECUTE_NETWORK -m $MODEL -c $BACKEND,CpuRef $ADDITIONAL_PARAM -A -N`
+ REQUIRES_CPUREF="*"
+ else
+ # In the case of a general failure against this backend tell the user what we tried and then
+ # ignore this backend.
+ echo -e "\t***Execution failed. Ignoring this backend. Command was: \"$EXECUTE_NETWORK -m $MODEL -c $BACKEND -A -N\""
+ return 1
+ fi
+ fi
+ # Now check the RMS value. If it isn't 0 then mark this as questionable accuracy.
+ ACCURACY_VALUE=`echo "$ACCURACY_RUN" | grep 'Byte level'`
+ if [[ ! $ACCURACY_VALUE == *0 ]]; then
+ ACCURACY=!`echo $ACCURACY_VALUE | sed 's/[a-zA-Z:]*//g'`
+ else
+ ACCURACY="OK"
+ fi
+ # Add on the * if we needed to add CpuRef.
+ if [ -z $REQUIRES_CPUREF ]; then
+ echo -e "$ACCURACY $REQUIRES_CPUREF\t\t"
+ else
+ echo -e "$ACCURACY\t\t"
+ fi
+}
+
+# This function will execute the model and return a string representation of the results. The execution
+# Is done wth -c $BACKEND,CpuRef to allow the odd layer to ot be supported by an accelerated backend.
+#
+# Parameters:
+# $1 Backend string like CpuRef.
+# $2 Additional ExecuteNetwork parameters.
+#
+function RunPerformanceOnBackendWithParameters {
+ BACKEND=$1
+ ADDITIONAL_PARAM=$2
+ # Execute with 6 inferences. Mark the first as initial inference. Average the rest.
+ SPEED_RUN=`$EXECUTE_NETWORK -m $MODEL -c $BACKEND,CpuRef -I 6 -N $ADDITIONAL_PARAM`
+
+ # Extract the model load time
+ MODEL_LOAD_TIME=`echo "$SPEED_RUN" | grep "Initialization time" | sed 's/[a-zA-Z:]*//g'`
+ MODEL_LOAD_TIME=`echo ${MODEL_LOAD_TIME::-2}` # Remove the tailing space and full stop.
+ # and the optimization time.
+ OPTIMIZATION_TIME=`echo "$SPEED_RUN" | grep "Optimization time" | sed 's/[a-zA-Z:]*//g'`
+ OPTIMIZATION_TIME=`echo ${OPTIMIZATION_TIME::-1}` # Remove the tailing space.
+
+ # All 6 inference times.
+ RAW_INFERENCE=`echo "$SPEED_RUN" | grep "Inference time"`
+ # This will take "Info: Inference time: 0.03 ms Info:..." and transform to "0.03 0.01 0.01"
+ INFERENCE_TIMES=`echo $RAW_INFERENCE | sed 's/[a-zA-Z:]*//g'`
+ INITIAL_INFERENCE_TIME=`echo $INFERENCE_TIMES | cut -d ' ' -f 1`
+ # Now remove the initial inference time as it will skew the average.
+ INFERENCE_TIMES=`echo $INFERENCE_TIMES | sed 's/[^ ]* //'`
+ # Use awk to sum and average the remaining 5 numbers.
+ AVERAGE_INFERENCE_TIME=`echo $INFERENCE_TIMES | awk '{s+=$1}END{print s/NR}' RS=" "`
+
+ # Result format is: MODEL LOAD | OPTIMIZATION | INITIAL INFERENCE | AVERAGE INFERENCE
+ echo -e "$MODEL_LOAD_TIME\t\t$OPTIMIZATION_TIME\t\t\t$INITIAL_INFERENCE_TIME\t\t\t$AVERAGE_INFERENCE_TIME\t"
+}
+
+
+# Check execution in all available backends.
+echo "==================================================================================="
+echo -e "BACKEND\t\tACCURACY\tMODEL LOAD(ms)\tOPTIMIZATION(ms)\tINITIAL INFERENCE(ms)\tAVERAGE INFERENCE(ms)"
+for backend in $BACKENDS
+do
+ echo -n -e "$backend\t\t"
+ RESULT=$(RunAccuracyOnBackendWithParameters $backend)
+ echo -n -e "$RESULT"
+ if [[ $RESULT =~ "*" ]]; then
+ REQUIRED_CPU_REF=1
+ fi
+ # It's possible the backend wasn't supported.
+ if [[ ! "$RESULT" =~ "not supported" ]]; then
+ # It was, continue.
+ RESULT=$(RunPerformanceOnBackendWithParameters $backend)
+ echo -n -e "$RESULT"
+ # Save some specific values for use later.
+ if [ $backend == "CpuAcc" ]; then
+ # In the case of CpuAcc we save the avrage inference time.
+ CPUACC_AVERAGE_INFERENCE_TIME=`echo $RESULT | cut -d ' ' -f 4`
+ fi
+ if [ $backend == "GpuAcc" ]; then
+ # In the case of GpuAcc we save the avrage inference time.
+ GPUACC_AVERAGE_INFERENCE_TIME=`echo $RESULT | cut -d ' ' -f 4`
+ fi
+ else
+ # Remove this backend from future tests.
+ BACKENDS=`echo $BACKENDS | sed "s/$backend//"`
+ fi
+ echo
+done
+# Only print this if it was required.
+if [ ! -z $REQUIRED_CPU_REF ]; then
+ echo "* denotes this backend required fallback to CpuRef."
+ echo
+fi
+
+# Now its time to look at backend specific parameters.
+
+# This function first run the accuracy test and then the performance test. It uses the average from earlier
+# to compare to.
+function RunAccuracyAndPerformanceWithExtraParameter
+{
+ BACKEND=$1
+ EXTRA_PARAM=$2
+ AVERAGE_INFERENCE_TIME=$3
+ echo -e "ACCURACY\tMODEL LOAD(ms)\tOPTIMIZATION(ms)\tINITIAL INFERENCE(ms)\tAVERAGE INFERENCE(ms)\t\tDELTA(ms)"
+ RESULT=$(RunAccuracyOnBackendWithParameters $BACKEND,CpuRef $EXTRA_PARAM)
+ echo -n "$RESULT"
+ RESULT=$(RunPerformanceOnBackendWithParameters $BACKEND,CpuRef $EXTRA_PARAM)
+ PARAM_AVERAGE_INFERENCE_TIME=`echo $RESULT | cut -d ' ' -f 4`
+ # If adding the parameter was faster then incude by how much.
+ if [[ "$PARAM_AVERAGE_INFERENCE_TIME" < "$AVERAGE_INFERENCE_TIME" ]]; then
+ DELTA=`echo $AVERAGE_INFERENCE_TIME - $PARAM_AVERAGE_INFERENCE_TIME | bc`
+ echo -e "$RESULT\t\t\t$DELTA ($PARAM_AVERAGE_INFERENCE_TIME v $AVERAGE_INFERENCE_TIME)"
+ else
+ echo -e "$RESULT\t\t\t**No improvment**"
+ fi
+}
+
+
+# Start with CpuAcc. Three knobs to twiddle, threads, fast-math and fp16.
+if [[ $BACKENDS =~ "CpuAcc" ]]; then
+ echo
+ echo "CpuAcc optimizations."
+ echo "============================"
+ echo "The value of \"number-of-threads\" parameter by default is decided on by the backend."
+ echo "Cycle through number-of-threads=1 -> 12 and see if any are faster than the default."
+ echo
+ for i in {1..12}
+ do
+ RESULT=$(RunPerformanceOnBackendWithParameters "CpuAcc,CpuRef" "--number-of-threads $i")
+ AVERAGE_INFERENCE_TIME=`echo $RESULT | cut -d ' ' -f 4`
+ # Print something out if the returned average is less than the previously saved average.
+ if (( $(echo "$AVERAGE_INFERENCE_TIME < $CPUACC_AVERAGE_INFERENCE_TIME" | bc -l) )); then
+ DELTA=`echo $CPUACC_AVERAGE_INFERENCE_TIME - $AVERAGE_INFERENCE_TIME | bc`
+ echo " \"--number-of-threads $i\" resulted in a faster average inference by $DELTA ms. ($AVERAGE_INFERENCE_TIME v $CPUACC_AVERAGE_INFERENCE_TIME)"
+ FASTER=1
+ fi
+ done
+ if [ -z $FASTER ]; then
+ echo "No value of \"number-of-threads\" was faster than the default."
+ fi
+ # Next is fp16-turbo-mode. We do both accuracy and speed on this one.
+ echo
+ echo -n "Now trying to enable fp16-turbo-mode. This will only have positive results with fp32 models."
+ echo
+ RunAccuracyAndPerformanceWithExtraParameter CpuAcc "--fp16-turbo-mode" $CPUACC_AVERAGE_INFERENCE_TIME
+
+ # Next is enable-fast-math. Again both accuracy and speed on this one.
+ echo
+ echo -n "Now trying \"enable-fast-math\"."
+ echo
+ RunAccuracyAndPerformanceWithExtraParameter CpuAcc "--enable-fast-math" $CPUACC_AVERAGE_INFERENCE_TIME
+fi
+
+# GpuAcc.
+# Options to check enable-fast-math, fp16-turbo-mode, and tuning-level/tuning-path.
+if [[ $BACKENDS =~ "GpuAcc" ]]; then
+ echo
+ echo "GpuAcc optimizations."
+ echo "============================"
+
+ # fp16-turbo-mode. We do both accuracy and speed on this one.
+ echo
+ echo -n "Now trying to enable fp16-turbo-mode. This will only have positive results with fp32 models."
+ echo
+ RunAccuracyAndPerformanceWithExtraParameter GpuAcc "--fp16-turbo-mode" $GPUACC_AVERAGE_INFERENCE_TIME
+
+ # Next is enable-fast-math. Again both accuracy and speed on this one.
+ echo
+ echo -n "Now trying \"enable-fast-math\"."
+ echo
+ RunAccuracyAndPerformanceWithExtraParameter GpuAcc "--enable-fast-math" $GPUACC_AVERAGE_INFERENCE_TIME
+
+ # Next is tuning levels. Just speed on this one.
+ echo
+ echo -n "Now trying \"tuning-level/tuning-path\"."
+ echo
+ for i in {1..3}
+ do
+ touch ./tuned-network.bin
+ # Create tuned network file with the first run.
+ OUTPUT=`$EXECUTE_NETWORK -m $MODEL -c $GpuAcc,CpuRef --tuning-path ./tuned-network.bin --tuning-level $i -N`
+ AssertZeroExitCode
+ # Now run the perforance test reusing that saved network.
+ RESULT=$(RunPerformanceOnBackendWithParameters "GpuAcc,CpuRef" "--tuning-path ./tuned-network.bin")
+ AVERAGE_INFERENCE_TIME=`echo $RESULT | cut -d ' ' -f 4`
+ if (( $(echo "$AVERAGE_INFERENCE_TIME < $GPUACC_AVERAGE_INFERENCE_TIME" | bc -l) )); then
+ DELTA=`echo $AVERAGE_INFERENCE_TIME - $GPUACC_AVERAGE_INFERENCE_TIME | bc`
+ echo " \"--tuning-level $i\" resulted in a faster average inference by $DELTA ms. ($AVERAGE_INFERENCE_TIME v $GPUACC_AVERAGE_INFERENCE_TIME)"
+ else
+ echo " \"--tuning-level $i\" did not result in a faster average inference time. ($AVERAGE_INFERENCE_TIME v $GPUACC_AVERAGE_INFERENCE_TIME)"
+ fi
+ rm ./tuned-network.bin
+ done
+fi