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