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 | |