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