Remove input, output buffers from process kernel
[tidl/tidl-api.git] / examples / test / main.cpp
1 /******************************************************************************
2  * Copyright (c) 2017-2018  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 tidl;
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 TIDL on the SoC, exit
81     uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
82     uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
83     if (num_eve == 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::EVE;
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_eve > 0)
102         {
103             // Run on 2 devices because there is not enough CMEM available by
104             // default
105             if (num_eve = 4)
106             {
107                 std::cout
108                  << "Running on 2 EVE devices instead of the available 4 "
109                  << "due to insufficient OpenCL global memory. Refer the "
110                  << "TIDL API User's Guide, Frequently Asked Questions, "
111                  << "Section \"Insufficient OpenCL global memory\" for details "
112                  << "on increasing the amount of CMEM available for OpenCL."
113                  << std::endl;
114                 num_eve = 2;
115             }
116             status = RunAllConfigurations(num_eve, DeviceType::EVE);
117             status &= RunMultipleExecutors(
118                      "testvecs/config/infer/tidl_config_j11_v2.txt",
119                      "testvecs/config/infer/tidl_config_j11_cifar.txt",
120                      num_eve);
121         }
123         if (num_dsp > 0)
124         {
125             status &= RunAllConfigurations(num_dsp, DeviceType::DSP);
126         }
127     }
129     if (!status)
130     {
131         std::cout << "tidl FAILED" << std::endl;
132         return EXIT_FAILURE;
133     }
135     std::cout << "tidl PASSED" << std::endl;
136     return EXIT_SUCCESS;
139 bool RunConfiguration(const std::string& config_file, int num_devices,
140                       DeviceType device_type)
142     DeviceIds ids;
143     for (int i = 0; i < num_devices; i++)
144         ids.insert(static_cast<DeviceId>(i));
146     // Read the TI DL configuration file
147     Configuration configuration;
148     bool status = configuration.ReadFromFile(config_file);
149     if (!status)
150     {
151         std::cerr << "Error in configuration file: " << config_file
152                   << std::endl;
153         return false;
154     }
156     // Open input and output files
157     std::ifstream input_data_file(configuration.inData, std::ios::binary);
158     std::ofstream output_data_file(configuration.outData, std::ios::binary);
159     assert (input_data_file.good());
160     assert (output_data_file.good());
162     try
163     {
164         // Create a executor with the approriate core type, number of cores
165         // and configuration specified
166         Executor executor(device_type, ids, configuration);
168         // Query Executor for set of ExecutionObjects created
169         const ExecutionObjects& execution_objects =
170                                                 executor.GetExecutionObjects();
171         int num_eos = execution_objects.size();
173         // Allocate input and output buffers for each execution object
174         std::vector<void *> buffers;
175         for (auto &eo : execution_objects)
176         {
177             size_t in_size  = eo->GetInputBufferSizeInBytes();
178             size_t out_size = eo->GetOutputBufferSizeInBytes();
179             ArgInfo in  = { ArgInfo(malloc(in_size),  in_size)};
180             ArgInfo out = { ArgInfo(malloc(out_size), out_size)};
181             eo->SetInputOutputBuffer(in, out);
183             buffers.push_back(in.ptr());
184             buffers.push_back(out.ptr());
185         }
187         #define MAX_NUM_EOS  4
188         struct timespec t0[MAX_NUM_EOS], t1;
190         // Process frames with available execution objects in a pipelined manner
191         // additional num_eos iterations to flush the pipeline (epilogue)
192         for (int frame_idx = 0;
193              frame_idx < configuration.numFrames + num_eos; frame_idx++)
194         {
195             ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
197             // Wait for previous frame on the same eo to finish processing
198             if (eo->ProcessFrameWait())
199             {
200                 clock_gettime(CLOCK_MONOTONIC, &t1);
201                 double elapsed_host =
202                                 ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
203                 double elapsed_device = eo->GetProcessTimeInMilliSeconds();
204                 double overhead = 100 - (elapsed_device/elapsed_host*100);
206                 std::cout << "frame[" << eo->GetFrameIndex() << "]: "
207                           << "Time on device: "
208                           << std::setw(6) << std::setprecision(4)
209                           << elapsed_device << "ms, "
210                           << "host: "
211                           << std::setw(6) << std::setprecision(4)
212                           << elapsed_host << "ms ";
213                 std::cout << "API overhead: "
214                           << std::setw(6) << std::setprecision(3)
215                           << overhead << " %" << std::endl;
217                 WriteFrame(*eo, output_data_file);
218                 if (configuration.enableOutputTrace)
219                     eo->WriteLayerOutputsToFile();
220             }
222             // Read a frame and start processing it with current eo
223             if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
224             {
225                 clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
226                 eo->ProcessFrameStartAsync();
227             }
228         }
230         for (auto b : buffers)
231             free(b);
233     }
234     catch (tidl::Exception &e)
235     {
236         std::cerr << e.what() << std::endl;
237         status = false;
238     }
241     input_data_file.close();
242     output_data_file.close();
244     return status;
247 namespace tidl {
248 extern bool CompareFiles (const std::string &F1, const std::string &F2);
249 extern bool CompareFrames(const std::string &F1, const std::string &F2,
250                           int numFrames, int width, int height);
253 bool RunAllConfigurations(int32_t num_devices, DeviceType device_type)
255     std::vector<std::string> configurations;
257     if (device_type == DeviceType::EVE)
258         configurations = {"dense_1x1",  "j11_bn", "j11_cifar",
259                           "j11_controlLayers", "j11_prelu", "j11_v2",
260                           "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
261     else
262         configurations = {"j11_bn",
263                           "j11_controlLayers", "j11_v2",
264                           "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
266     int errors = 0;
267     for (auto config : configurations)
268     {
269         std::string config_file = "testvecs/config/infer/tidl_config_"
270                                   + config + ".txt";
271         std::cout << "Running " << config << " on " << num_devices
272                   << " devices, type "
273                   << ((device_type == DeviceType::EVE) ? "EVE" : "DSP")
274                   << std::endl;
276         Configuration configuration;
277         bool status = configuration.ReadFromFile(config_file);
278         if (!status) { errors++; continue; }
280         status = RunConfiguration(config_file, num_devices, device_type);
282         if (!status) { errors++; continue; }
284         // Check output against reference output
285         std::string reference_output = "testvecs/reference/"
286                                        + config + "_ref.bin";
288         // Reference for jseg21_tiscapes only has one frame
289         if (config.compare("jseg21_tiscapes") == 0)
290             status = CompareFrames(configuration.outData, reference_output,
291                                    1, 1024, 512);
292         else
293             status = CompareFiles(configuration.outData, reference_output);
295         if (status) std::cout << config << " : PASSED" << std::endl;
296         else        std::cout << config << " : FAILED" << std::endl;
298         if (!status) errors++;
299     }
301     if (errors > 0) return false;
303     return true;
308 bool ReadFrame(ExecutionObject &eo, int frame_idx,
309                const Configuration& configuration,
310                std::istream& input_file)
312     if (frame_idx >= configuration.numFrames)
313         return false;
315     char*  frame_buffer = eo.GetInputBufferPtr();
316     assert (frame_buffer != nullptr);
318     input_file.read(eo.GetInputBufferPtr(),
319                     eo.GetInputBufferSizeInBytes());
321     if (input_file.eof())
322         return false;
324     assert (input_file.good());
326     // Set the frame index  being processed by the EO. This is used to
327     // sort the frames before they are output
328     eo.SetFrameIndex(frame_idx);
330     if (input_file.good())
331         return true;
333     return false;
336 bool WriteFrame(const ExecutionObject &eo, std::ostream& output_file)
338     output_file.write(
339             eo.GetOutputBufferPtr(), eo.GetOutputBufferSizeInBytes());
340     assert(output_file.good() == true);
342     if (output_file.good())
343         return true;
345     return false;
349 void ProcessArgs(int argc, char *argv[], std::string& config_file,
350                  int& num_devices, DeviceType& device_type)
352     const struct option long_options[] =
353     {
354         {"config_file", required_argument, 0, 'c'},
355         {"num_devices", required_argument, 0, 'n'},
356         {"device_type", required_argument, 0, 't'},
357         {"help",        no_argument,       0, 'h'},
358         {"verbose",     no_argument,       0, 'v'},
359         {0, 0, 0, 0}
360     };
362     int option_index = 0;
364     while (true)
365     {
366         int c = getopt_long(argc, argv, "c:n:t:hv", long_options, &option_index);
368         if (c == -1)
369             break;
371         switch (c)
372         {
373             case 'c': config_file = optarg;
374                       break;
376             case 'n': num_devices = atoi(optarg);
377                       assert (num_devices > 0 && num_devices <= 4);
378                       break;
380             case 't': if (*optarg == 'e')
381                           device_type = DeviceType::EVE;
382                       else if (*optarg == 'd')
383                           device_type = DeviceType::DSP;
384                       else
385                       {
386                           std::cerr << "Invalid argument to -t, only e or d"
387                                        " allowed" << std::endl;
388                           exit(EXIT_FAILURE);
389                       }
390                       break;
392             case 'v': __TI_show_debug_ = true;
393                       break;
395             case 'h': DisplayHelp();
396                       exit(EXIT_SUCCESS);
397                       break;
399             case '?': // Error in getopt_long
400                       exit(EXIT_FAILURE);
401                       break;
403             default:
404                       std::cerr << "Unsupported option: " << c << std::endl;
405                       break;
406         }
407     }
410 void DisplayHelp()
412     std::cout << "Usage: test_tidl\n"
413                  "  Will run all available networks if invoked without"
414                  " any arguments.\n  Use -c to run a single network.\n"
415                  "Optional arguments:\n"
416                  " -c                   Path to the configuration file\n"
417                  " -n <number of cores> Number of cores to use (1 - 4)\n"
418                  " -t <d|e>             Type of core. d -> DSP, e -> EVE\n"
419                  " -v                   Verbose output during execution\n"
420                  " -h                   Help\n";