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 //! \file test_openmp.cpp 18 //! \brief Test for [internal] functionality 19 20 #if _WIN32 || _WIN64 21 #define _CRT_SECURE_NO_WARNINGS 22 #endif 23 24 #include "common/test.h" 25 #include "common/utils.h" 26 #include "common/utils_env.h" 27 #include "tbb/global_control.h" 28 #include "tbb/blocked_range.h" 29 #include "tbb/parallel_for.h" 30 #include "tbb/parallel_reduce.h" 31 32 // Test mixing OpenMP and TBB 33 #include <omp.h> 34 35 using data_type = short; 36 37 void SerialConvolve( data_type c[], const data_type a[], int m, const data_type b[], int n ) { 38 for (int i = 0; i < m + n - 1; ++i) { 39 int start = i < n ? 0 : i - n + 1; 40 int finish = i < m ? i + 1 : m; 41 data_type sum = 0; 42 for (int j = start; j < finish; ++j) 43 sum += a[j] * b[i - j]; 44 c[i] = sum; 45 } 46 } 47 48 #if _MSC_VER && !defined(__INTEL_COMPILER) 49 // Suppress overzealous warning about short+=short 50 #pragma warning( push ) 51 #pragma warning( disable: 4244 ) 52 #endif 53 54 class InnerBody: utils::NoAssign { 55 const data_type* my_a; 56 const data_type* my_b; 57 const int i; 58 public: 59 data_type sum; 60 InnerBody( data_type /*c*/[], const data_type a[], const data_type b[], int ii ) : 61 my_a(a), my_b(b), i(ii), sum(0) 62 {} 63 InnerBody( InnerBody& x, tbb::split ) : 64 my_a(x.my_a), my_b(x.my_b), i(x.i), sum(0) 65 { 66 } 67 void join( InnerBody& x ) { sum += x.sum; } 68 void operator()( const tbb::blocked_range<int>& range ) { 69 for (int j = range.begin(); j != range.end(); ++j) 70 sum += my_a[j] * my_b[i - j]; 71 } 72 }; 73 74 #if _MSC_VER && !defined(__INTEL_COMPILER) 75 #pragma warning( pop ) 76 #endif 77 78 //! Test OpenMP loop around TBB loop 79 void OpenMP_TBB_Convolve( data_type c[], const data_type a[], int m, const data_type b[], int n, int p ) { 80 utils::suppress_unused_warning(p); 81 #pragma omp parallel num_threads(p) 82 { 83 #pragma omp for 84 for (int i = 0; i < m + n - 1; ++i) { 85 int start = i < n ? 0 : i - n + 1; 86 int finish = i < m ? i + 1 : m; 87 InnerBody body(c, a, b, i); 88 tbb::parallel_reduce(tbb::blocked_range<int>(start, finish, 10), body); 89 c[i] = body.sum; 90 } 91 } 92 } 93 94 class OuterBody: utils::NoAssign { 95 const data_type* my_a; 96 const data_type* my_b; 97 data_type* my_c; 98 const int m; 99 const int n; 100 #if __clang__ && !__INTEL_COMPILER 101 #pragma clang diagnostic push 102 #pragma clang diagnostic ignored "-Wunused-private-field" 103 #endif 104 const int p; 105 #if __clang__ && !__INTEL_COMPILER 106 #pragma clang diagnostic pop // "-Wunused-private-field" 107 #endif 108 public: 109 OuterBody( data_type c[], const data_type a[], int m_, const data_type b[], int n_, int p_ ) : 110 my_a(a), my_b(b), my_c(c), m(m_), n(n_), p(p_) 111 {} 112 void operator()( const tbb::blocked_range<int>& range ) const { 113 for (int i = range.begin(); i != range.end(); ++i) { 114 int start = i < n ? 0 : i - n + 1; 115 int finish = i < m ? i + 1 : m; 116 data_type sum = 0; 117 #pragma omp parallel for reduction(+:sum) num_threads(p) 118 for (int j = start; j < finish; ++j) 119 sum += my_a[j] * my_b[i - j]; 120 my_c[i] = sum; 121 } 122 } 123 }; 124 125 //! Test TBB loop around OpenMP loop 126 void TBB_OpenMP_Convolve( data_type c[], const data_type a[], int m, const data_type b[], int n, int p ) { 127 tbb::parallel_for(tbb::blocked_range<int>(0, m + n - 1, 10), OuterBody(c, a, m, b, n, p)); 128 } 129 130 #if __INTEL_COMPILER 131 void TestNumThreads() { 132 utils::SetEnv("KMP_AFFINITY", "compact"); 133 // Make an OpenMP call before initializing TBB 134 int omp_nthreads = omp_get_max_threads(); 135 #pragma omp parallel 136 {} 137 int tbb_nthreads = tbb::this_task_arena::max_concurrency(); 138 // For the purpose of testing, assume that OpenMP and TBB should utilize the same # of threads. 139 // If it's not true on some platforms, the test will need to be adjusted. 140 REQUIRE_MESSAGE(tbb_nthreads == omp_nthreads, "Initialization of TBB is possibly affected by OpenMP"); 141 } 142 #endif // __INTEL_COMPILER 143 144 const int M = 17 * 17; 145 const int N = 13 * 13; 146 data_type A[M], B[N]; 147 data_type expected[M+N], actual[M+N]; 148 149 template <class Func> 150 void RunTest( Func F, int m, int n, int p) { 151 tbb::global_control limit(tbb::global_control::max_allowed_parallelism, p); 152 memset(actual, -1, (m + n) * sizeof(data_type)); 153 F(actual, A, m, B, n, p); 154 CHECK(memcmp(actual, expected, (m + n - 1) * sizeof(data_type)) == 0); 155 } 156 157 // Disable it because OpenMP isn't instrumented that leads to false positive 158 #if !__TBB_USE_THREAD_SANITIZER 159 //! \brief \ref error_guessing 160 TEST_CASE("Testing oneTBB with OpenMP") { 161 #if __INTEL_COMPILER 162 TestNumThreads(); // Testing initialization-related behavior; must be the first 163 #endif // __INTEL_COMPILER 164 for (int p = static_cast<int>(utils::MinThread); p <= static_cast<int>(utils::MaxThread); ++p) { 165 for (int m = 1; m <= M; m *= 17) { 166 for (int n = 1; n <= N; n *= 13) { 167 for (int i = 0; i < m; ++i) A[i] = data_type(1 + i / 5); 168 for (int i = 0; i < n; ++i) B[i] = data_type(1 + i / 7); 169 SerialConvolve( expected, A, m, B, n ); 170 RunTest( OpenMP_TBB_Convolve, m, n, p ); 171 RunTest( TBB_OpenMP_Convolve, m, n, p ); 172 } 173 } 174 } 175 } 176 #endif 177