1 /*
2     Copyright (c) 2005-2021 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 {
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>
44 AbstractValueType<Tag> MakeAbstractValueType( int i ) {
45     AbstractValueType<Tag> x;
46     x.value = i;
47     return x;
48 }
49 
50 template<typename Tag>
51 int GetValueOf( const AbstractValueType<Tag>& v ) {return v.value;}
52 
53 template<typename Tag>
54 bool operator<( const AbstractValueType<Tag>& u, const AbstractValueType<Tag>& v ) {
55     return GetValueOf(u)<GetValueOf(v);
56 }
57 
58 template<typename Tag>
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>
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 
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*>(0), static_cast<page_type*>(0) );
98                                         utils::AssertSameType( static_cast<range_type::row_range_type::const_iterator*>(0), static_cast<row_type*>(0) );
99                                         utils::AssertSameType( static_cast<range_type::col_range_type::const_iterator*>(0), static_cast<col_type*>(0) );
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
144     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 
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 = original.rows().begin() + ps.left() * (original.rows().end() - original.rows().begin()) / (ps.left() + ps.right());
195     if (first.rows().size() == second.rows().size()) {
196         if (first.cols().size() == second.cols().size()) {
197             // Splitting was made by pages
198             utils::check_range_bounds_after_splitting(original.pages(), first.pages(), second.pages(), expected_first_end);
199         } else {
200             // Splitting was made by cols
201             utils::check_range_bounds_after_splitting(original.cols(), first.cols(), second.cols(), expected_first_end);
202         }
203     } else {
204         // Splitting was made by rows
205         utils::check_range_bounds_after_splitting(original.rows(), first.rows(), second.rows(), expected_first_end);
206     }
207 }
208 
209 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
210 //! Testing blocked_range3d deduction guides
211 //! \brief \ref interface
212 TEST_CASE("Deduction guides") {
213     std::vector<const unsigned long *> v;
214     std::vector<double> v2;
215     std::vector<std::vector<int>> v3;
216 
217     // check blocked_range2d(PageValue, PageValue, size_t, RowValue, RowValue, size_t, ColValue, ColValue, size_t)
218     oneapi::tbb::blocked_range3d r1(v.begin(), v.end(), 2, v2.begin(), v2.end(), 2, v3.begin(), v3.end(), 6);
219     static_assert(std::is_same<decltype(r1),
220         oneapi::tbb::blocked_range3d<decltype(v)::iterator, decltype(v2)::iterator, decltype(v3)::iterator>>::value);
221 
222     // check blocked_range2d(blocked_range3d &)
223     oneapi::tbb::blocked_range3d r2(r1);
224     static_assert(std::is_same<decltype(r2), decltype(r1)>::value);
225 
226     // check blocked_range2d(blocked_range3d &&)
227     oneapi::tbb::blocked_range3d r3(std::move(r1));
228     static_assert(std::is_same<decltype(r3), decltype(r1)>::value);
229 }
230 #endif
231 
232