Renamed namespace (tidl -> tinn), updated documentation
[tidl/tidl-api.git] / examples / test / main.cpp
1 /******************************************************************************
2  * Copyright (c) 2017, Texas Instruments Incorporated - http://www.ti.com/
3  *   All rights reserved.
4  *
5  *   Redistribution and use in source and binary forms, with or without
6  *   modification, are permitted provided that the following conditions are met:
7  *       * Redistributions of source code must retain the above copyright
8  *         notice, this list of conditions and the following disclaimer.
9  *       * Redistributions in binary form must reproduce the above copyright
10  *         notice, this list of conditions and the following disclaimer in the
11  *         documentation and/or other materials provided with the distribution.
12  *       * Neither the name of Texas Instruments Incorporated nor the
13  *         names of its contributors may be used to endorse or promote products
14  *         derived from this software without specific prior written permission.
15  *
16  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  *   THE POSSIBILITY OF SUCH DAMAGE.
27  *****************************************************************************/
28 #include <signal.h>
29 #include <getopt.h>
30 #include <iostream>
31 #include <iomanip>
32 #include <fstream>
33 #include <cassert>
34 #include <string>
35 #include <functional>
36 #include <algorithm>
37 #include <time.h>
39 #include "executor.h"
40 #include "execution_object.h"
41 #include "configuration.h"
43 bool __TI_show_debug_ = false;
45 using namespace tinn;
47 bool RunMultipleExecutors(const std::string& config_file_1,
48                           const std::string& config_file_2,
49                           uint32_t num_devices_available);
51 bool RunConfiguration(const std::string& config_file, int num_devices,
52                       DeviceType device_type);
53 bool RunAllConfigurations(int32_t num_devices, DeviceType device_type);
55 bool ReadFrame(ExecutionObject&     eo,
56                int                  frame_idx,
57                const Configuration& configuration,
58                std::istream&        input_file);
60 bool WriteFrame(const ExecutionObject &eo,
61                 std::ostream& output_file);
63 static void ProcessArgs(int argc, char *argv[],
64                         std::string& config_file,
65                         int& num_devices,
66                         DeviceType& device_type);
68 static void DisplayHelp();
70 static double ms_diff(struct timespec &t0, struct timespec &t1)
71 { return (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; }
74 int main(int argc, char *argv[])
75 {
76     // Catch ctrl-c to ensure a clean exit
77     signal(SIGABRT, exit);
78     signal(SIGTERM, exit);
80     // If there are no devices capable of offloading TINN on the SoC, exit
81     uint32_t num_dla = Executor::GetNumDevices(DeviceType::DLA);
82     uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
83     if (num_dla == 0 && num_dsp == 0)
84     {
85         std::cout << "TI DL not supported on this SoC." << std::endl;
86         return EXIT_SUCCESS;
87     }
88     std::cout << "API Version: " << Executor::GetAPIVersion() << std::endl;
90     // Process arguments
91     std::string config_file;
92     int         num_devices = 1;
93     DeviceType  device_type = DeviceType::DLA;
94     ProcessArgs(argc, argv, config_file, num_devices, device_type);
96     bool status = true;
97     if (!config_file.empty())
98         status = RunConfiguration(config_file, num_devices, device_type);
99     else
100     {
101         if (num_dla > 0)
102         {
103             //TODO: Use memory availability to determine # devices
104             // Run on 2 devices because there is not enough CMEM available by
105             // default
106             if (num_dla = 4) num_dla = 2;
107             status = RunAllConfigurations(num_dla, DeviceType::DLA);
108             status &= RunMultipleExecutors(
109                      "testvecs/config/infer/tidl_config_j11_v2.txt",
110                      "testvecs/config/infer/tidl_config_j11_cifar.txt",
111                      num_dla);
112         }
114         if (num_dsp > 0)
115         {
116             status &= RunAllConfigurations(num_dsp, DeviceType::DSP);
117         }
118     }
120     if (!status)
121     {
122         std::cout << "tidl FAILED" << std::endl;
123         return EXIT_FAILURE;
124     }
126     std::cout << "tidl PASSED" << std::endl;
127     return EXIT_SUCCESS;
130 bool RunConfiguration(const std::string& config_file, int num_devices,
131                       DeviceType device_type)
133     DeviceIds ids;
134     for (int i = 0; i < num_devices; i++)
135         ids.insert(static_cast<DeviceId>(i));
137     // Read the TI DL configuration file
138     Configuration configuration;
139     bool status = configuration.ReadFromFile(config_file);
140     if (!status)
141     {
142         std::cerr << "Error in configuration file: " << config_file
143                   << std::endl;
144         return false;
145     }
147     // Open input and output files
148     std::ifstream input_data_file(configuration.inData, std::ios::binary);
149     std::ofstream output_data_file(configuration.outData, std::ios::binary);
150     assert (input_data_file.good());
151     assert (output_data_file.good());
153     // Determine input frame size from configuration
154     size_t frame_sz = configuration.inWidth * configuration.inHeight *
155                       configuration.inNumChannels;
157     try
158     {
159         // Create a executor with the approriate core type, number of cores
160         // and configuration specified
161         Executor executor(device_type, ids, configuration);
163         // Query Executor for set of ExecutionObjects created
164         const ExecutionObjects& execution_objects =
165                                                 executor.GetExecutionObjects();
166         int num_eos = execution_objects.size();
168         // Allocate input and output buffers for each execution object
169         std::vector<void *> buffers;
170         for (auto &eo : execution_objects)
171         {
172             ArgInfo in  = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
173             ArgInfo out = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
174             eo->SetInputOutputBuffer(in, out);
176             buffers.push_back(in.ptr());
177             buffers.push_back(out.ptr());
178         }
180         #define MAX_NUM_EOS  4
181         struct timespec t0[MAX_NUM_EOS], t1;
183         // Process frames with available execution objects in a pipelined manner
184         // additional num_eos iterations to flush the pipeline (epilogue)
185         for (int frame_idx = 0;
186              frame_idx < configuration.numFrames + num_eos; frame_idx++)
187         {
188             ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
190             // Wait for previous frame on the same eo to finish processing
191             if (eo->ProcessFrameWait())
192             {
193                 clock_gettime(CLOCK_MONOTONIC, &t1);
194                 double elapsed_host =
195                                 ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
196                 double elapsed_device = eo->GetProcessTimeInMilliSeconds();
197                 double overhead = 100 - (elapsed_device/elapsed_host*100);
199                 std::cout << "frame[" << eo->GetFrameIndex() << "]: "
200                           << "Time on device: "
201                           << std::setw(6) << std::setprecision(4)
202                           << elapsed_device << "ms, "
203                           << "host: "
204                           << std::setw(6) << std::setprecision(4)
205                           << elapsed_host << "ms ";
206                 std::cout << "API overhead: "
207                           << std::setw(6) << std::setprecision(3)
208                           << overhead << " %" << std::endl;
210                 WriteFrame(*eo, output_data_file);
211             }
213             // Read a frame and start processing it with current eo
214             if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
215             {
216                 clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
217                 eo->ProcessFrameStartAsync();
218             }
219         }
221         for (auto b : buffers)
222             __free_ddr(b);
224     }
225     catch (tinn::Exception &e)
226     {
227         std::cerr << e.what() << std::endl;
228         status = false;
229     }
232     input_data_file.close();
233     output_data_file.close();
235     return status;
238 namespace tinn {
239 extern bool CompareFiles (const std::string &F1, const std::string &F2);
240 extern bool CompareFrames(const std::string &F1, const std::string &F2,
241                           int numFrames, int width, int height);
244 bool RunAllConfigurations(int32_t num_devices, DeviceType device_type)
246     std::vector<std::string> configurations;
248     if (device_type == DeviceType::DLA)
249         configurations = {"dense_1x1",  "j11_bn", "j11_cifar",
250                           "j11_controlLayers", "j11_prelu", "j11_v2",
251                           "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
252     else
253         configurations = {"j11_bn",
254                           "j11_controlLayers", "j11_v2",
255                           "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
257     int errors = 0;
258     for (auto config : configurations)
259     {
260         std::string config_file = "testvecs/config/infer/tidl_config_"
261                                   + config + ".txt";
262         std::cout << "Running " << config << " on " << num_devices
263                   << " devices, type "
264                   << ((device_type == DeviceType::DLA) ? "EVE" : "DSP")
265                   << std::endl;
267         Configuration configuration;
268         bool status = configuration.ReadFromFile(config_file);
269         if (!status) { errors++; continue; }
271         status = RunConfiguration(config_file, num_devices, device_type);
273         if (!status) { errors++; continue; }
275         // Check output against reference output
276         std::string reference_output = "testvecs/reference/"
277                                        + config + "_ref.bin";
279         // Reference for jseg21_tiscapes only has one frame
280         if (config.compare("jseg21_tiscapes") == 0)
281             status = CompareFrames(configuration.outData, reference_output,
282                                    1, 1024, 512);
283         else
284             status = CompareFiles(configuration.outData, reference_output);
286         if (status) std::cout << config << " : PASSED" << std::endl;
287         else        std::cout << config << " : FAILED" << std::endl;
289         if (!status) errors++;
290     }
292     if (errors > 0) return false;
294     return true;
299 bool ReadFrame(ExecutionObject &eo, int frame_idx,
300                const Configuration& configuration,
301                std::istream& input_file)
303     if (frame_idx >= configuration.numFrames)
304         return false;
306     char*  frame_buffer = eo.GetInputBufferPtr();
307     assert (frame_buffer != nullptr);
309     input_file.read(eo.GetInputBufferPtr(),
310                     eo.GetInputBufferSizeInBytes());
312     if (input_file.eof())
313         return false;
315     assert (input_file.good());
317     // Set the frame index  being processed by the EO. This is used to
318     // sort the frames before they are output
319     eo.SetFrameIndex(frame_idx);
321     if (input_file.good())
322         return true;
324     return false;
327 bool WriteFrame(const ExecutionObject &eo, std::ostream& output_file)
329     output_file.write(
330             eo.GetOutputBufferPtr(), eo.GetOutputBufferSizeInBytes());
331     assert(output_file.good() == true);
333     if (output_file.good())
334         return true;
336     return false;
340 void ProcessArgs(int argc, char *argv[], std::string& config_file,
341                  int& num_devices, DeviceType& device_type)
343     const struct option long_options[] =
344     {
345         {"config_file", required_argument, 0, 'c'},
346         {"num_devices", required_argument, 0, 'n'},
347         {"device_type", required_argument, 0, 't'},
348         {"help",        no_argument,       0, 'h'},
349         {"verbose",     no_argument,       0, 'v'},
350         {0, 0, 0, 0}
351     };
353     int option_index = 0;
355     while (true)
356     {
357         int c = getopt_long(argc, argv, "c:n:t:hv", long_options, &option_index);
359         if (c == -1)
360             break;
362         switch (c)
363         {
364             case 'c': config_file = optarg;
365                       break;
367             case 'n': num_devices = atoi(optarg);
368                       assert (num_devices > 0 && num_devices <= 4);
369                       break;
371             case 't': if (*optarg == 'e')
372                           device_type = DeviceType::DLA;
373                       else if (*optarg == 'd')
374                           device_type = DeviceType::DSP;
375                       else
376                       {
377                           std::cerr << "Invalid argument to -t, only e or d"
378                                        " allowed" << std::endl;
379                           exit(EXIT_FAILURE);
380                       }
381                       break;
383             case 'v': __TI_show_debug_ = true;
384                       break;
386             case 'h': DisplayHelp();
387                       exit(EXIT_SUCCESS);
388                       break;
390             case '?': // Error in getopt_long
391                       exit(EXIT_FAILURE);
392                       break;
394             default:
395                       std::cerr << "Unsupported option: " << c << std::endl;
396                       break;
397         }
398     }
401 void DisplayHelp()
403     std::cout << "Usage: test_tinn\n"
404                  "  Will run all available networks if invoked without"
405                  " any arguments.\n  Use -c to run a single network.\n"
406                  "Optional arguments:\n"
407                  " -c                   Path to the configuration file\n"
408                  " -n <number of cores> Number of cores to use (1 - 4)\n"
409                  " -t <d|e>             Type of core. d -> DSP, e -> DLA\n"
410                  " -v                   Verbose output during execution\n"
411                  " -h                   Help\n";