Added an example to illustrate 1 EO per frame
[tidl/tidl-api.git] / examples / one_eo_per_frame / 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  *****************************************************************************/
29 //
30 // This example illustrates using a single EO to process a frame. To increase
31 // throughput, multiple EOs are used.
32 // For details, refer http://downloads.ti.com/mctools/esd/docs/tidl-api/
33 //
34 #include <signal.h>
35 #include <iostream>
36 #include <iomanip>
37 #include <fstream>
38 #include <cassert>
39 #include <string>
40 #include <cstring>
42 #include "executor.h"
43 #include "execution_object.h"
44 #include "configuration.h"
45 #include <boost/format.hpp>
47 using namespace tidl;
48 using std::string;
49 using std::unique_ptr;
50 using std::vector;
51 using boost::format;
53 bool Run(const string& config_file, int num_eve,int num_dsp,
54          const char* ref_output);
56 bool ReadFrame(ExecutionObject*     eo,
57                int                  frame_idx,
58                const Configuration& configuration,
59                std::istream&        input_file);
60 bool CheckFrame(const ExecutionObject *eo, const char *ref_output);
62 const char* ReadReferenceOutput(const string& name);
63 void        ReportTime(const ExecutionObject* eo);
65 Executor* CreateExecutor(DeviceType dt, int num, const Configuration& c);
66 void      CollectEOs(const Executor *e, vector<ExecutionObject *>& EOs);
68 void AllocateMemory(const vector<ExecutionObject *>& EOs);
69 void FreeMemory    (const vector<ExecutionObject *>& EOs);
72 int main(int argc, char *argv[])
73 {
74     // Catch ctrl-c to ensure a clean exit
75     signal(SIGABRT, exit);
76     signal(SIGTERM, exit);
78     // If there are no devices capable of offloading TIDL on the SoC, exit
79     uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
80     uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
81     if (num_eve == 0 && num_dsp == 0)
82     {
83         std::cout << "TI DL not supported on this SoC." << std::endl;
84         return EXIT_SUCCESS;
85     }
87     string config_file ="../test/testvecs/config/infer/tidl_config_j11_v2.txt";
88     string ref_file    ="../test/testvecs/reference/j11_v2_ref.bin";
90     unique_ptr<const char> reference_output(ReadReferenceOutput(ref_file));
92     bool status = Run(config_file, num_eve, num_dsp, reference_output.get());
94     if (!status)
95     {
96         std::cout << "FAILED" << std::endl;
97         return EXIT_FAILURE;
98     }
100     std::cout << "PASSED" << std::endl;
101     return EXIT_SUCCESS;
104 bool Run(const string& config_file, int num_eve, int num_dsp,
105          const char* ref_output)
107     Configuration c;
108     if (!c.ReadFromFile(config_file))
109         return false;
111     // heap sizes determined using Configuration::showHeapStats
112     c.PARAM_HEAP_SIZE   = (3 << 20); // 3MB
113     c.NETWORK_HEAP_SIZE = (20 << 20); // 20MB
115     c.numFrames = 16;
117     // Open input file for reading
118     std::ifstream input_data_file(c.inData, std::ios::binary);
119     assert (input_data_file.good());
121     bool status = true;
122     try
123     {
124         // Create Executors - use all the DSP and EVE cores available
125         unique_ptr<Executor> e_dsp(CreateExecutor(DeviceType::DSP, num_dsp, c));
126         unique_ptr<Executor> e_eve(CreateExecutor(DeviceType::EVE, num_eve, c));
128         // Accumulate all the EOs from across the Executors
129         vector<ExecutionObject *> EOs;
130         CollectEOs(e_eve.get(), EOs);
131         CollectEOs(e_dsp.get(), EOs);
133         AllocateMemory(EOs);
135         // Process frames with EOs in a pipelined manner
136         // additional num_eos iterations to flush the pipeline (epilogue)
137         int num_eos = EOs.size();
138         for (int frame_idx = 0; frame_idx < c.numFrames + num_eos; frame_idx++)
139         {
140             ExecutionObject* eo = EOs[frame_idx % num_eos];
142             // Wait for previous frame on the same eo to finish processing
143             if (eo->ProcessFrameWait())
144             {
145                 ReportTime(eo);
146                 if (frame_idx < num_eos && !CheckFrame(eo, ref_output))
147                     status = false;
148             }
150             // Read a frame and start processing it with current eo
151             if (ReadFrame(eo, frame_idx, c, input_data_file))
152                 eo->ProcessFrameStartAsync();
153         }
155         FreeMemory(EOs);
157     }
158     catch (tidl::Exception &e)
159     {
160         std::cerr << e.what() << std::endl;
161         status = false;
162     }
164     input_data_file.close();
166     return status;
169 // Create an Executor with the specified type and number of EOs
170 Executor* CreateExecutor(DeviceType dt, int num, const Configuration& c)
172     if (num == 0) return nullptr;
174     DeviceIds ids;
175     for (int i = 0; i < num; i++)
176         ids.insert(static_cast<DeviceId>(i));
178     return new Executor(dt, ids, c);
181 // Accumulate EOs from an Executor into a vector of EOs
182 void CollectEOs(const Executor *e, vector<ExecutionObject *>& EOs)
184     if (!e) return;
186     for (unsigned int i = 0; i < e->GetNumExecutionObjects(); i++)
187         EOs.push_back((*e)[i]);
190 // Allocate input and output memory for each EO
191 void AllocateMemory(const vector<ExecutionObject *>& EOs)
193     // Allocate input and output buffers for each execution object
194     for (auto eo : EOs)
195     {
196         size_t in_size  = eo->GetInputBufferSizeInBytes();
197         size_t out_size = eo->GetOutputBufferSizeInBytes();
198         ArgInfo in  = { ArgInfo(malloc(in_size),  in_size)};
199         ArgInfo out = { ArgInfo(malloc(out_size), out_size)};
200         eo->SetInputOutputBuffer(in, out);
201     }
204 // Free the input and output memory associated with each EO
205 void FreeMemory(const vector<ExecutionObject *>& EOs)
207     for (auto eo : EOs)
208     {
209         free(eo->GetInputBufferPtr());
210         free(eo->GetOutputBufferPtr());
211     }
215 // Read a frame. Since the sample input has a single frame, read the same
216 // frame over and over.
217 bool ReadFrame(ExecutionObject *eo, int frame_idx,
218                const Configuration& configuration,
219                std::istream& input_file)
221     char*  frame_buffer = eo->GetInputBufferPtr();
222     assert (frame_buffer != nullptr);
224     input_file.seekg(0, input_file.beg);
226     input_file.read(eo->GetInputBufferPtr(),
227                     eo->GetInputBufferSizeInBytes());
229     if (input_file.eof())
230         return false;
232     assert (input_file.good());
234     // Note: Frame index is used by the EO for debug messages only
235     eo->SetFrameIndex(frame_idx);
237     if (input_file.good())
238         return true;
240     return false;
243 // Compare output against reference output
244 bool CheckFrame(const ExecutionObject *eo, const char *ref_output)
246     if (std::memcmp(static_cast<const void*>(ref_output),
247                static_cast<const void*>(eo->GetOutputBufferPtr()),
248                eo->GetOutputBufferSizeInBytes()) == 0)
249         return true;
251     return false;
255 void ReportTime(const ExecutionObject* eo)
257     double elapsed_host   = eo->GetHostProcessTimeInMilliSeconds();
258     double elapsed_device = eo->GetProcessTimeInMilliSeconds();
259     double overhead = 100 - (elapsed_device/elapsed_host*100);
261     std::cout << format("frame[%3d]: Time on %s: %4.2f ms, host: %4.2f ms"
262                         " API overhead: %2.2f %%\n")
263                         % eo->GetFrameIndex() % eo->GetDeviceName()
264                         % elapsed_device % elapsed_host % overhead;
267 namespace tidl {
268 std::size_t GetBinaryFileSize (const std::string &F);
269 bool        ReadBinary        (const std::string &F, char* buffer, int size);
272 // Read a file into a buffer.
273 const char* ReadReferenceOutput(const string& name)
275     size_t size = GetBinaryFileSize(name);
277     if (size == 0)
278         return nullptr;
280     char* buffer = new char[size];
282     if (!buffer)
283         return nullptr;
285     if (!ReadBinary(name, buffer, size))
286     {
287         delete [] buffer;
288         return nullptr;
289     }
291     return buffer;