ArmNN
 21.05
TfLiteBenchmark-Armnn.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2020 STMicroelectronics and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include <algorithm>
7 #include <getopt.h>
8 #include <numeric>
9 #include <signal.h>
10 #include <string>
11 #include <sys/time.h>
12 #include <vector>
13 
14 #include <armnn/BackendId.hpp>
16 #include <armnn/IRuntime.hpp>
19 
20 // Application parameters
22 std::vector<armnn::BackendId> preferred_backends_order;
23 std::string model_file_str;
25 int nb_loops = 1;
26 
27 double get_us(struct timeval t)
28 {
29  return (armnn::numeric_cast<double>(t.tv_sec) *
30  armnn::numeric_cast<double>(1000000) +
31  armnn::numeric_cast<double>(t.tv_usec));
32 }
33 
34 double get_ms(struct timeval t)
35 {
36  return (armnn::numeric_cast<double>(t.tv_sec) *
37  armnn::numeric_cast<double>(1000) +
38  armnn::numeric_cast<double>(t.tv_usec) / 1000);
39 }
40 
41 static void print_help(char** argv)
42 {
43  std::cout <<
44  "Usage: " << argv[0] << " -m <model .tflite>\n"
45  "\n"
46  "-m --model_file <.tflite file path>: .tflite model to be executed\n"
47  "-b --backend <device>: preferred backend device to run layers on by default. Possible choices: "
49  " (by default CpuAcc, CpuRef)\n"
50  "-l --loops <int>: provide the number of times the inference will be executed\n"
51  " (by default nb_loops=1)\n"
52  "--help: show this help\n";
53  exit(1);
54 }
55 
56 void process_args(int argc, char** argv)
57 {
58  const char* const short_opts = "m:b:l:h";
59  const option long_opts[] = {
60  {"model_file", required_argument, nullptr, 'm'},
61  {"backend", required_argument, nullptr, 'b'},
62  {"loops", required_argument, nullptr, 'l'},
63  {"help", no_argument, nullptr, 'h'},
64  {nullptr, no_argument, nullptr, 0}
65  };
66 
67  while (true)
68  {
69  const auto opt = getopt_long(argc, argv, short_opts, long_opts, nullptr);
70 
71  if (-1 == opt)
72  {
73  break;
74  }
75 
76  switch (opt)
77  {
78  case 'm':
79  model_file_str = std::string(optarg);
80  std::cout << "model file set to: " << model_file_str << std::endl;
81  break;
82  case 'b':
83  preferred_backend_str = std::string(optarg);
84  // Overwrite the backend
86 
87  std::cout << "backend device set to:" << preferred_backend_str << std::endl;;
88  break;
89  case 'l':
90  nb_loops = std::stoi(optarg);
91  std::cout << "benchmark will execute " << nb_loops << " inference(s)" << std::endl;
92  break;
93  case 'h': // -h or --help
94  case '?': // Unrecognized option
95  default:
96  print_help(argv);
97  break;
98  }
99  }
100 
101  if (model_file_str.empty())
102  {
103  print_help(argv);
104  }
105 }
106 
107 int main(int argc, char* argv[])
108 {
109  std::vector<double> inferenceTimes;
110 
111  // Get options
112  process_args(argc, argv);
113 
114  // Create the runtime
117 
118  // Create Parser
120 
121  // Create a network
122  armnn::INetworkPtr network = armnnparser->CreateNetworkFromBinaryFile(model_file_str.c_str());
123  if (!network)
124  {
125  throw armnn::Exception("Failed to create an ArmNN network");
126  }
127 
128  // Optimize the network
129  if (preferred_backends_order.size() == 0)
130  {
132  }
133  armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*network,
135  runtime->GetDeviceSpec());
136  armnn::NetworkId networkId;
137 
138  // Load the network in to the runtime
139  runtime->LoadNetwork(networkId, std::move(optimizedNet));
140 
141  // Check the number of subgraph
142  if (armnnparser->GetSubgraphCount() != 1)
143  {
144  std::cout << "Model with more than 1 subgraph is not supported by this benchmark application.\n";
145  exit(0);
146  }
147  size_t subgraphId = 0;
148 
149  // Set up the input network
150  std::cout << "\nModel information:" << std::endl;
151  std::vector<armnnTfLiteParser::BindingPointInfo> inputBindings;
152  std::vector<armnn::TensorInfo> inputTensorInfos;
153  std::vector<std::string> inputTensorNames = armnnparser->GetSubgraphInputTensorNames(subgraphId);
154  for (unsigned int i = 0; i < inputTensorNames.size() ; i++)
155  {
156  std::cout << "inputTensorNames[" << i << "] = " << inputTensorNames[i] << std::endl;
157  armnnTfLiteParser::BindingPointInfo inputBinding = armnnparser->GetNetworkInputBindingInfo(
158  subgraphId,
159  inputTensorNames[i]);
160  armnn::TensorInfo inputTensorInfo = runtime->GetInputTensorInfo(networkId, inputBinding.first);
161  inputBindings.push_back(inputBinding);
162  inputTensorInfos.push_back(inputTensorInfo);
163  }
164 
165  // Set up the output network
166  std::vector<armnnTfLiteParser::BindingPointInfo> outputBindings;
167  std::vector<armnn::TensorInfo> outputTensorInfos;
168  std::vector<std::string> outputTensorNames = armnnparser->GetSubgraphOutputTensorNames(subgraphId);
169  for (unsigned int i = 0; i < outputTensorNames.size() ; i++)
170  {
171  std::cout << "outputTensorNames[" << i << "] = " << outputTensorNames[i] << std::endl;
172  armnnTfLiteParser::BindingPointInfo outputBinding = armnnparser->GetNetworkOutputBindingInfo(
173  subgraphId,
174  outputTensorNames[i]);
175  armnn::TensorInfo outputTensorInfo = runtime->GetOutputTensorInfo(networkId, outputBinding.first);
176  outputBindings.push_back(outputBinding);
177  outputTensorInfos.push_back(outputTensorInfo);
178  }
179 
180  // Allocate input tensors
181  unsigned int nb_inputs = armnn::numeric_cast<unsigned int>(inputTensorInfos.size());
182  armnn::InputTensors inputTensors;
183  std::vector<std::vector<float>> in;
184  for (unsigned int i = 0 ; i < nb_inputs ; i++)
185  {
186  std::vector<float> in_data(inputTensorInfos.at(i).GetNumElements());
187  in.push_back(in_data);
188  inputTensors.push_back({ inputBindings[i].first, armnn::ConstTensor(inputBindings[i].second, in[i].data()) });
189  }
190 
191  // Allocate output tensors
192  unsigned int nb_ouputs = armnn::numeric_cast<unsigned int>(outputTensorInfos.size());
193  armnn::OutputTensors outputTensors;
194  std::vector<std::vector<float>> out;
195  for (unsigned int i = 0; i < nb_ouputs ; i++)
196  {
197  std::vector<float> out_data(outputTensorInfos.at(i).GetNumElements());
198  out.push_back(out_data);
199  outputTensors.push_back({ outputBindings[i].first, armnn::Tensor(outputBindings[i].second, out[i].data()) });
200  }
201 
202  // Run the inferences
203  std::cout << "\ninferences are running: " << std::flush;
204  for (int i = 0 ; i < nb_loops ; i++)
205  {
206  struct timeval start_time, stop_time;
207  gettimeofday(&start_time, nullptr);
208 
209  runtime->EnqueueWorkload(networkId, inputTensors, outputTensors);
210 
211  gettimeofday(&stop_time, nullptr);
212  inferenceTimes.push_back((get_us(stop_time) - get_us(start_time)));
213  std::cout << "# " << std::flush;
214  }
215 
216  auto maxInfTime = *std::max_element(inferenceTimes.begin(), inferenceTimes.end());
217  auto minInfTime = *std::min_element(inferenceTimes.begin(), inferenceTimes.end());
218  auto avgInfTime = accumulate(inferenceTimes.begin(), inferenceTimes.end(), 0.0) /
219  armnn::numeric_cast<double>(inferenceTimes.size());
220  std::cout << "\n\ninference time: ";
221  std::cout << "min=" << minInfTime << "us ";
222  std::cout << "max=" << maxInfTime << "us ";
223  std::cout << "avg=" << avgInfTime << "us" << std::endl;
224 
225  return 0;
226 }
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:37
CPU Execution: Reference C++ kernels.
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:28
int nb_loops
BackendRegistry & BackendRegistryInstance()
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
Definition: Tensor.hpp:340
std::unique_ptr< ITfLiteParser, void(*)(ITfLiteParser *parser)> ITfLiteParserPtr
std::string GetBackendIdsAsString() const
static ITfLiteParserPtr Create(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:306
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
Definition: Network.cpp:1568
int NetworkId
Definition: IRuntime.hpp:22
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:314
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
Definition: Tensor.hpp:341
double get_us(struct timeval t)
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:174
void process_args(int argc, char **argv)
std::string model_file_str
int main(int argc, char *argv[])
std::vector< armnn::BackendId > default_preferred_backends_order
double get_ms(struct timeval t)
std::string preferred_backend_str
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
CPU Execution: NEON: ArmCompute.
std::vector< armnn::BackendId > preferred_backends_order
armnn::BindingPointInfo BindingPointInfo
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:173