1 // RUN: %libomp-cxx-compile-and-run 2 // RUN: %libomp-cxx-compile -DFLG=1 && %libomp-run 3 // REQUIRES: openmp-5.0 4 // GCC-5 is needed for OpenMP 4.0 support (taskgroup) 5 // XFAIL: gcc-4 6 #include <cstdio> 7 #include <cmath> 8 #include <cassert> 9 #include <omp.h> 10 11 // Total number of loop iterations, should be multiple of T for this test 12 #define N 10000 13 14 // Flag to request lazy (1) or eager (0) allocation of reduction objects 15 #ifndef FLG 16 #define FLG 0 17 #endif 18 19 /* 20 // initial user's code that corresponds to pseudo code of the test 21 #pragma omp taskgroup task_reduction(+:i,j) task_reduction(*:x) 22 { 23 for( int l = 0; l < N; ++l ) { 24 #pragma omp task firstprivate(l) in_reduction(+:i) in_reduction(*:x) 25 { 26 i += l; 27 if( l%2 ) 28 x *= 1.0 / (l + 1); 29 else 30 x *= (l + 1); 31 } 32 } 33 34 #pragma omp taskgroup task_reduction(-:i,k) task_reduction(+:y) 35 { 36 for( int l = 0; l < N; ++l ) { 37 #pragma omp task firstprivate(l) in_reduction(+:j,y) \ 38 in_reduction(*:x) in_reduction(-:k) 39 { 40 j += l; 41 k -= l; 42 y += (double)l; 43 if( l%2 ) 44 x *= 1.0 / (l + 1); 45 else 46 x *= (l + 1); 47 } 48 #pragma omp task firstprivate(l) in_reduction(+:y) in_reduction(-:i,k) 49 { 50 i -= l; 51 k -= l; 52 y += (double)l; 53 } 54 #pragma omp task firstprivate(l) in_reduction(+:j) in_reduction(*:x) 55 { 56 j += l; 57 if( l%2 ) 58 x *= 1.0 / (l + 1); 59 else 60 x *= (l + 1); 61 } 62 } 63 } // inner reduction 64 65 for( int l = 0; l < N; ++l ) { 66 #pragma omp task firstprivate(l) in_reduction(+:j) 67 j += l; 68 } 69 } // outer reduction 70 */ 71 72 //------------------------------------------------ 73 // OpenMP runtime library routines 74 #ifdef __cplusplus 75 extern "C" { 76 #endif 77 extern void* __kmpc_task_reduction_get_th_data(int gtid, void* tg, void* item); 78 extern void* __kmpc_task_reduction_init(int gtid, int num, void* data); 79 extern int __kmpc_global_thread_num(void*); 80 #ifdef __cplusplus 81 } 82 #endif 83 84 //------------------------------------------------ 85 // Compiler-generated code 86 87 typedef struct _task_red_item { 88 void *shar; // shared reduction item 89 size_t size; // size of data item 90 void *f_init; // data initialization routine 91 void *f_fini; // data finalization routine 92 void *f_comb; // data combiner routine 93 unsigned flags; 94 } _task_red_item_t; 95 96 // int:+ no need in init/fini callbacks, valid for subtraction 97 void __red_int_add_comb(void *lhs, void *rhs) // combiner 98 { *(int*)lhs += *(int*)rhs; } 99 100 // long long:+ no need in init/fini callbacks, valid for subtraction 101 void __red_llong_add_comb(void *lhs, void *rhs) // combiner 102 { *(long long*)lhs += *(long long*)rhs; } 103 104 // double:* no need in fini callback 105 void __red_dbl_mul_init(void *data) // initializer 106 { *(double*)data = 1.0; } 107 void __red_dbl_mul_comb(void *lhs, void *rhs) // combiner 108 { *(double*)lhs *= *(double*)rhs; } 109 110 // double:+ no need in init/fini callbacks 111 void __red_dbl_add_comb(void *lhs, void *rhs) // combiner 112 { *(double*)lhs += *(double*)rhs; } 113 114 // ============================== 115 116 void calc_serial(int *pi, long long *pj, double *px, long long *pk, double *py) 117 { 118 for( int l = 0; l < N; ++l ) { 119 *pi += l; 120 if( l%2 ) 121 *px *= 1.0 / (l + 1); 122 else 123 *px *= (l + 1); 124 } 125 for( int l = 0; l < N; ++l ) { 126 *pj += l; 127 *pk -= l; 128 *py += (double)l; 129 if( l%2 ) 130 *px *= 1.0 / (l + 1); 131 else 132 *px *= (l + 1); 133 134 *pi -= l; 135 *pk -= l; 136 *py += (double)l; 137 138 *pj += l; 139 if( l%2 ) 140 *px *= 1.0 / (l + 1); 141 else 142 *px *= (l + 1); 143 } 144 for( int l = 0; l < N; ++l ) { 145 *pj += l; 146 } 147 } 148 149 //------------------------------------------------ 150 // Test case 151 int main() 152 { 153 int nthreads = omp_get_max_threads(); 154 int err = 0; 155 void** ptrs = (void**)malloc(nthreads*sizeof(void*)); 156 157 // user's code ====================================== 158 // variables for serial calculations: 159 int is = 3; 160 long long js = -9999999; 161 double xs = 99999.0; 162 long long ks = 99999999; 163 double ys = -99999999.0; 164 // variables for parallel calculations: 165 int ip = 3; 166 long long jp = -9999999; 167 double xp = 99999.0; 168 long long kp = 99999999; 169 double yp = -99999999.0; 170 171 calc_serial(&is, &js, &xs, &ks, &ys); 172 // ================================================== 173 for (int i = 0; i < nthreads; ++i) 174 ptrs[i] = NULL; 175 #pragma omp parallel 176 { 177 #pragma omp single nowait 178 { 179 // outer taskgroup reduces (i,j,x) 180 #pragma omp taskgroup // task_reduction(+:i,j) task_reduction(*:x) 181 { 182 _task_red_item_t red_data[3]; 183 red_data[0].shar = &ip; 184 red_data[0].size = sizeof(ip); 185 red_data[0].f_init = NULL; // RTL will zero thread-specific objects 186 red_data[0].f_fini = NULL; // no destructors needed 187 red_data[0].f_comb = (void*)&__red_int_add_comb; 188 red_data[0].flags = FLG; 189 red_data[1].shar = &jp; 190 red_data[1].size = sizeof(jp); 191 red_data[1].f_init = NULL; // RTL will zero thread-specific objects 192 red_data[1].f_fini = NULL; // no destructors needed 193 red_data[1].f_comb = (void*)&__red_llong_add_comb; 194 red_data[1].flags = FLG; 195 red_data[2].shar = &xp; 196 red_data[2].size = sizeof(xp); 197 red_data[2].f_init = (void*)&__red_dbl_mul_init; 198 red_data[2].f_fini = NULL; // no destructors needed 199 red_data[2].f_comb = (void*)&__red_dbl_mul_comb; 200 red_data[2].flags = FLG; 201 int gtid = __kmpc_global_thread_num(NULL); 202 void* tg1 = __kmpc_task_reduction_init(gtid, 3, red_data); 203 204 for( int l = 0; l < N; l += 2 ) { 205 // 2 iterations per task to get correct x value; actually any even 206 // number of iters per task will work, otherwise x looses precision 207 #pragma omp task firstprivate(l) //in_reduction(+:i) in_reduction(*:x) 208 { 209 int gtid = __kmpc_global_thread_num(NULL); 210 int *p_ip = (int*)__kmpc_task_reduction_get_th_data(gtid, tg1, &ip); 211 double *p_xp = (double*)__kmpc_task_reduction_get_th_data( 212 gtid, tg1, &xp); 213 if (!ptrs[gtid]) ptrs[gtid] = p_xp; 214 215 // user's pseudo-code ============================== 216 *p_ip += l; 217 *p_xp *= (l + 1); 218 219 *p_ip += l + 1; 220 *p_xp *= 1.0 / (l + 2); 221 // ================================================== 222 } 223 } 224 // inner taskgroup reduces (i,k,y), i is same object as in outer one 225 #pragma omp taskgroup // task_reduction(-:i,k) task_reduction(+:y) 226 { 227 _task_red_item_t red_data[3]; 228 red_data[0].shar = &ip; 229 red_data[0].size = sizeof(ip); 230 red_data[0].f_init = NULL; // RTL will zero thread-specific objects 231 red_data[0].f_fini = NULL; // no destructors needed 232 red_data[0].f_comb = (void*)&__red_int_add_comb; 233 red_data[0].flags = FLG; 234 red_data[1].shar = &kp; 235 red_data[1].size = sizeof(kp); 236 red_data[1].f_init = NULL; // RTL will zero thread-specific objects 237 red_data[1].f_fini = NULL; // no destructors needed 238 red_data[1].f_comb = (void*)&__red_llong_add_comb; // same for + and - 239 red_data[1].flags = FLG; 240 red_data[2].shar = &yp; 241 red_data[2].size = sizeof(yp); 242 red_data[2].f_init = NULL; // RTL will zero thread-specific objects 243 red_data[2].f_fini = NULL; // no destructors needed 244 red_data[2].f_comb = (void*)&__red_dbl_add_comb; 245 red_data[2].flags = FLG; 246 int gtid = __kmpc_global_thread_num(NULL); 247 void* tg2 = __kmpc_task_reduction_init(gtid, 3, red_data); 248 249 for( int l = 0; l < N; l += 2 ) { 250 #pragma omp task firstprivate(l) 251 // in_reduction(+:j,y) in_reduction(*:x) in_reduction(-:k) 252 { 253 int gtid = __kmpc_global_thread_num(NULL); 254 long long *p_jp = (long long*)__kmpc_task_reduction_get_th_data( 255 gtid, tg1, &jp); 256 long long *p_kp = (long long*)__kmpc_task_reduction_get_th_data( 257 gtid, tg2, &kp); 258 double *p_xp = (double*)__kmpc_task_reduction_get_th_data( 259 gtid, tg1, &xp); 260 double *p_yp = (double*)__kmpc_task_reduction_get_th_data( 261 gtid, tg2, &yp); 262 // user's pseudo-code ============================== 263 *p_jp += l; 264 *p_kp -= l; 265 *p_yp += (double)l; 266 *p_xp *= (l + 1); 267 268 *p_jp += l + 1; 269 *p_kp -= l + 1; 270 *p_yp += (double)(l + 1); 271 *p_xp *= 1.0 / (l + 2); 272 // ================================================= 273 { 274 // the following code is here just to check __kmpc_task_reduction_get_th_data: 275 int tid = omp_get_thread_num(); 276 void *addr1; 277 void *addr2; 278 addr1 = __kmpc_task_reduction_get_th_data(gtid, tg1, &xp); // from shared 279 addr2 = __kmpc_task_reduction_get_th_data(gtid, tg1, addr1); // from private 280 if (addr1 != addr2) { 281 #pragma omp atomic 282 ++err; 283 printf("Wrong thread-specific addresses %d s:%p p:%p\n", tid, addr1, addr2); 284 } 285 // from neighbour w/o taskgroup (should start lookup from current tg2) 286 if (tid > 0) { 287 if (ptrs[tid-1]) { 288 addr2 = __kmpc_task_reduction_get_th_data(gtid, NULL, ptrs[tid-1]); 289 if (addr1 != addr2) { 290 #pragma omp atomic 291 ++err; 292 printf("Wrong thread-specific addresses %d s:%p n:%p\n", 293 tid, addr1, addr2); 294 } 295 } 296 } else { 297 if (ptrs[nthreads-1]) { 298 addr2 = __kmpc_task_reduction_get_th_data(gtid, NULL, ptrs[nthreads-1]); 299 if (addr1 != addr2) { 300 #pragma omp atomic 301 ++err; 302 printf("Wrong thread-specific addresses %d s:%p n:%p\n", 303 tid, addr1, addr2); 304 } 305 } 306 } 307 // ---------------------------------------------- 308 } 309 } 310 #pragma omp task firstprivate(l) 311 // in_reduction(+:y) in_reduction(-:i,k) 312 { 313 int gtid = __kmpc_global_thread_num(NULL); 314 int *p_ip = (int*)__kmpc_task_reduction_get_th_data( 315 gtid, tg2, &ip); 316 long long *p_kp = (long long*)__kmpc_task_reduction_get_th_data( 317 gtid, tg2, &kp); 318 double *p_yp = (double*)__kmpc_task_reduction_get_th_data( 319 gtid, tg2, &yp); 320 321 // user's pseudo-code ============================== 322 *p_ip -= l; 323 *p_kp -= l; 324 *p_yp += (double)l; 325 326 *p_ip -= l + 1; 327 *p_kp -= l + 1; 328 *p_yp += (double)(l + 1); 329 // ================================================= 330 } 331 #pragma omp task firstprivate(l) 332 // in_reduction(+:j) in_reduction(*:x) 333 { 334 int gtid = __kmpc_global_thread_num(NULL); 335 long long *p_jp = (long long*)__kmpc_task_reduction_get_th_data( 336 gtid, tg1, &jp); 337 double *p_xp = (double*)__kmpc_task_reduction_get_th_data( 338 gtid, tg1, &xp); 339 // user's pseudo-code ============================== 340 *p_jp += l; 341 *p_xp *= (l + 1); 342 343 *p_jp += l + 1; 344 *p_xp *= 1.0 / (l + 2); 345 // ================================================= 346 } 347 } 348 } // inner reduction 349 350 for( int l = 0; l < N; l += 2 ) { 351 #pragma omp task firstprivate(l) // in_reduction(+:j) 352 { 353 int gtid = __kmpc_global_thread_num(NULL); 354 long long *p_jp = (long long*)__kmpc_task_reduction_get_th_data( 355 gtid, tg1, &jp); 356 // user's pseudo-code ============================== 357 *p_jp += l; 358 *p_jp += l + 1; 359 // ================================================= 360 } 361 } 362 } // outer reduction 363 } // end single 364 } // end parallel 365 // check results 366 #if _DEBUG 367 printf("reduction flags = %u\n", FLG); 368 #endif 369 if (ip == is && jp == js && ks == kp && 370 fabs(xp - xs) < 0.01 && fabs(yp - ys) < 0.01) 371 printf("passed\n"); 372 else 373 printf("failed,\n ser:(%d %lld %f %lld %f)\n par:(%d %lld %f %lld %f)\n", 374 is, js, xs, ks, ys, 375 ip, jp, xp, kp, yp); 376 return 0; 377 } 378