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