blob: a010717412ee2c3dd5831859d42d0217ca76d8d8 [file] [log] [blame]
Jim Flynn27a9bd92020-11-12 15:48:34 +00001//
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>
15#include <armnn/BackendRegistry.hpp>
16#include <armnn/IRuntime.hpp>
17#include <armnn/utility/NumericCast.hpp>
18#include <armnnTfLiteParser/ITfLiteParser.hpp>
19
20// Application parameters
21std::vector<armnn::BackendId> preferred_backends_order = {armnn::Compute::CpuAcc, armnn::Compute::CpuRef};
22std::string model_file_str;
23std::string preferred_backend_str;
24int nb_loops = 1;
25
26double get_us(struct timeval t)
27{
28 return (armnn::numeric_cast<double>(t.tv_sec) *
29 armnn::numeric_cast<double>(1000000) +
30 armnn::numeric_cast<double>(t.tv_usec));
31}
32
33double get_ms(struct timeval t)
34{
35 return (armnn::numeric_cast<double>(t.tv_sec) *
36 armnn::numeric_cast<double>(1000) +
37 armnn::numeric_cast<double>(t.tv_usec) / 1000);
38}
39
40static void print_help(char** argv)
41{
42 std::cout <<
43 "Usage: " << argv[0] << " -m <model .tflite>\n"
44 "\n"
45 "-m --model_file <.tflite file path>: .tflite model to be executed\n"
46 "-b --backend <device>: preferred backend device to run layers on by default. Possible choices: "
47 << armnn::BackendRegistryInstance().GetBackendIdsAsString() << "\n"
48 "-l --loops <int>: provide the number of time the inference will be executed\n"
49 " (by default nb_loops=1)\n"
50 "--help: show this help\n";
51 exit(1);
52}
53
54void process_args(int argc, char** argv)
55{
56 const char* const short_opts = "m:b:l:h";
57 const option long_opts[] = {
58 {"model_file", required_argument, nullptr, 'm'},
59 {"backend", required_argument, nullptr, 'b'},
60 {"loops", required_argument, nullptr, 'l'},
61 {"help", no_argument, nullptr, 'h'},
62 {nullptr, no_argument, nullptr, 0}
63 };
64
65 while (true)
66 {
67 const auto opt = getopt_long(argc, argv, short_opts, long_opts, nullptr);
68
69 if (-1 == opt)
70 {
71 break;
72 }
73
74 switch (opt)
75 {
76 case 'm':
77 model_file_str = std::string(optarg);
78 std::cout << "model file set to: " << model_file_str << std::endl;
79 break;
80 case 'b':
81 preferred_backend_str = std::string(optarg);
82 // Overwrite the prefered backend order
83 if (preferred_backend_str == "CpuAcc")
84 {
85 preferred_backends_order = {armnn::Compute::CpuAcc, armnn::Compute::CpuRef};
86 }
87 else if (preferred_backend_str == "CpuRef")
88 {
89 preferred_backends_order = {armnn::Compute::CpuRef, armnn::Compute::CpuAcc};
90 }
91
92 std::cout << "preferred backend device set to:";
93 for (unsigned int i = 0; i < preferred_backends_order.size(); i++)
94 {
95 std::cout << " " << preferred_backends_order.at(i);
96 }
97 std::cout << std::endl;
98 break;
99 case 'l':
100 nb_loops = std::stoi(optarg);
101 std::cout << "benchmark will execute " << nb_loops << " inference(s)" << std::endl;
102 break;
103 case 'h': // -h or --help
104 case '?': // Unrecognized option
105 default:
106 print_help(argv);
107 break;
108 }
109 }
110
111 if (model_file_str.empty())
112 {
113 print_help(argv);
114 }
115}
116
117int main(int argc, char* argv[])
118{
119 std::vector<double> inferenceTimes;
120
121 // Get options
122 process_args(argc, argv);
123
124 // Create the runtime
125 armnn::IRuntime::CreationOptions options;
126 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
127
128 // Create Parser
129 armnnTfLiteParser::ITfLiteParserPtr armnnparser(armnnTfLiteParser::ITfLiteParser::Create());
130
131 // Create a network
132 armnn::INetworkPtr network = armnnparser->CreateNetworkFromBinaryFile(model_file_str.c_str());
133 if (!network)
134 {
135 throw armnn::Exception("Failed to create an ArmNN network");
136 }
137
138 // Optimize the network
139 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*network,
140 preferred_backends_order,
141 runtime->GetDeviceSpec());
142 armnn::NetworkId networkId;
143
144 // Load the network in to the runtime
145 runtime->LoadNetwork(networkId, std::move(optimizedNet));
146
147 // Check the number of subgraph
148 if (armnnparser->GetSubgraphCount() != 1)
149 {
150 std::cout << "Model with more than 1 subgraph is not supported by this benchmark application.\n";
151 exit(0);
152 }
153 size_t subgraphId = 0;
154
155 // Set up the input network
156 std::cout << "\nModel information:" << std::endl;
157 std::vector<armnnTfLiteParser::BindingPointInfo> inputBindings;
158 std::vector<armnn::TensorInfo> inputTensorInfos;
159 std::vector<std::string> inputTensorNames = armnnparser->GetSubgraphInputTensorNames(subgraphId);
160 for (unsigned int i = 0; i < inputTensorNames.size() ; i++)
161 {
162 std::cout << "inputTensorNames[" << i << "] = " << inputTensorNames[i] << std::endl;
163 armnnTfLiteParser::BindingPointInfo inputBinding = armnnparser->GetNetworkInputBindingInfo(
164 subgraphId,
165 inputTensorNames[i]);
166 armnn::TensorInfo inputTensorInfo = runtime->GetInputTensorInfo(networkId, inputBinding.first);
167 inputBindings.push_back(inputBinding);
168 inputTensorInfos.push_back(inputTensorInfo);
169 }
170
171 // Set up the output network
172 std::vector<armnnTfLiteParser::BindingPointInfo> outputBindings;
173 std::vector<armnn::TensorInfo> outputTensorInfos;
174 std::vector<std::string> outputTensorNames = armnnparser->GetSubgraphOutputTensorNames(subgraphId);
175 for (unsigned int i = 0; i < outputTensorNames.size() ; i++)
176 {
177 std::cout << "outputTensorNames[" << i << "] = " << outputTensorNames[i] << std::endl;
178 armnnTfLiteParser::BindingPointInfo outputBinding = armnnparser->GetNetworkOutputBindingInfo(
179 subgraphId,
180 outputTensorNames[i]);
181 armnn::TensorInfo outputTensorInfo = runtime->GetOutputTensorInfo(networkId, outputBinding.first);
182 outputBindings.push_back(outputBinding);
183 outputTensorInfos.push_back(outputTensorInfo);
184 }
185
186 // Allocate input tensors
187 unsigned int nb_inputs = armnn::numeric_cast<unsigned int>(inputTensorInfos.size());
188 armnn::InputTensors inputTensors;
189 std::vector<std::vector<float>> in;
190 for (unsigned int i = 0 ; i < nb_inputs ; i++)
191 {
192 std::vector<float> in_data(inputTensorInfos.at(i).GetNumElements());
193 in.push_back(in_data);
194 inputTensors.push_back({ inputBindings[i].first, armnn::ConstTensor(inputBindings[i].second, in.data()) });
195 }
196
197 // Allocate output tensors
198 unsigned int nb_ouputs = armnn::numeric_cast<unsigned int>(outputTensorInfos.size());
199 armnn::OutputTensors outputTensors;
200 std::vector<std::vector<float>> out;
201 for (unsigned int i = 0; i < nb_ouputs ; i++)
202 {
203 std::vector<float> out_data(outputTensorInfos.at(i).GetNumElements());
204 out.push_back(out_data);
205 outputTensors.push_back({ outputBindings[i].first, armnn::Tensor(outputBindings[i].second, out[i].data()) });
206 }
207
208 // Run the inferences
209 std::cout << "\ninferences are running: " << std::flush;
210 for (int i = 0 ; i < nb_loops ; i++)
211 {
212 struct timeval start_time, stop_time;
213 gettimeofday(&start_time, nullptr);
214
215 runtime->EnqueueWorkload(networkId, inputTensors, outputTensors);
216
217 gettimeofday(&stop_time, nullptr);
218 inferenceTimes.push_back((get_us(stop_time) - get_us(start_time)));
219 std::cout << "# " << std::flush;
220 }
221
222 auto maxInfTime = *std::max_element(inferenceTimes.begin(), inferenceTimes.end());
223 auto minInfTime = *std::min_element(inferenceTimes.begin(), inferenceTimes.end());
224 auto avgInfTime = accumulate(inferenceTimes.begin(), inferenceTimes.end(), 0.0) /
225 armnn::numeric_cast<double>(inferenceTimes.size());
226 std::cout << "\n\ninference time: ";
227 std::cout << "min=" << minInfTime << "us ";
228 std::cout << "max=" << maxInfTime << "us ";
229 std::cout << "avg=" << avgInfTime << "us" << std::endl;
230
231 return 0;
232}