1/*
2 Copyright (c) 2005-2019 Intel Corporation
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 "harness.h"
18#include "harness_graph.h"
19#include "tbb/flow_graph.h"
20#include "tbb/task_scheduler_init.h"
21
22#if defined(_MSC_VER) && _MSC_VER < 1600
23 #pragma warning (disable : 4503) //disabling the "decorated name length exceeded" warning for VS2008 and earlier
24#endif
25
26//
27// Tests
28//
29
30const int Count = 300;
31const int MaxPorts = 10;
32const int MaxNSources = 5; // max # of source_nodes to register for each split_node input in parallel test
33
34std::vector<bool> flags; // for checking output
35
36template<typename T>
37class name_of {
38public:
39 static const char* name() { return "Unknown"; }
40};
41template<>
42class name_of<int> {
43public:
44 static const char* name() { return "int"; }
45};
46template<>
47class name_of<float> {
48public:
49 static const char* name() { return "float"; }
50};
51template<>
52class name_of<double> {
53public:
54 static const char* name() { return "double"; }
55};
56template<>
57class name_of<long> {
58public:
59 static const char* name() { return "long"; }
60};
61template<>
62class name_of<short> {
63public:
64 static const char* name() { return "short"; }
65};
66
67// T must be arithmetic, and shouldn't wrap around for reasonable sizes of Count (which is now 150, and maxPorts is 10,
68// so the max number generated right now is 1500 or so.) Source will generate a series of TT with value
69// (init_val + (i-1)*addend) * my_mult, where i is the i-th invocation of the body. We are attaching addend
70// source nodes to a join_port, and each will generate part of the numerical series the port is expecting
71// to receive. If there is only one source node, the series order will be maintained; if more than one,
72// this is not guaranteed.
73
74template<int N>
75struct tuple_helper {
76 template<typename TupleType>
77 static void set_element( TupleType &t, int i) {
78 tbb::flow::get<N-1>(t) = (typename tbb::flow::tuple_element<N-1,TupleType>::type)(i * (N+1));
79 tuple_helper<N-1>::set_element(t, i);
80 }
81};
82
83template<>
84struct tuple_helper<1> {
85 template<typename TupleType>
86 static void set_element(TupleType &t, int i) {
87 tbb::flow::get<0>(t) = (typename tbb::flow::tuple_element<0,TupleType>::type)(i * 2);
88 }
89};
90
91// if we start N source_bodys they will all have the addend N, and my_count should be initialized to 0 .. N-1.
92// the output tuples should have all the sequence, but the order will in general vary.
93template<typename TupleType>
94class source_body {
95 typedef TupleType TT;
96 static const int N = tbb::flow::tuple_size<TT>::value;
97 int my_count;
98 int addend;
99public:
100 source_body(int init_val, int addto) : my_count(init_val), addend(addto) { }
101 void operator=( const source_body& other) { my_count = other.my_count; addend = other.addend; }
102 bool operator()( TT &v) {
103 if(my_count >= Count) return false;
104 tuple_helper<N>::set_element(v, my_count);
105 my_count += addend;
106 return true;
107 }
108};
109
110// allocator for split_node.
111
112template<int N, typename SType>
113class makeSplit {
114public:
115 static SType *create(tbb::flow::graph& g) {
116 SType *temp = new SType(g);
117 return temp;
118 }
119 static void destroy(SType *p) { delete p; }
120};
121
122// holder for sink_node pointers for eventual deletion
123
124static void* all_sink_nodes[MaxPorts];
125
126
127template<int ELEM, typename SType>
128class sink_node_helper {
129public:
130 typedef typename SType::input_type TT;
131 typedef typename tbb::flow::tuple_element<ELEM-1,TT>::type IT;
132 typedef typename tbb::flow::queue_node<IT> my_sink_node_type;
133 static void print_parallel_remark() {
134 sink_node_helper<ELEM-1,SType>::print_parallel_remark();
135 REMARK(", %s", name_of<IT>::name());
136 }
137 static void print_serial_remark() {
138 sink_node_helper<ELEM-1,SType>::print_serial_remark();
139 REMARK(", %s", name_of<IT>::name());
140 }
141 static void add_sink_nodes(SType &my_split, tbb::flow::graph &g) {
142 my_sink_node_type *new_node = new my_sink_node_type(g);
143 tbb::flow::make_edge( tbb::flow::output_port<ELEM-1>(my_split) , *new_node);
144 all_sink_nodes[ELEM-1] = (void *)new_node;
145 sink_node_helper<ELEM-1, SType>::add_sink_nodes(my_split, g);
146 }
147
148 static void check_sink_values() {
149 my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[ELEM-1]);
150 for(int i = 0; i < Count; ++i) {
151 IT v;
152 ASSERT(dp->try_get(v), NULL);
153 flags[((int)v) / (ELEM+1)] = true;
154 }
155 for(int i = 0; i < Count; ++i) {
156 ASSERT(flags[i], NULL);
157 flags[i] = false; // reset for next test
158 }
159 sink_node_helper<ELEM-1,SType>::check_sink_values();
160 }
161 static void remove_sink_nodes(SType& my_split) {
162 my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[ELEM-1]);
163 tbb::flow::remove_edge( tbb::flow::output_port<ELEM-1>(my_split) , *dp);
164 delete dp;
165 sink_node_helper<ELEM-1, SType>::remove_sink_nodes(my_split);
166 }
167};
168
169template<typename SType>
170class sink_node_helper<1, SType> {
171 typedef typename SType::input_type TT;
172 typedef typename tbb::flow::tuple_element<0,TT>::type IT;
173 typedef typename tbb::flow::queue_node<IT> my_sink_node_type;
174public:
175 static void print_parallel_remark() {
176 REMARK("Parallel test of split_node< %s", name_of<IT>::name());
177 }
178 static void print_serial_remark() {
179 REMARK("Serial test of split_node< %s", name_of<IT>::name());
180 }
181 static void add_sink_nodes(SType &my_split, tbb::flow::graph &g) {
182 my_sink_node_type *new_node = new my_sink_node_type(g);
183 tbb::flow::make_edge( tbb::flow::output_port<0>(my_split) , *new_node);
184 all_sink_nodes[0] = (void *)new_node;
185 }
186 static void check_sink_values() {
187 my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[0]);
188 for(int i = 0; i < Count; ++i) {
189 IT v;
190 ASSERT(dp->try_get(v), NULL);
191 flags[((int)v) / 2] = true;
192 }
193 for(int i = 0; i < Count; ++i) {
194 ASSERT(flags[i], NULL);
195 flags[i] = false; // reset for next test
196 }
197 }
198 static void remove_sink_nodes(SType& my_split) {
199 my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[0]);
200 tbb::flow::remove_edge( tbb::flow::output_port<0>(my_split) , *dp);
201 delete dp;
202 }
203};
204
205// parallel_test: create source_nodes that feed tuples into the split node
206// and queue_nodes that receive the output.
207template<typename SType>
208class parallel_test {
209public:
210 typedef typename SType::input_type TType;
211 typedef tbb::flow::source_node<TType> source_type;
212 static const int N = tbb::flow::tuple_size<TType>::value;
213 static void test() {
214 source_type* all_source_nodes[MaxNSources];
215 sink_node_helper<N,SType>::print_parallel_remark();
216 REMARK(" >\n");
217 for(int i=0; i < MaxPorts; ++i) {
218 all_sink_nodes[i] = NULL;
219 }
220 // try test for # sources 1 .. MaxNSources
221 for(int nInputs = 1; nInputs <= MaxNSources; ++nInputs) {
222 tbb::flow::graph g;
223 SType* my_split = makeSplit<N,SType>::create(g);
224
225 // add sinks first so when sources start spitting out values they are there to catch them
226 sink_node_helper<N, SType>::add_sink_nodes((*my_split), g);
227
228 // now create nInputs source_nodes, each spitting out i, i+nInputs, i+2*nInputs ...
229 // each element of the tuple is i*(n+1), where n is the tuple element index (1-N)
230 for(int i = 0; i < nInputs; ++i) {
231 // create source node
232 source_type *s = new source_type(g, source_body<TType>(i, nInputs) );
233 tbb::flow::make_edge(*s, *my_split);
234 all_source_nodes[i] = s;
235 }
236
237 g.wait_for_all();
238
239 // check that we got Count values in each output queue, and all the index values
240 // are there.
241 sink_node_helper<N, SType>::check_sink_values();
242
243 sink_node_helper<N, SType>::remove_sink_nodes(*my_split);
244 for(int i = 0; i < nInputs; ++i) {
245 delete all_source_nodes[i];
246 }
247 makeSplit<N,SType>::destroy(my_split);
248 }
249 }
250};
251
252//
253// Single predecessor, single accepting successor at each port
254
255template<typename SType>
256void test_one_serial( SType &my_split, tbb::flow::graph &g) {
257 typedef typename SType::input_type TType;
258 static const int TUPLE_SIZE = tbb::flow::tuple_size<TType>::value;
259 sink_node_helper<TUPLE_SIZE, SType>::add_sink_nodes(my_split,g);
260 typedef TType q3_input_type;
261 tbb::flow::queue_node< q3_input_type > q3(g);
262
263 tbb::flow::make_edge( q3, my_split );
264
265 // fill the queue with its value one-at-a-time
266 flags.clear();
267 for (int i = 0; i < Count; ++i ) {
268 TType v;
269 tuple_helper<TUPLE_SIZE>::set_element(v, i);
270 ASSERT(my_split.try_put(v), NULL);
271 flags.push_back(false);
272 }
273
274 g.wait_for_all();
275
276 sink_node_helper<TUPLE_SIZE,SType>::check_sink_values();
277
278 sink_node_helper<TUPLE_SIZE, SType>::remove_sink_nodes(my_split);
279
280}
281
282template<typename SType>
283class serial_test {
284 typedef typename SType::input_type TType;
285 static const int TUPLE_SIZE = tbb::flow::tuple_size<TType>::value;
286 static const int ELEMS = 3;
287public:
288static void test() {
289 tbb::flow::graph g;
290 flags.reserve(Count);
291 SType* my_split = makeSplit<TUPLE_SIZE,SType>::create(g);
292 sink_node_helper<TUPLE_SIZE, SType>::print_serial_remark(); REMARK(" >\n");
293
294 test_output_ports_return_ref(*my_split);
295
296 test_one_serial<SType>(*my_split, g);
297 // build the vector with copy construction from the used split node.
298 std::vector<SType>split_vector(ELEMS, *my_split);
299 // destroy the tired old split_node in case we're accidentally reusing pieces of it.
300 makeSplit<TUPLE_SIZE,SType>::destroy(my_split);
301
302
303 for(int e = 0; e < ELEMS; ++e) { // exercise each of the vector elements
304 test_one_serial<SType>(split_vector[e], g);
305 }
306}
307
308}; // serial_test
309
310template<
311 template<typename> class TestType, // serial_test or parallel_test
312 typename TupleType > // type of the input of the split
313struct generate_test {
314 typedef tbb::flow::split_node<TupleType> split_node_type;
315 static void do_test() {
316 TestType<split_node_type>::test();
317 }
318}; // generate_test
319
320int TestMain() {
321#if __TBB_USE_TBB_TUPLE
322 REMARK(" Using TBB tuple\n");
323#else
324 REMARK(" Using platform tuple\n");
325#endif
326 for (int p = 0; p < 2; ++p) {
327 generate_test<serial_test, tbb::flow::tuple<float, double> >::do_test();
328#if MAX_TUPLE_TEST_SIZE >= 4
329 generate_test<serial_test, tbb::flow::tuple<float, double, int, long> >::do_test();
330#endif
331#if MAX_TUPLE_TEST_SIZE >= 6
332 generate_test<serial_test, tbb::flow::tuple<double, double, int, long, int, short> >::do_test();
333#endif
334#if MAX_TUPLE_TEST_SIZE >= 8
335 generate_test<serial_test, tbb::flow::tuple<float, double, double, double, float, int, float, long> >::do_test();
336#endif
337#if MAX_TUPLE_TEST_SIZE >= 10
338 generate_test<serial_test, tbb::flow::tuple<float, double, int, double, double, float, long, int, float, long> >::do_test();
339#endif
340 generate_test<parallel_test, tbb::flow::tuple<float, double> >::do_test();
341#if MAX_TUPLE_TEST_SIZE >= 3
342 generate_test<parallel_test, tbb::flow::tuple<float, int, long> >::do_test();
343#endif
344#if MAX_TUPLE_TEST_SIZE >= 5
345 generate_test<parallel_test, tbb::flow::tuple<double, double, int, int, short> >::do_test();
346#endif
347#if MAX_TUPLE_TEST_SIZE >= 7
348 generate_test<parallel_test, tbb::flow::tuple<float, int, double, float, long, float, long> >::do_test();
349#endif
350#if MAX_TUPLE_TEST_SIZE >= 9
351 generate_test<parallel_test, tbb::flow::tuple<float, double, int, double, double, long, int, float, long> >::do_test();
352#endif
353 }
354 return Harness::Done;
355}
356