1 | /* |
2 | Copyright (c) 2017-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 | #define TBB_PREVIEW_BLOCKED_RANGE_ND 1 |
18 | #include "tbb/blocked_rangeNd.h" |
19 | |
20 | #include "tbb/tbb_config.h" |
21 | |
22 | #if __TBB_CPP11_PRESENT && __TBB_CPP11_ARRAY_PRESENT && __TBB_CPP11_TEMPLATE_ALIASES_PRESENT |
23 | // AbstractValueType class represents Value concept's requirements in the most abstract way |
24 | class AbstractValueType { |
25 | int value; |
26 | AbstractValueType() {} |
27 | public: |
28 | friend AbstractValueType MakeAbstractValue(int i); |
29 | friend int GetValueOf(const AbstractValueType& v); |
30 | }; |
31 | |
32 | int GetValueOf(const AbstractValueType& v) { return v.value; } |
33 | |
34 | AbstractValueType MakeAbstractValue(int i) { |
35 | AbstractValueType x; |
36 | x.value = i; |
37 | return x; |
38 | } |
39 | |
40 | // operator- returns amount of elements of AbstractValueType between u and v |
41 | std::size_t operator-(const AbstractValueType& u, const AbstractValueType& v) { |
42 | return GetValueOf(u) - GetValueOf(v); |
43 | } |
44 | |
45 | bool operator<(const AbstractValueType& u, const AbstractValueType& v) { |
46 | return GetValueOf(u) < GetValueOf(v); |
47 | } |
48 | |
49 | AbstractValueType operator+(const AbstractValueType& u, std::size_t offset) { |
50 | return MakeAbstractValue(GetValueOf(u) + int(offset)); |
51 | } |
52 | |
53 | #include "harness_assert.h" |
54 | #include <algorithm> // std::for_each |
55 | #include <array> |
56 | |
57 | namespace internal { |
58 | template<typename range_t, unsigned int N> |
59 | struct utils { |
60 | using val_t = typename range_t::value_type; |
61 | |
62 | template<typename EntityType, std::size_t DimSize> |
63 | using data_type = std::array<typename utils<range_t, N - 1>::template data_type<EntityType, DimSize>, DimSize>; |
64 | |
65 | template<typename EntityType, std::size_t DimSize> |
66 | static void init_data(data_type<EntityType, DimSize>& data) { |
67 | std::for_each(data.begin(), data.end(), utils<range_t, N - 1>::template init_data<EntityType, DimSize>); |
68 | } |
69 | |
70 | template<typename EntityType, std::size_t DimSize> |
71 | static void increment_data(const range_t& range, data_type<EntityType, DimSize>& data) { |
72 | auto begin = data.begin() + range.dim(N - 1).begin(); |
73 | // same as "auto end = out.begin() + range.dim(N - 1).end();" |
74 | auto end = begin + range.dim(N - 1).size(); |
75 | for (auto i = begin; i != end; ++i) { |
76 | utils<range_t, N - 1>::template increment_data<EntityType, DimSize>(range, *i); |
77 | } |
78 | } |
79 | |
80 | template<typename EntityType, std::size_t DimSize> |
81 | static void check_data(const range_t& range, data_type<EntityType, DimSize>& data) { |
82 | auto begin = data.begin() + range.dim(N - 1).begin(); |
83 | // same as "auto end = out.begin() + range.dim(N - 1).end();" |
84 | auto end = begin + range.dim(N - 1).size(); |
85 | for (auto i = begin; i != end; ++i) { |
86 | utils<range_t, N - 1>::template check_data<EntityType, DimSize>(range, *i); |
87 | } |
88 | } |
89 | |
90 | template<typename input_t, std::size_t... Is> |
91 | static range_t make_range(std::size_t shift, bool negative, val_t(*gen)(input_t), tbb::internal::index_sequence<Is...>) { |
92 | return range_t( { { |
93 | /* begin =*/gen(negative ? -input_t(Is + shift) : 0), |
94 | /* end =*/gen(input_t(Is + shift)), |
95 | /*grainsize =*/Is + 1} |
96 | /*pack expansion*/... } ); |
97 | } |
98 | |
99 | static bool is_empty(const range_t& range) { |
100 | if (range.dim(N - 1).empty()) { return true; } |
101 | return utils<range_t, N - 1>::is_empty(range); |
102 | } |
103 | |
104 | static bool is_divisible(const range_t& range) { |
105 | if (range.dim(N - 1).is_divisible()) { return true; } |
106 | return utils<range_t, N - 1>::is_divisible(range); |
107 | } |
108 | |
109 | static void check_splitting(const range_t& range_split, const range_t& range_new, int(*get)(const val_t&), bool split_checker = false) { |
110 | if (get(range_split.dim(N - 1).begin()) == get(range_new.dim(N - 1).begin())) { |
111 | ASSERT(get(range_split.dim(N - 1).end()) == get(range_new.dim(N - 1).end()), NULL); |
112 | } |
113 | else { |
114 | ASSERT(get(range_split.dim(N - 1).end()) == get(range_new.dim(N - 1).begin()) && !split_checker, NULL); |
115 | split_checker = true; |
116 | } |
117 | utils<range_t, N - 1>::check_splitting(range_split, range_new, get, split_checker); |
118 | } |
119 | |
120 | }; |
121 | |
122 | template<typename range_t> |
123 | struct utils<range_t, 0> { |
124 | using val_t = typename range_t::value_type; |
125 | |
126 | template<typename EntityType, std::size_t DimSize> |
127 | using data_type = EntityType; |
128 | |
129 | template<typename EntityType, std::size_t DimSize> |
130 | static void init_data(data_type<EntityType, DimSize>& data) { data = 0; } |
131 | |
132 | template<typename EntityType, std::size_t DimSize> |
133 | static void increment_data(const range_t&, data_type<EntityType, DimSize>& data) { ++data; } |
134 | |
135 | template<typename EntityType, std::size_t DimSize> |
136 | static void check_data(const range_t&, data_type<EntityType, DimSize>& data) { |
137 | ASSERT(data == 1, NULL); |
138 | } |
139 | |
140 | static bool is_empty(const range_t&) { return false; } |
141 | |
142 | static bool is_divisible(const range_t&) { return false; } |
143 | |
144 | static void check_splitting(const range_t&, const range_t&, int(*)(const val_t&), bool) {} |
145 | }; |
146 | |
147 | // We need MakeInt function to pass it into make_range as factory function |
148 | // because of matching make_range with AbstractValueType and other types too |
149 | int MakeInt(int i) { return i; } |
150 | } |
151 | |
152 | template<unsigned int DimAmount> |
153 | void SerialTest() { |
154 | __TBB_STATIC_ASSERT((tbb::blocked_rangeNd<int, DimAmount>::ndims() |
155 | == tbb::blocked_rangeNd<AbstractValueType, DimAmount>::ndims()), |
156 | "different amount of dimensions" ); |
157 | |
158 | using range_t = tbb::blocked_rangeNd<AbstractValueType, DimAmount>; |
159 | // 'typedef' instead of 'using' because of GCC 4.7.2 bug on Debian 7.0 |
160 | typedef internal::utils<range_t, DimAmount> utils; |
161 | |
162 | // Generate empty range |
163 | range_t r = utils::make_range(0, true, &MakeAbstractValue, tbb::internal::make_index_sequence<DimAmount>()); |
164 | |
165 | AssertSameType(r.is_divisible(), bool()); |
166 | AssertSameType(r.empty(), bool()); |
167 | AssertSameType(range_t::ndims(), 0U); |
168 | |
169 | ASSERT(r.empty() == utils::is_empty(r) && r.empty(), NULL); |
170 | ASSERT(r.is_divisible() == utils::is_divisible(r), NULL); |
171 | |
172 | // Generate not-empty range divisible range |
173 | r = utils::make_range(1, true, &MakeAbstractValue, tbb::internal::make_index_sequence<DimAmount>()); |
174 | ASSERT(r.empty() == utils::is_empty(r) && !r.empty(), NULL); |
175 | ASSERT(r.is_divisible() == utils::is_divisible(r) && r.is_divisible(), NULL); |
176 | |
177 | range_t r_new(r, tbb::split()); |
178 | utils::check_splitting(r, r_new, &GetValueOf); |
179 | |
180 | SerialTest<DimAmount - 1>(); |
181 | } |
182 | template<> void SerialTest<0>() {} |
183 | |
184 | #include "tbb/parallel_for.h" |
185 | |
186 | template<unsigned int DimAmount> |
187 | void ParallelTest() { |
188 | using range_t = tbb::blocked_rangeNd<int, DimAmount>; |
189 | // 'typedef' instead of 'using' because of GCC 4.7.2 bug on Debian 7.0 |
190 | typedef internal::utils<range_t, DimAmount> utils; |
191 | |
192 | // Max size is 1 << 20 - 1 bytes |
193 | // Thus size of one dimension's elements is 1 << (20 / DimAmount - 1) bytes |
194 | typename utils::template data_type<unsigned char, 1 << (20 / DimAmount - 1)> data; |
195 | utils::init_data(data); |
196 | |
197 | range_t r = utils::make_range((1 << (20 / DimAmount - 1)) - DimAmount, false, &internal::MakeInt, tbb::internal::make_index_sequence<DimAmount>()); |
198 | |
199 | tbb::parallel_for(r, [&data](const range_t& range) { |
200 | utils::increment_data(range, data); |
201 | }); |
202 | |
203 | utils::check_data(r, data); |
204 | |
205 | ParallelTest<DimAmount - 1>(); |
206 | } |
207 | template<> void ParallelTest<0>() {} |
208 | |
209 | void TestCtors() { |
210 | tbb::blocked_rangeNd<int, 1>{ { 0,13,3 } }; |
211 | |
212 | tbb::blocked_rangeNd<int, 1>{ tbb::blocked_range<int>{ 0,13,3 } }; |
213 | |
214 | tbb::blocked_rangeNd<int, 2>(tbb::blocked_range<int>(-8923, 8884, 13), tbb::blocked_range<int>(-8923, 5, 13)); |
215 | |
216 | tbb::blocked_rangeNd<int, 2>({ -8923, 8884, 13 }, { -8923, 8884, 13 }); |
217 | |
218 | tbb::blocked_range<int> r1(0, 13); |
219 | |
220 | tbb::blocked_range<int> r2(-12, 23); |
221 | |
222 | tbb::blocked_rangeNd<int, 2>({ { -8923, 8884, 13 }, r1}); |
223 | |
224 | tbb::blocked_rangeNd<int, 2>({ r2, r1 }); |
225 | |
226 | tbb::blocked_rangeNd<int, 2>(r1, r2); |
227 | |
228 | tbb::blocked_rangeNd<AbstractValueType, 4>({ MakeAbstractValue(-3), MakeAbstractValue(13), 8 }, |
229 | { MakeAbstractValue(-53), MakeAbstractValue(23), 2 }, |
230 | { MakeAbstractValue(-23), MakeAbstractValue(33), 1 }, |
231 | { MakeAbstractValue(-13), MakeAbstractValue(43), 7 }); |
232 | } |
233 | |
234 | static const std::size_t N = 4; |
235 | |
236 | #include "harness.h" |
237 | #include "tbb/task_scheduler_init.h" |
238 | |
239 | int TestMain() { |
240 | TestCtors(); |
241 | SerialTest<N>(); |
242 | for( int p=MinThread; p<= MaxThread; ++p ) { |
243 | tbb::task_scheduler_init init(p); |
244 | ParallelTest<N>(); |
245 | } |
246 | return Harness::Done; |
247 | } |
248 | |
249 | #else |
250 | |
251 | // tbb::blocked_rangeNd requires C++11 support |
252 | #define HARNESS_SKIP_TEST 1 |
253 | #include "harness.h" |
254 | |
255 | #endif /* __TBB_CPP11_PRESENT && __TBB_CPP11_ARRAY_PRESENT && __TBB_CPP11_TEMPLATE_ALIASES_PRESENT */ |
256 | |