1 /* 2 Copyright (c) 2005-2021 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 "common/test.h" 18 19 #include "tbb/blocked_range.h" 20 21 #include <cmath> 22 #include <vector> 23 24 #include "common/utils_report.h" 25 26 namespace test_partitioner_utils { 27 28 struct RangeStatisticData { 29 // denotes the number of range objects 30 size_t m_rangeNum; 31 32 // store minimal and maximal range sizes (in terms of number of iterations) 33 size_t m_minRangeSize; 34 size_t m_maxRangeSize; 35 36 bool m_wasMinRangeSizeWritten; // shows whether relevant field was written or not 37 }; 38 39 using tbb::split; 40 using tbb::proportional_split; 41 using tbb::blocked_range; 42 43 // helper for calculating number of range objects created before balancing phase is started 44 // and for finding maximum and minimum number of iterations among all such ranges 45 // Note: class does not provide exclusive access to members 46 class RangeStatisticCollector { 47 public: RangeStatisticCollector(RangeStatisticData * statisticData)48 RangeStatisticCollector(RangeStatisticData *statisticData) : 49 m_statData(statisticData) 50 { 51 m_called = false; 52 if (m_statData) 53 m_statData->m_rangeNum = 1; 54 } 55 56 // constructor is called from non-proportional split constructor of derived Range RangeStatisticCollector(RangeStatisticCollector & sc,size_t rangeSize)57 RangeStatisticCollector(RangeStatisticCollector& sc, size_t rangeSize) { 58 if (!sc.m_called) { 59 // this is the first time non-proportional split constructor is called 60 // it means that work distribution phase has been completed and 61 // work balancing phase has been just started 62 sc.m_called = true; 63 64 if (sc.m_statData) { 65 size_t *minRangeSize = &sc.m_statData->m_minRangeSize; 66 if (*minRangeSize > rangeSize || !sc.m_statData->m_wasMinRangeSizeWritten) { // if minimum is not an actual minimum 67 *minRangeSize = rangeSize; 68 sc.m_statData->m_wasMinRangeSizeWritten = true; 69 } 70 size_t *maxRangeSize = &sc.m_statData->m_maxRangeSize; 71 if (*maxRangeSize < rangeSize) { // if maximum is not an actual maximum 72 *maxRangeSize = rangeSize; 73 } 74 } 75 } 76 *this = sc; 77 // constructor is used on work balancing phase only, so no need to increment 78 // number of range objects created 79 } 80 RangeStatisticCollector(RangeStatisticCollector & sc,proportional_split &)81 RangeStatisticCollector(RangeStatisticCollector& sc, proportional_split&) { 82 if (sc.m_statData) 83 sc.m_statData->m_rangeNum++; 84 *this = sc; 85 } 86 87 private: 88 RangeStatisticData *m_statData; 89 90 // turns to 'true' when non-proportional split constructor is called first time 91 bool m_called; 92 }; 93 94 // Base class for fake ranges used in various tests for parallel 95 // algorithms as well as for partitioner 96 template <typename DerivedRange, typename T> 97 class RangeBase: public RangeStatisticCollector { 98 protected: 99 size_t my_begin, my_end; 100 bool m_provide_feedback; 101 bool m_ensure_non_empty_size; 102 public: RangeBase(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)103 RangeBase(size_t _begin, size_t _end, RangeStatisticData *statData, 104 bool provide_feedback, bool ensure_non_empty_size) 105 : RangeStatisticCollector(statData) 106 , my_begin(_begin), my_end(_end) 107 , m_provide_feedback(provide_feedback) 108 , m_ensure_non_empty_size(ensure_non_empty_size) 109 { } RangeBase(RangeBase & r,tbb::split)110 RangeBase(RangeBase& r, tbb::split) : RangeStatisticCollector(r, r.size()) { 111 *this = r; 112 size_t middle = r.my_begin + (r.my_end - r.my_begin) / 2u; 113 r.my_end = my_begin = middle; 114 } 115 RangeBase(RangeBase & r,proportional_split & p)116 RangeBase(RangeBase& r, proportional_split& p) : RangeStatisticCollector(r, p) { 117 *this = r; 118 size_t original_size = r.size(); 119 T right = self().compute_right_part(r, p); 120 size_t right_part = self().round(right); 121 if( m_ensure_non_empty_size ) { 122 right_part = (original_size == right_part) ? (original_size - 1) : right_part; 123 right_part = (right_part != 0) ? right_part : 1; 124 } 125 r.my_end = my_begin = r.my_end - right_part; 126 if( m_ensure_non_empty_size ) 127 CHECK_MESSAGE((r.my_end != r.my_begin && my_end != my_begin), "Incorrect range split"); 128 } 129 begin()130 size_t begin() const { return my_begin; } end()131 size_t end() const { return my_end; } is_divisible()132 bool is_divisible() const { return (my_end - my_begin) > 1; } empty()133 bool empty() const { return my_end == my_begin; } size()134 size_t size() const { return my_end - my_begin; } 135 136 // helper methods (not part of the range concept) self()137 DerivedRange& self() { return static_cast<DerivedRange&>(*this); } round(T part)138 size_t round(T part) { return size_t(part); } compute_right_part(RangeBase & r,proportional_split & p)139 T compute_right_part(RangeBase& r, proportional_split& p) { 140 return T(r.size() * T(p.right())) / T(p.left() + p.right()); 141 } is_ensure_non_emptiness()142 bool is_ensure_non_emptiness() { return m_ensure_non_empty_size; } 143 }; 144 145 namespace TestRanges { 146 /* 147 * RoundedUpRange rounds result up 148 * RoundedDownRange rounds result down 149 * Range1_2 forces proportion always to be 1:2 and rounds up 150 * Range1_999 uses weird proportion 1:999 and rounds up 151 * Range1_999 uses weird proportion 999:1 and rounds up 152 * BlockedRange uses tbb::blocked_range formula for proportion calculation 153 * InvertedProportionRange inverts proportion suggested by partitioner (e.g. 1:3 --> 3:1) 154 * ExactSplitRange uses integer arithmetic for accurate splitting 155 */ 156 157 class RoundedDownRange: public RangeBase<RoundedDownRange, float> { 158 public: RoundedDownRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)159 RoundedDownRange(size_t _begin, size_t _end, RangeStatisticData *statData, 160 bool provide_feedback, bool ensure_non_empty_size) 161 : RangeBase<RoundedDownRange, float>(_begin, _end, statData, provide_feedback, 162 ensure_non_empty_size) { } RoundedDownRange(RoundedDownRange & r,tbb::split)163 RoundedDownRange(RoundedDownRange& r, tbb::split) 164 : RangeBase<RoundedDownRange, float>(r, tbb::split()) { } RoundedDownRange(RoundedDownRange & r,proportional_split & p)165 RoundedDownRange(RoundedDownRange& r, proportional_split& p) 166 : RangeBase<RoundedDownRange, float>(r, p) { } 167 // uses default implementation of RangeBase::round() which rounds down 168 }; 169 170 class RoundedUpRange: public RangeBase<RoundedUpRange, float> { 171 public: RoundedUpRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)172 RoundedUpRange(size_t _begin, size_t _end, RangeStatisticData *statData, 173 bool provide_feedback, bool ensure_non_empty_size) 174 : RangeBase<RoundedUpRange, float>(_begin, _end, statData, provide_feedback, 175 ensure_non_empty_size) { } RoundedUpRange(RoundedUpRange & r,tbb::split)176 RoundedUpRange(RoundedUpRange& r, tbb::split) 177 : RangeBase<RoundedUpRange, float>(r, tbb::split()) { } RoundedUpRange(RoundedUpRange & r,proportional_split & p)178 RoundedUpRange(RoundedUpRange& r, proportional_split& p) 179 : RangeBase<RoundedUpRange, float>(r, p) { } round(float part)180 size_t round(float part) { return size_t(std::ceil(part)); } 181 }; 182 183 class Range1_2: public RangeBase<Range1_2, float> { 184 public: Range1_2(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)185 Range1_2(size_t _begin, size_t _end, RangeStatisticData *statData, 186 bool provide_feedback, bool ensure_non_empty_size) 187 : RangeBase<Range1_2, float>(_begin, _end, statData, provide_feedback, 188 ensure_non_empty_size) { } Range1_2(Range1_2 & r,tbb::split)189 Range1_2(Range1_2& r, tbb::split) : RangeBase<Range1_2, float>(r, tbb::split()) { } Range1_2(Range1_2 & r,proportional_split & p)190 Range1_2(Range1_2& r, proportional_split& p) : RangeBase<Range1_2, float>(r, p) { } compute_right_part(RangeBase<Range1_2,float> & r,proportional_split &)191 float compute_right_part(RangeBase<Range1_2, float>& r, proportional_split&) { 192 return float(r.size() * 2) / 3.0f; 193 } 194 // uses default implementation of RangeBase::round() which rounds down 195 }; 196 197 class Range1_999: public RangeBase<Range1_999, float> { 198 public: Range1_999(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)199 Range1_999(size_t _begin, size_t _end, RangeStatisticData *statData, 200 bool provide_feedback, bool ensure_non_empty_size) 201 : RangeBase<Range1_999, float>(_begin, _end, statData, provide_feedback, 202 ensure_non_empty_size) { } Range1_999(Range1_999 & r,tbb::split)203 Range1_999(Range1_999& r, tbb::split) : RangeBase<Range1_999, float>(r, tbb::split()) { } Range1_999(Range1_999 & r,proportional_split & p)204 Range1_999(Range1_999& r, proportional_split& p) : RangeBase<Range1_999, float>(r, p) { } compute_right_part(RangeBase<Range1_999,float> & r,proportional_split &)205 float compute_right_part(RangeBase<Range1_999, float>& r, proportional_split&) { 206 return float(r.size() * 999) / 1000.0f; 207 } 208 // uses default implementation of RangeBase::round() which rounds down 209 }; 210 211 class Range999_1: public RangeBase<Range999_1, float> { 212 public: Range999_1(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)213 Range999_1(size_t _begin, size_t _end, RangeStatisticData *statData, 214 bool provide_feedback, bool ensure_non_empty_size) 215 : RangeBase<Range999_1, float>(_begin, _end, statData, provide_feedback, 216 ensure_non_empty_size) { } Range999_1(Range999_1 & r,tbb::split)217 Range999_1(Range999_1& r, tbb::split) : RangeBase<Range999_1, float>(r, tbb::split()) { } Range999_1(Range999_1 & r,proportional_split & p)218 Range999_1(Range999_1& r, proportional_split& p) : RangeBase<Range999_1, float>(r, p) { } compute_right_part(RangeBase<Range999_1,float> & r,proportional_split &)219 float compute_right_part(RangeBase<Range999_1, float>& r, proportional_split&) { 220 return float(r.size()) / 1000.0f; 221 } 222 // uses default implementation of RangeBase::round() which rounds down 223 }; 224 225 class BlockedRange: public RangeStatisticCollector, public blocked_range<size_t> { 226 public: BlockedRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool,bool)227 BlockedRange(size_t _begin, size_t _end, RangeStatisticData *statData, bool, bool) 228 : RangeStatisticCollector(statData), blocked_range<size_t>(_begin, _end) { } BlockedRange(BlockedRange & r,split)229 BlockedRange(BlockedRange& r, split) 230 : RangeStatisticCollector(r, r.size()), blocked_range<size_t>(r, split()) { } BlockedRange(BlockedRange & r,proportional_split & p)231 BlockedRange(BlockedRange& r, proportional_split& p) 232 : RangeStatisticCollector(r, p), blocked_range<size_t>(r, p) { } is_ensure_non_emptiness()233 bool is_ensure_non_emptiness() { return false; } 234 }; 235 236 class InvertedProportionRange: public RangeBase<InvertedProportionRange, float> { 237 public: InvertedProportionRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)238 InvertedProportionRange(size_t _begin, size_t _end, RangeStatisticData *statData, 239 bool provide_feedback, bool ensure_non_empty_size) 240 : RangeBase<InvertedProportionRange, float>(_begin, _end, statData, provide_feedback, 241 ensure_non_empty_size) { } InvertedProportionRange(InvertedProportionRange & r,split)242 InvertedProportionRange(InvertedProportionRange& r, split) 243 : RangeBase<InvertedProportionRange, float>(r, split()) { } InvertedProportionRange(InvertedProportionRange & r,proportional_split & p)244 InvertedProportionRange(InvertedProportionRange& r, proportional_split& p) 245 : RangeBase<InvertedProportionRange, float>(r, p) { } compute_right_part(RangeBase<InvertedProportionRange,float> & r,proportional_split & p)246 float compute_right_part(RangeBase<InvertedProportionRange, float>& r, 247 proportional_split& p) { 248 return float(r.size() * float(p.left())) / float(p.left() + p.right()); 249 } 250 }; 251 252 class ExactSplitRange: public RangeBase<ExactSplitRange, size_t> { 253 public: ExactSplitRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)254 ExactSplitRange(size_t _begin, size_t _end, RangeStatisticData *statData, 255 bool provide_feedback, bool ensure_non_empty_size) 256 : RangeBase<ExactSplitRange, size_t>(_begin, _end, statData, provide_feedback, 257 ensure_non_empty_size) { } ExactSplitRange(ExactSplitRange & r,split)258 ExactSplitRange(ExactSplitRange& r, split) 259 : RangeBase<ExactSplitRange, size_t>(r, split()) { } ExactSplitRange(ExactSplitRange & r,proportional_split & p)260 ExactSplitRange(ExactSplitRange& r, proportional_split& p) 261 : RangeBase<ExactSplitRange, size_t>(r, p) { } compute_right_part(RangeBase<ExactSplitRange,size_t> & r,proportional_split & p)262 size_t compute_right_part(RangeBase<ExactSplitRange, size_t>& r, proportional_split& p) { 263 size_t parts = size_t(p.left() + p.right()); 264 size_t currSize = r.size(); 265 size_t int_part = currSize / parts * p.right(); 266 size_t remainder = currSize % parts * p.right(); 267 int_part += remainder / parts; 268 remainder %= parts; 269 size_t right_part = int_part + (remainder > parts/2 ? 1 : 0); 270 return right_part; 271 } 272 }; 273 274 } // namespace TestRanges 275 276 struct TreeNode { 277 size_t m_affinity; 278 size_t m_range_begin, m_range_end; 279 TreeNode *m_left, *m_right; 280 private: TreeNodeTreeNode281 TreeNode(size_t range_begin, size_t range_end, size_t affinity, 282 TreeNode* left, TreeNode* right) 283 : m_affinity(affinity), m_range_begin(range_begin), m_range_end(range_end), 284 m_left(left), m_right(right) { } 285 286 friend TreeNode* make_node(size_t range_begin, size_t range_end, size_t affinity, 287 TreeNode *left, TreeNode *right); 288 }; 289 290 TreeNode* make_node(size_t range_begin, size_t range_end, size_t affinity, 291 TreeNode* left = NULL, TreeNode* right = NULL) { 292 CHECK_MESSAGE((range_begin <= range_end), "Incorrect range interval"); 293 return new TreeNode(range_begin, range_end, affinity, left, right); 294 } 295 296 // Class stores nodes as a binary tree 297 // (marshals TreeNode objects in accordance with values of range intervals) 298 // Note: BinaryTree deletes all TreeNode objects pushed into it in a destruction phase 299 class BinaryTree { 300 public: BinaryTree()301 BinaryTree() : m_root(NULL) { } ~BinaryTree()302 ~BinaryTree() { 303 if (m_root) 304 remove_node_recursively(m_root); 305 } 306 307 // pushed node must be within subrange of the parent nodes push_node(TreeNode * node)308 void push_node(TreeNode* node) { 309 if (!node) 310 return; 311 312 if (m_root) { 313 CHECK_MESSAGE((node->m_range_begin >= m_root->m_range_begin && node->m_range_end <= m_root->m_range_end), 314 "Cannot push node not from subrange"); 315 } 316 317 push_subnode(m_root, node); 318 } 319 visualize()320 void visualize() { 321 if (!m_root) { // nothing to visualize 322 REPORT("Tree is empty\n"); 323 return; 324 } 325 visualize_node(m_root); 326 } 327 328 bool operator ==(const BinaryTree& other_tree) const { return compare_nodes(m_root, other_tree.m_root); } fill_leafs(std::vector<TreeNode * > & leafs)329 void fill_leafs(std::vector<TreeNode*>& leafs) const { fill_leafs_impl(m_root, leafs); } 330 331 private: 332 TreeNode *m_root; 333 push_subnode(TreeNode * & root_node,TreeNode * node)334 void push_subnode(TreeNode *&root_node, TreeNode *node) { 335 if (!root_node) { 336 root_node = node; 337 return; 338 } else if (are_nodes_equal(root_node, node)) { 339 // no need to push the same node 340 return; 341 } 342 343 if (!has_children(root_node)) { 344 // if current root_node does not have children passed node 345 // should has one of the interval bounds to be equal to 346 // the same bound in the root_node 347 if (is_look_like_left_sibling(root_node, node)) 348 push_subnode(root_node->m_left, node); 349 else 350 push_subnode(root_node->m_right, node); 351 return; 352 } 353 354 if (has_left_child(root_node)) { 355 if (is_subnode(root_node->m_left, node)) { 356 push_subnode(root_node->m_left, node); 357 return; 358 } 359 push_subnode(root_node->m_right, node); 360 return; 361 } 362 363 CHECK_MESSAGE(root_node->m_right != nullptr, "Right child is NULL but must be present"); 364 if (is_subnode(root_node->m_right, node)) { 365 push_subnode(root_node->m_right, node); 366 return; 367 } 368 push_subnode(root_node->m_left, node); 369 return; 370 } 371 has_children(TreeNode * node)372 bool has_children(TreeNode *node) { return node->m_left || node->m_right; } 373 is_look_like_left_sibling(TreeNode * root_node,TreeNode * node)374 bool is_look_like_left_sibling(TreeNode *root_node, TreeNode *node) { 375 if (root_node->m_range_begin == node->m_range_begin) 376 return true; 377 CHECK_MESSAGE(root_node->m_range_end == node->m_range_end, NULL); 378 return false; 379 } 380 has_left_child(TreeNode * node)381 bool has_left_child(TreeNode *node) { return node->m_left != NULL; } 382 is_subnode(TreeNode * root_node,TreeNode * node)383 bool is_subnode(TreeNode *root_node, TreeNode *node) { 384 return root_node->m_range_begin <= node->m_range_begin && 385 node->m_range_end <= root_node->m_range_end; 386 } 387 are_nodes_equal(TreeNode * node1,TreeNode * node2)388 bool are_nodes_equal(TreeNode *node1, TreeNode *node2) const { 389 return node1->m_range_begin == node2->m_range_begin && 390 node1->m_range_end == node2->m_range_end; 391 } 392 remove_node_recursively(TreeNode * node)393 void remove_node_recursively(TreeNode *node) { 394 if (node->m_left) 395 remove_node_recursively(node->m_left); 396 if (node->m_right) 397 remove_node_recursively(node->m_right); 398 delete node; 399 } 400 401 static void visualize_node(const TreeNode* node, unsigned indent = 0) { 402 // respecting indent 403 const char *indentStep = " "; 404 for (unsigned i = 0; i < indent; ++i) 405 REPORT("%s", indentStep); 406 407 size_t rangeSize = node->m_range_end - node->m_range_begin; 408 REPORT("[%llu, %llu)%%%llu@%llu\n", uint64_t(node->m_range_begin), uint64_t(node->m_range_end), 409 uint64_t(rangeSize), uint64_t(node->m_affinity)); 410 411 if (node->m_left) 412 visualize_node(node->m_left, indent + 1); 413 if (node->m_right) 414 visualize_node(node->m_right, indent + 1); 415 } 416 compare_nodes(TreeNode * node1,TreeNode * node2)417 bool compare_nodes(TreeNode* node1, TreeNode* node2) const { 418 if (node1 == NULL && node2 == NULL) return true; 419 if (node1 == NULL || node2 == NULL) return false; 420 return are_nodes_equal(node1, node2) && compare_nodes(node1->m_left, node2->m_left) 421 && compare_nodes(node1->m_right, node2->m_right); 422 } 423 fill_leafs_impl(TreeNode * node,std::vector<TreeNode * > & leafs)424 void fill_leafs_impl(TreeNode* node, std::vector<TreeNode*>& leafs) const { 425 if (node->m_left == NULL && node->m_right == NULL) 426 leafs.push_back(node); 427 if (node->m_left != NULL) fill_leafs_impl(node->m_left, leafs); 428 if (node->m_right != NULL) fill_leafs_impl(node->m_right, leafs); 429 } 430 }; 431 432 class SimpleBody { 433 public: SimpleBody()434 SimpleBody() { } 435 template <typename Range> operator()436 void operator()(Range&) const { } 437 }; 438 439 class SimpleReduceBody { 440 public: SimpleReduceBody()441 SimpleReduceBody() { } SimpleReduceBody(SimpleReduceBody &,tbb::split)442 SimpleReduceBody(SimpleReduceBody&, tbb::split) { } 443 template <typename Range> operator()444 void operator()(Range&) { } join(SimpleReduceBody &)445 void join(SimpleReduceBody&) { } 446 }; 447 448 namespace interaction_with_range_and_partitioner { 449 450 class SplitConstructorAssertedRange { 451 mutable bool is_divisible_called; 452 mutable bool is_empty_called; 453 bool my_assert_in_nonproportional, my_assert_in_proportional; 454 public: SplitConstructorAssertedRange(bool assert_in_nonproportional,bool assert_in_proportional)455 SplitConstructorAssertedRange(bool assert_in_nonproportional, bool assert_in_proportional) 456 : is_divisible_called(false), 457 is_empty_called(false), 458 my_assert_in_nonproportional(assert_in_nonproportional), 459 my_assert_in_proportional(assert_in_proportional) { } SplitConstructorAssertedRange(SplitConstructorAssertedRange & r,tbb::split)460 SplitConstructorAssertedRange(SplitConstructorAssertedRange& r, tbb::split) { 461 *this = r; 462 CHECK_MESSAGE( !my_assert_in_nonproportional, "Disproportional splitting constructor was called but should not been" ); 463 } SplitConstructorAssertedRange(SplitConstructorAssertedRange & r,proportional_split &)464 SplitConstructorAssertedRange(SplitConstructorAssertedRange& r, proportional_split&) { 465 *this = r; 466 CHECK_MESSAGE( !my_assert_in_proportional, "Proportional splitting constructor was called but should not been" ); 467 } is_divisible()468 bool is_divisible() const { 469 if (!is_divisible_called) { 470 is_divisible_called = true; 471 return true; 472 } 473 return false; 474 } empty()475 bool empty() const { 476 if (!is_empty_called) { 477 is_empty_called = true; 478 return false; 479 } 480 return true; 481 } 482 }; 483 484 /* 485 * Possible use cases are: 486 * ------------------------------------------------------------------------------------------------------------- 487 * Range# is_splittable_in_proportion Range proportional ctor Used partitioner Result Effect 488 * ------------------------------------------------------------------------------------------------------------- 489 * 1 true available proportional pMN, r(p), part(p) 490 * ------------------------------------------------------------------------------------------------------------- 491 * 2 false available proportional p11, r(p), part(p) 492 * ------------------------------------------------------------------------------------------------------------- 493 * 3 not defined available proportional p11, r(p), part(p) 494 * ------------------------------------------------------------------------------------------------------------- 495 * 4 true not available proportional pMN, r(s), part(p) * 496 * ------------------------------------------------------------------------------------------------------------- 497 * 5 false not available proportional p11, r(s), part(p) 498 * ------------------------------------------------------------------------------------------------------------- 499 * 6 not defined not available proportional p11, r(s), part(p) 500 * ------------------------------------------------------------------------------------------------------------- 501 * 1 true available simple s, r(s), part(s) 502 * ------------------------------------------------------------------------------------------------------------- 503 * 2 false available simple s, r(s), part(s) 504 * ------------------------------------------------------------------------------------------------------------- 505 * 3 not defined available simple s, r(s), part(s) 506 * ------------------------------------------------------------------------------------------------------------- 507 * 4 true not available simple s, r(s), part(s) 508 * ------------------------------------------------------------------------------------------------------------- 509 * 5 false not available simple s, r(s), part(s) 510 * ------------------------------------------------------------------------------------------------------------- 511 * 6 not defined not available simple s, r(s), part(s) 512 * ------------------------------------------------------------------------------------------------------------- 513 * 514 * Legend: 515 * proportional - with proportional splits (e.g. affinity_partitioner) 516 * simple - without proportional splits (e.g. simple_partitioner, auto_partitioner) 517 * pMN - proportional_split object with proportion M to N is created. (p11 - proportion 1 to 1) 518 * s - split object is created 519 * r(p) - range's proportional split constructor is called 520 * r(s) - range's ordinary split constructor is called 521 * part(p) - partitioner's proportional split constructor is called 522 * part(s) - partitioner's ordinary split constructor is called 523 * * - incorrect split behavior is possible (e.g. partitioner divides at an arbitrary ratio while 524 * range divides into halves) 525 */ 526 527 528 // proportional_split ctor defined 529 class Range1: public SplitConstructorAssertedRange { 530 public: Range1(bool assert_in_nonproportional,bool assert_in_proportional)531 Range1(bool assert_in_nonproportional, bool assert_in_proportional) 532 : SplitConstructorAssertedRange(assert_in_nonproportional, assert_in_proportional) { } Range1(Range1 & r,tbb::split)533 Range1( Range1& r, tbb::split ) : SplitConstructorAssertedRange(r, tbb::split()) { } Range1(Range1 & r,proportional_split & proportion)534 Range1( Range1& r, proportional_split& proportion ) : SplitConstructorAssertedRange(r, proportion) { } 535 }; 536 537 // proportional_split ctor is not defined 538 class Range6: public SplitConstructorAssertedRange { 539 public: Range6(bool assert_in_nonproportional,bool assert_in_proportional)540 Range6(bool assert_in_nonproportional, bool assert_in_proportional) 541 : SplitConstructorAssertedRange(assert_in_nonproportional, assert_in_proportional) { } Range6(Range6 & r,tbb::split)542 Range6(Range6& r, tbb::split) : SplitConstructorAssertedRange(r, tbb::split()) { } 543 }; 544 545 } // namespace interaction_with_range_and_partitioner 546 547 } // namespace test_partitioner_utils 548