3b9f6b1987f760cfb6cff10b18e9d0dc11df796d
1 /******************************************************************************
2 * Copyright (c) 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 //! @file multiple_executors.cpp
30 //! Illustrates how to setup multiple Executor instances using
31 //! non-overlapping sets of device ids and running the Executor instances
32 //! in parallel - each in its own thread
34 #include <signal.h>
35 #include <getopt.h>
36 #include <iostream>
37 #include <fstream>
38 #include <cassert>
39 #include <string>
40 #include <functional>
41 #include <algorithm>
42 #include <pthread.h>
44 #include "executor.h"
45 #include "execution_object.h"
46 #include "configuration.h"
48 using namespace tidl;
50 extern bool ReadFrame(ExecutionObject& eo,
51 int frame_idx,
52 const Configuration& configuration,
53 std::istream& input_file);
55 extern bool WriteFrame(const ExecutionObject &eo,
56 std::ostream& output_file);
58 void* run_network(void *data);
60 struct ThreadArg
61 {
62 ThreadArg(const DeviceIds& ids, const std::string& s):
63 ids(ids), config_file(s) {}
65 DeviceIds ids;
66 std::string config_file;
67 };
69 bool thread_status[2];
71 bool RunMultipleExecutors(const std::string& config_file_1,
72 const std::string& config_file_2,
73 uint32_t num_devices_available)
74 {
75 // If there is only 1 device available, skip
76 if (num_devices_available == 1)
77 return true;
79 DeviceIds ids1, ids2;
81 if (num_devices_available == 4)
82 {
83 ids1 = {DeviceId::ID2, DeviceId::ID3};
84 ids2 = {DeviceId::ID0, DeviceId::ID1};
85 }
86 else
87 {
88 ids1 = {DeviceId::ID0};
89 ids2 = {DeviceId::ID1};
90 }
92 // Set up devices and config files for each thread
93 ThreadArg arg1(ids2, config_file_1);
94 ThreadArg arg2(ids1, config_file_2);
96 // Run network 1 in a thread
97 std::cout << std::endl << "Multiple Executor..." << std::endl;
98 std::cout << "Running network "
99 << arg1.config_file.substr(arg1.config_file.find("tidl"))
100 << " on EVEs: ";
101 for (DeviceId id : arg1.ids)
102 std::cout << static_cast<int>(id) << " ";
103 std::cout << " in thread 0" << std::endl;
105 pthread_t network_thread_1;
106 pthread_create(&network_thread_1, 0, &run_network, &arg1);
108 // Run network 2 in a thread
109 std::cout << "Running network "
110 << arg2.config_file.substr(arg2.config_file.find("tidl"))
111 << " on EVEs: ";
112 for (DeviceId id : arg2.ids)
113 std::cout << static_cast<int>(id) << " ";
114 std::cout << " in thread 1" << std::endl;
116 pthread_t network_thread_2;
117 pthread_create(&network_thread_2, 0, &run_network, &arg2);
119 // Wait for both networks to complete
120 void *thread_return_val1;
121 void *thread_return_val2;
122 pthread_join(network_thread_1, &thread_return_val1);
123 pthread_join(network_thread_2, &thread_return_val2);
125 if (thread_return_val1 == 0 || thread_return_val2 == 0)
126 {
127 std::cout << "Multiple executors: FAILED" << std::endl;
128 return false;
129 }
131 std::cout << "Multiple executors: PASSED" << std::endl;
132 return true;
133 }
136 void* run_network(void *data)
137 {
138 const ThreadArg* arg = static_cast<const ThreadArg *>(data);
140 const DeviceIds& ids = arg->ids;
141 const std::string& config_file = arg->config_file;
143 // Read the TI DL configuration file
144 Configuration configuration;
145 bool status = configuration.ReadFromFile(config_file);
146 assert (status != false);
148 configuration.outData += std::to_string(pthread_self());
150 // Open input and output files
151 std::ifstream input_data_file(configuration.inData, std::ios::binary);
152 std::ofstream output_data_file(configuration.outData, std::ios::binary);
153 assert (input_data_file.good());
154 assert (output_data_file.good());
156 // Determine input frame size from configuration
157 size_t frame_sz = configuration.inWidth * configuration.inHeight *
158 configuration.inNumChannels;
160 try
161 {
162 // Create a executor with the approriate core type, number of cores
163 // and configuration specified
164 Executor executor(DeviceType::EVE, ids, configuration);
166 const ExecutionObjects& execution_objects =
167 executor.GetExecutionObjects();
168 int num_eos = execution_objects.size();
170 // Allocate input and output buffers for each execution object
171 std::vector<void *> buffers;
172 for (auto &eo : execution_objects)
173 {
174 ArgInfo in = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
175 ArgInfo out = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
176 eo->SetInputOutputBuffer(in, out);
178 buffers.push_back(in.ptr());
179 buffers.push_back(out.ptr());
180 }
182 // Process frames with available execution objects in a pipelined manner
183 // additional num_eos iterations to flush the pipeline (epilogue)
184 for (int frame_idx = 0;
185 frame_idx < configuration.numFrames + num_eos; frame_idx++)
186 {
187 ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
189 // Wait for previous frame on the same eo to finish processing
190 if (eo->ProcessFrameWait())
191 WriteFrame(*eo, output_data_file);
193 // Read a frame and start processing it with current eo
194 if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
195 eo->ProcessFrameStartAsync();
196 }
199 for (auto b : buffers)
200 __free_ddr(b);
201 }
202 catch (tidl::Exception &e)
203 {
204 std::cerr << e.what() << std::endl;
205 status = false;
206 }
208 input_data_file.close();
209 output_data_file.close();
211 // Return 1 for true, 0 for false. void * pattern follows example from:
212 // "Advanced programming in the Unix Environment"
213 if (!status) return ((void *)0);
215 return ((void *)1);
216 }