Imported Upstream version 1.12.0
[platform/core/ml/nnfw.git] / tests / tools / tflite_vanilla_run / src / tflite_vanilla_run.cc
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "tensorflow/lite/model.h"
18 #include "tensorflow/lite/kernels/register.h"
19
20 #include "args.h"
21 #include "tensor_view.h"
22 #include "misc/EnvVar.h"
23 #include "misc/RandomGenerator.h"
24 #include "misc/tensor/IndexIterator.h"
25 #include "misc/tensor/Object.h"
26 #include "benchmark.h"
27
28 #include <iostream>
29 #include <chrono>
30 #include <algorithm>
31 #include <vector>
32 #include <memory>
33
34 using namespace std::placeholders; // for _1, _2 ...
35
36 #define TFLITE_ENSURE(exp)                                             \
37   {                                                                    \
38     const TfLiteStatus status = (exp);                                 \
39                                                                        \
40     if (status != kTfLiteOk)                                           \
41     {                                                                  \
42       std::ostringstream ss;                                           \
43       ss << #exp << " failed (" << __FILE__ << ":" << __LINE__ << ")"; \
44       throw std::runtime_error{ss.str()};                              \
45     }                                                                  \
46   }
47
48 namespace
49 {
50
51 void print_max_idx(float *f, int size)
52 {
53   float *p = std::max_element(f, f + size);
54   std::cout << "max:" << p - f;
55 }
56
57 static const char *default_backend_cand = "tflite_cpu";
58
59 // Verifies whether the model is a flatbuffer file.
60 class BMFlatBufferVerifier : public tflite::TfLiteVerifier
61 {
62 public:
63   bool Verify(const char *data, int length, tflite::ErrorReporter *reporter) override
64   {
65
66     flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t *>(data), length);
67     if (!tflite::VerifyModelBuffer(verifier))
68     {
69       reporter->Report("The model is not a valid Flatbuffer file");
70       return false;
71     }
72     return true;
73   }
74 };
75
76 } // namespace anonymous
77
78 int main(const int argc, char **argv)
79 {
80   tflite::StderrReporter error_reporter;
81
82   TFLiteVanillaRun::Args args(argc, argv);
83
84   std::chrono::milliseconds t_model_load(0), t_prepare(0);
85
86   // TODO Apply verbose level to phases
87   const int verbose = args.getVerboseLevel();
88   benchmark::Phases phases(
89     benchmark::PhaseOption{args.getMemoryPoll(), args.getGpuMemoryPoll(), args.getRunDelay()});
90
91   std::unique_ptr<tflite::FlatBufferModel> model;
92   std::unique_ptr<tflite::Interpreter> interpreter;
93   std::unique_ptr<tflite::TfLiteVerifier> verifier{new BMFlatBufferVerifier};
94
95   try
96   {
97     phases.run("MODEL_LOAD", [&](const benchmark::Phase &, uint32_t) {
98       if (args.getModelValidate())
99       {
100         model = tflite::FlatBufferModel::VerifyAndBuildFromFile(args.getTFLiteFilename().c_str(),
101                                                                 verifier.get(), &error_reporter);
102       }
103       else
104       {
105         model =
106           tflite::FlatBufferModel::BuildFromFile(args.getTFLiteFilename().c_str(), &error_reporter);
107       }
108       if (model == nullptr)
109       {
110         throw std::runtime_error{"Cannot create model"};
111       }
112
113       // Use tflite's resolver, not onert's one
114       tflite::ops::builtin::BuiltinOpResolver resolver;
115       tflite::InterpreterBuilder builder(*model, resolver);
116       TFLITE_ENSURE(builder(&interpreter))
117       interpreter->SetNumThreads(nnfw::misc::EnvVar("THREAD").asInt(-1));
118     });
119   }
120   catch (const std::exception &e)
121   {
122     std::cerr << e.what() << '\n';
123     return 1;
124   }
125
126   const bool use_nnapi = nnfw::misc::EnvVar("USE_NNAPI").asBool(false);
127
128   try
129   {
130     phases.run("PREPARE", [&](const benchmark::Phase &, uint32_t) {
131       interpreter->UseNNAPI(use_nnapi);
132       interpreter->AllocateTensors();
133     });
134   }
135   catch (const std::exception &e)
136   {
137     std::cerr << e.what() << '\n';
138     return 1;
139   }
140
141   const int seed = 1; /* TODO Add an option for seed value */
142   nnfw::misc::RandomGenerator randgen{seed, 0.0f, 2.0f};
143
144   // No input specified. So we fill the input tensors with random values.
145   for (const auto &o : interpreter->inputs())
146   {
147     TfLiteTensor *tensor = interpreter->tensor(o);
148     if (tensor->type == kTfLiteInt32)
149     {
150       // Generate singed 32-bit integer (s32) input
151       auto tensor_view = TFLiteVanillaRun::TensorView<int32_t>::make(*interpreter, o);
152
153       int32_t value = 0;
154
155       nnfw::misc::tensor::iterate(tensor_view.shape())
156         << [&](const nnfw::misc::tensor::Index &ind) {
157              // TODO Generate random values
158              // Gather operation: index should be within input coverage.
159              tensor_view.at(ind) = value;
160              value++;
161            };
162     }
163     else if (tensor->type == kTfLiteUInt8)
164     {
165       // Generate unsigned 8-bit integer input
166       auto tensor_view = TFLiteVanillaRun::TensorView<uint8_t>::make(*interpreter, o);
167
168       uint8_t value = 0;
169
170       nnfw::misc::tensor::iterate(tensor_view.shape())
171         << [&](const nnfw::misc::tensor::Index &ind) {
172              // TODO Generate random values
173              tensor_view.at(ind) = value;
174              value = (value + 1) & 0xFF;
175            };
176     }
177     else if (tensor->type == kTfLiteBool)
178     {
179       // Generate bool input
180       auto tensor_view = TFLiteVanillaRun::TensorView<bool>::make(*interpreter, o);
181
182       auto fp = static_cast<bool (nnfw::misc::RandomGenerator::*)(
183         const ::nnfw::misc::tensor::Shape &, const ::nnfw::misc::tensor::Index &)>(
184         &nnfw::misc::RandomGenerator::generate<bool>);
185       const nnfw::misc::tensor::Object<bool> data(tensor_view.shape(),
186                                                   std::bind(fp, randgen, _1, _2));
187
188       nnfw::misc::tensor::iterate(tensor_view.shape())
189         << [&](const nnfw::misc::tensor::Index &ind) {
190              const auto value = data.at(ind);
191              tensor_view.at(ind) = value;
192            };
193     }
194     else
195     {
196       assert(tensor->type == kTfLiteFloat32);
197
198       const float *end = reinterpret_cast<const float *>(tensor->data.raw_const + tensor->bytes);
199       for (float *ptr = tensor->data.f; ptr < end; ptr++)
200       {
201         *ptr = randgen.generate<float>();
202       }
203     }
204   }
205
206   std::cout << "input tensor indices = [";
207   for (const auto &o : interpreter->inputs())
208   {
209     std::cout << o << ",";
210   }
211   std::cout << "]" << std::endl;
212
213   // NOTE: Measuring memory can't avoid taking overhead. Therefore, memory will be measured on the
214   // only warmup.
215   if (verbose == 0)
216   {
217     phases.run(
218       "WARMUP", [&](const benchmark::Phase &, uint32_t) { interpreter->Invoke(); },
219       args.getWarmupRuns());
220     phases.run(
221       "EXECUTE", [&](const benchmark::Phase &, uint32_t) { interpreter->Invoke(); },
222       args.getNumRuns(), true);
223   }
224   else
225   {
226     phases.run(
227       "WARMUP", [&](const benchmark::Phase &, uint32_t) { interpreter->Invoke(); },
228       [&](const benchmark::Phase &phase, uint32_t nth) {
229         std::cout << "... "
230                   << "warmup " << nth + 1 << " takes " << phase.time[nth] / 1e3 << " ms"
231                   << std::endl;
232       },
233       args.getWarmupRuns());
234     phases.run(
235       "EXECUTE", [&](const benchmark::Phase &, uint32_t) { interpreter->Invoke(); },
236       [&](const benchmark::Phase &phase, uint32_t nth) {
237         std::cout << "... "
238                   << "run " << nth + 1 << " takes " << phase.time[nth] / 1e3 << " ms" << std::endl;
239       },
240       args.getNumRuns(), true);
241   }
242
243   std::cout << "output tensor indices = [";
244   for (const auto &o : interpreter->outputs())
245   {
246     std::cout << o << "(";
247
248     print_max_idx(interpreter->tensor(o)->data.f, interpreter->tensor(o)->bytes / sizeof(float));
249
250     std::cout << "),";
251   }
252   std::cout << "]" << std::endl;
253
254   // TODO Apply verbose level to result
255
256   // prepare result
257   benchmark::Result result(phases);
258
259   // to stdout
260   benchmark::printResult(result);
261
262   if (args.getWriteReport())
263   {
264     // prepare csv task
265     std::string exec_basename;
266     std::string model_basename;
267     std::string backend_name = default_backend_cand;
268     {
269       std::vector<char> vpath(args.getTFLiteFilename().begin(), args.getTFLiteFilename().end() + 1);
270       model_basename = basename(vpath.data());
271       size_t lastindex = model_basename.find_last_of(".");
272       model_basename = model_basename.substr(0, lastindex);
273       exec_basename = basename(argv[0]);
274     }
275     benchmark::writeResult(result, exec_basename, model_basename, backend_name);
276   }
277
278   return 0;
279 }