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_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 { 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> 42 AbstractValueType<Tag> MakeAbstractValueType( int i ) { 43 AbstractValueType<Tag> x; 44 x.value = i; 45 return x; 46 } 47 48 template<typename Tag> 49 int GetValueOf( const AbstractValueType<Tag>& v ) {return v.value;} 50 51 template<typename Tag> 52 bool operator<( const AbstractValueType<Tag>& u, const AbstractValueType<Tag>& v ) { 53 return GetValueOf(u)<GetValueOf(v); 54 } 55 56 template<typename Tag> 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> 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 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*>(nullptr), static_cast<row_type*>(nullptr) ); 87 utils::AssertSameType( static_cast<range_type::col_range_type::const_iterator*>(nullptr), static_cast<col_type*>(nullptr) ); 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 116 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 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