| 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 |  | 
|---|
| 30 | const int Count = 300; | 
|---|
| 31 | const int MaxPorts = 10; | 
|---|
| 32 | const int MaxNSources = 5; // max # of source_nodes to register for each split_node input in parallel test | 
|---|
| 33 |  | 
|---|
| 34 | std::vector<bool> flags;   // for checking output | 
|---|
| 35 |  | 
|---|
| 36 | template<typename T> | 
|---|
| 37 | class name_of { | 
|---|
| 38 | public: | 
|---|
| 39 | static const char* name() { return "Unknown"; } | 
|---|
| 40 | }; | 
|---|
| 41 | template<> | 
|---|
| 42 | class name_of<int> { | 
|---|
| 43 | public: | 
|---|
| 44 | static const char* name() { return "int"; } | 
|---|
| 45 | }; | 
|---|
| 46 | template<> | 
|---|
| 47 | class name_of<float> { | 
|---|
| 48 | public: | 
|---|
| 49 | static const char* name() { return "float"; } | 
|---|
| 50 | }; | 
|---|
| 51 | template<> | 
|---|
| 52 | class name_of<double> { | 
|---|
| 53 | public: | 
|---|
| 54 | static const char* name() { return "double"; } | 
|---|
| 55 | }; | 
|---|
| 56 | template<> | 
|---|
| 57 | class name_of<long> { | 
|---|
| 58 | public: | 
|---|
| 59 | static const char* name() { return "long"; } | 
|---|
| 60 | }; | 
|---|
| 61 | template<> | 
|---|
| 62 | class name_of<short> { | 
|---|
| 63 | public: | 
|---|
| 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 |  | 
|---|
| 74 | template<int N> | 
|---|
| 75 | struct 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 |  | 
|---|
| 83 | template<> | 
|---|
| 84 | struct 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. | 
|---|
| 93 | template<typename TupleType> | 
|---|
| 94 | class source_body { | 
|---|
| 95 | typedef TupleType TT; | 
|---|
| 96 | static const int N = tbb::flow::tuple_size<TT>::value; | 
|---|
| 97 | int my_count; | 
|---|
| 98 | int addend; | 
|---|
| 99 | public: | 
|---|
| 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 |  | 
|---|
| 112 | template<int N, typename SType> | 
|---|
| 113 | class makeSplit { | 
|---|
| 114 | public: | 
|---|
| 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 |  | 
|---|
| 124 | static void* all_sink_nodes[MaxPorts]; | 
|---|
| 125 |  | 
|---|
| 126 |  | 
|---|
| 127 | template<int ELEM, typename SType> | 
|---|
| 128 | class sink_node_helper { | 
|---|
| 129 | public: | 
|---|
| 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 () { | 
|---|
| 134 | sink_node_helper<ELEM-1,SType>::print_parallel_remark(); | 
|---|
| 135 | REMARK( ", %s", name_of<IT>::name()); | 
|---|
| 136 | } | 
|---|
| 137 | static void () { | 
|---|
| 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 |  | 
|---|
| 169 | template<typename SType> | 
|---|
| 170 | class 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; | 
|---|
| 174 | public: | 
|---|
| 175 | static void () { | 
|---|
| 176 | REMARK( "Parallel test of split_node< %s", name_of<IT>::name()); | 
|---|
| 177 | } | 
|---|
| 178 | static void () { | 
|---|
| 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. | 
|---|
| 207 | template<typename SType> | 
|---|
| 208 | class parallel_test { | 
|---|
| 209 | public: | 
|---|
| 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 |  | 
|---|
| 255 | template<typename SType> | 
|---|
| 256 | void 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 |  | 
|---|
| 282 | template<typename SType> | 
|---|
| 283 | class 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; | 
|---|
| 287 | public: | 
|---|
| 288 | static 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 |  | 
|---|
| 310 | template< | 
|---|
| 311 | template<typename> class TestType,  // serial_test or parallel_test | 
|---|
| 312 | typename TupleType >                               // type of the input of the split | 
|---|
| 313 | struct 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 |  | 
|---|
| 320 | int 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 |  | 
|---|