1 /*
2 Copyright (c) 2005-2022 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 #include "common/utils.h"
19 #include "common/utils_assert.h"
20 #include "common/utils_concurrency_limit.h"
21
22 #include "oneapi/tbb/blocked_range3d.h"
23 #include "oneapi/tbb/parallel_for.h"
24 #include "oneapi/tbb/global_control.h"
25
26 #include <vector>
27
28 //! \file conformance_blocked_range3d.cpp
29 //! \brief Test for [algorithms.blocked_range3d] specification
30
31 template<typename Tag>
32 class AbstractValueType {
AbstractValueType()33 AbstractValueType() {}
34 int value;
35 public:
36 template<typename OtherTag>
37 friend AbstractValueType<OtherTag> MakeAbstractValueType( int i );
38
39 template<typename OtherTag>
40 friend int GetValueOf( const AbstractValueType<OtherTag>& v ) ;
41 };
42
43 template<typename Tag>
MakeAbstractValueType(int i)44 AbstractValueType<Tag> MakeAbstractValueType( int i ) {
45 AbstractValueType<Tag> x;
46 x.value = i;
47 return x;
48 }
49
50 template<typename Tag>
GetValueOf(const AbstractValueType<Tag> & v)51 int GetValueOf( const AbstractValueType<Tag>& v ) {return v.value;}
52
53 template<typename Tag>
operator <(const AbstractValueType<Tag> & u,const AbstractValueType<Tag> & v)54 bool operator<( const AbstractValueType<Tag>& u, const AbstractValueType<Tag>& v ) {
55 return GetValueOf(u)<GetValueOf(v);
56 }
57
58 template<typename Tag>
operator -(const AbstractValueType<Tag> & u,const AbstractValueType<Tag> & v)59 std::size_t operator-( const AbstractValueType<Tag>& u, const AbstractValueType<Tag>& v ) {
60 return GetValueOf(u)-GetValueOf(v);
61 }
62
63 template<typename Tag>
operator +(const AbstractValueType<Tag> & u,std::size_t offset)64 AbstractValueType<Tag> operator+( const AbstractValueType<Tag>& u, std::size_t offset ) {
65 return MakeAbstractValueType<Tag>(GetValueOf(u)+int(offset));
66 }
67
68 struct PageTag {};
69 struct RowTag {};
70 struct ColTag {};
71
SerialTest()72 static void SerialTest() {
73 typedef AbstractValueType<PageTag> page_type;
74 typedef AbstractValueType<RowTag> row_type;
75 typedef AbstractValueType<ColTag> col_type;
76 typedef oneapi::tbb::blocked_range3d<page_type,row_type,col_type> range_type;
77 for( int page_x=-4; page_x<4; ++page_x ) {
78 for( int page_y=page_x; page_y<4; ++page_y ) {
79 page_type page_i = MakeAbstractValueType<PageTag>(page_x);
80 page_type page_j = MakeAbstractValueType<PageTag>(page_y);
81 for( int page_grain=1; page_grain<4; ++page_grain ) {
82 for( int row_x=-4; row_x<4; ++row_x ) {
83 for( int row_y=row_x; row_y<4; ++row_y ) {
84 row_type row_i = MakeAbstractValueType<RowTag>(row_x);
85 row_type row_j = MakeAbstractValueType<RowTag>(row_y);
86 for( int row_grain=1; row_grain<4; ++row_grain ) {
87 for( int col_x=-4; col_x<4; ++col_x ) {
88 for( int col_y=col_x; col_y<4; ++col_y ) {
89 col_type col_i = MakeAbstractValueType<ColTag>(col_x);
90 col_type col_j = MakeAbstractValueType<ColTag>(col_y);
91 for( int col_grain=1; col_grain<4; ++col_grain ) {
92 range_type r( page_i, page_j, page_grain, row_i, row_j, row_grain, col_i, col_j, col_grain );
93 utils::AssertSameType( r.is_divisible(), true );
94
95 utils::AssertSameType( r.empty(), true );
96
97 utils::AssertSameType( static_cast<range_type::page_range_type::const_iterator*>(nullptr), static_cast<page_type*>(nullptr) );
98 utils::AssertSameType( static_cast<range_type::row_range_type::const_iterator*>(nullptr), static_cast<row_type*>(nullptr) );
99 utils::AssertSameType( static_cast<range_type::col_range_type::const_iterator*>(nullptr), static_cast<col_type*>(nullptr) );
100
101 utils::AssertSameType( r.pages(), oneapi::tbb::blocked_range<page_type>( page_i, page_j, 1 ));
102 utils::AssertSameType( r.rows(), oneapi::tbb::blocked_range<row_type>( row_i, row_j, 1 ));
103 utils::AssertSameType( r.cols(), oneapi::tbb::blocked_range<col_type>( col_i, col_j, 1 ));
104
105 REQUIRE( r.empty()==(page_x==page_y||row_x==row_y||col_x==col_y) );
106
107 REQUIRE( r.is_divisible()==(page_y-page_x>page_grain||row_y-row_x>row_grain||col_y-col_x>col_grain) );
108
109 if( r.is_divisible() ) {
110 range_type r2(r,oneapi::tbb::split());
111 if( (GetValueOf(r2.pages().begin())==GetValueOf(r.pages().begin())) && (GetValueOf(r2.rows().begin())==GetValueOf(r.rows().begin())) ) {
112 REQUIRE( GetValueOf(r2.pages().end())==GetValueOf(r.pages().end()) );
113 REQUIRE( GetValueOf(r2.rows().end())==GetValueOf(r.rows().end()) );
114 REQUIRE( GetValueOf(r2.cols().begin())==GetValueOf(r.cols().end()) );
115 } else {
116 if ( (GetValueOf(r2.pages().begin())==GetValueOf(r.pages().begin())) && (GetValueOf(r2.cols().begin())==GetValueOf(r.cols().begin())) ) {
117 REQUIRE( GetValueOf(r2.pages().end())==GetValueOf(r.pages().end()) );
118 REQUIRE( GetValueOf(r2.cols().end())==GetValueOf(r.cols().end()) );
119 REQUIRE( GetValueOf(r2.rows().begin())==GetValueOf(r.rows().end()) );
120 } else {
121 REQUIRE( GetValueOf(r2.rows().end())==GetValueOf(r.rows().end()) );
122 REQUIRE( GetValueOf(r2.cols().end())==GetValueOf(r.cols().end()) );
123 REQUIRE( GetValueOf(r2.pages().begin())==GetValueOf(r.pages().end()) );
124 }
125 }
126 }
127 }
128 }
129 }
130 }
131 }
132 }
133 }
134 }
135 }
136 }
137
138 const int N = 1<<5;
139
140 unsigned char Array[N][N][N];
141
142 struct Striker {
143 // Note: we use <int> here instead of <long> in order to test for problems similar to Quad 407676
operator ()Striker144 void operator()( const oneapi::tbb::blocked_range3d<int>& r ) const {
145 for( oneapi::tbb::blocked_range<int>::const_iterator i=r.pages().begin(); i!=r.pages().end(); ++i )
146 for( oneapi::tbb::blocked_range<int>::const_iterator j=r.rows().begin(); j!=r.rows().end(); ++j )
147 for( oneapi::tbb::blocked_range<int>::const_iterator k=r.cols().begin(); k!=r.cols().end(); ++k )
148 ++Array[i][j][k];
149 }
150 };
151
ParallelTest()152 void ParallelTest() {
153 for( int i=0; i<N; i=i<3 ? i+1 : i*3 ) {
154 for( int j=0; j<N; j=j<3 ? j+1 : j*3 ) {
155 for( int k=0; k<N; k=k<3 ? k+1 : k*3 ) {
156 const oneapi::tbb::blocked_range3d<int> r( 0, i, 5, 0, j, 3, 0, k, 1 );
157 oneapi::tbb::parallel_for( r, Striker() );
158 for( int l=0; l<N; ++l ) {
159 for( int m=0; m<N; ++m ) {
160 for( int n=0; n<N; ++n ) {
161 if( Array[l][m][n] != (l<i && m<j && n<k) ) REQUIRE(false);
162 Array[l][m][n] = 0;
163 }
164 }
165 }
166 }
167 }
168 }
169 }
170
171 //! Testing blocked_range3d interface
172 //! \brief \ref interface \ref requirement
173 TEST_CASE("Serial test") {
174 SerialTest();
175 }
176
177 //! Testing blocked_range3d interface with parallel_for
178 //! \brief \ref requirement
179 TEST_CASE("Parallel test") {
180 for ( auto concurrency_level : utils::concurrency_range() ) {
181 oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
182 ParallelTest();
183 }
184 }
185
186 //! Testing blocked_range3d with proportional splitting
187 //! \brief \ref interface \ref requirement
188 TEST_CASE("blocked_range3d proportional splitting") {
189 oneapi::tbb::blocked_range3d<int> original(0, 100, 0, 100, 0, 100);
190 oneapi::tbb::blocked_range3d<int> first(original);
191 oneapi::tbb::proportional_split ps(3, 1);
192 oneapi::tbb::blocked_range3d<int> second(first, ps);
193
194 int expected_first_end = static_cast<int>(
195 original.rows().begin() + ps.left() * (original.rows().end() - original.rows().begin()) / (ps.left() + ps.right())
196 );
197 if (first.rows().size() == second.rows().size()) {
198 if (first.cols().size() == second.cols().size()) {
199 // Splitting was made by pages
200 utils::check_range_bounds_after_splitting(original.pages(), first.pages(), second.pages(), expected_first_end);
201 } else {
202 // Splitting was made by cols
203 utils::check_range_bounds_after_splitting(original.cols(), first.cols(), second.cols(), expected_first_end);
204 }
205 } else {
206 // Splitting was made by rows
207 utils::check_range_bounds_after_splitting(original.rows(), first.rows(), second.rows(), expected_first_end);
208 }
209 }
210
211 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
212 //! Testing blocked_range3d deduction guides
213 //! \brief \ref interface
214 TEST_CASE("Deduction guides") {
215 std::vector<const unsigned long *> v;
216 std::vector<double> v2;
217 std::vector<std::vector<int>> v3;
218
219 // check blocked_range2d(PageValue, PageValue, size_t, RowValue, RowValue, size_t, ColValue, ColValue, size_t)
220 oneapi::tbb::blocked_range3d r1(v.begin(), v.end(), 2, v2.begin(), v2.end(), 2, v3.begin(), v3.end(), 6);
221 static_assert(std::is_same<decltype(r1),
222 oneapi::tbb::blocked_range3d<decltype(v)::iterator, decltype(v2)::iterator, decltype(v3)::iterator>>::value);
223
224 // check blocked_range2d(blocked_range3d &)
225 oneapi::tbb::blocked_range3d r2(r1);
226 static_assert(std::is_same<decltype(r2), decltype(r1)>::value);
227
228 // check blocked_range2d(blocked_range3d &&)
229 oneapi::tbb::blocked_range3d r3(std::move(r1));
230 static_assert(std::is_same<decltype(r3), decltype(r1)>::value);
231 }
232 #endif
233
234