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