1 | // RUN: %libomp-cxx-compile -fopenmp-version=60 && %libomp-run |
2 | #include <stdio.h> |
3 | #include <omp.h> |
4 | #include <limits.h> |
5 | #include <complex.h> |
6 | #include <math.h> |
7 | #include "omp_testsuite.h" |
8 | |
9 | #define N 10 |
10 | class Sum { |
11 | int val; |
12 | |
13 | public: |
14 | Sum(int v = 0) : val(v) {} |
15 | Sum operator+(const Sum &rhs) const { return Sum(val + rhs.val); } |
16 | Sum &operator+=(const Sum &rhs) { |
17 | val += rhs.val; |
18 | return *this; |
19 | } |
20 | int getValue() const { return val; } |
21 | }; |
22 | |
23 | // Declare OpenMP reduction |
24 | #pragma omp declare reduction(sum_reduction:Sum : omp_out += omp_in) \ |
25 | initializer(omp_priv = Sum(0)) |
26 | |
27 | #pragma omp declare reduction(sum_pctor_reduction:Sum : omp_out += omp_in) \ |
28 | initializer(omp_priv = Sum(1)) // non-default ctor |
29 | |
30 | int checkUserDefinedReduction() { |
31 | Sum final_result_udr(0); |
32 | Sum final_result_udr_pctor(1); |
33 | Sum array_sum[N]; |
34 | int error_flag = 0; |
35 | int expected_value = 0; |
36 | int expected_value_pctor = 0; |
37 | for (int i = 0; i < N; ++i) { |
38 | array_sum[i] = Sum(i); |
39 | expected_value += i; // Calculate expected sum: 0 + 1 + ... + (N-1) |
40 | expected_value_pctor += i; |
41 | } |
42 | int num_threads_for_pctor_calc = 4; // num_threads(4) |
43 | int priv_initializer_val_pctor = 1; // initializer(omp_priv = Sum(1)) |
44 | expected_value_pctor += |
45 | num_threads_for_pctor_calc + priv_initializer_val_pctor; |
46 | #pragma omp parallel num_threads(4) private(final_result_udr) private( \ |
47 | final_result_udr_pctor) |
48 | { |
49 | #pragma omp for reduction(sum_reduction : final_result_udr) \ |
50 | reduction(sum_pctor_reduction : final_result_udr_pctor) |
51 | for (int i = 0; i < N; ++i) { |
52 | final_result_udr += array_sum[i]; |
53 | final_result_udr_pctor += array_sum[i]; |
54 | } |
55 | |
56 | if (final_result_udr.getValue() != expected_value || |
57 | final_result_udr_pctor.getValue() != expected_value_pctor) |
58 | error_flag += 1; |
59 | } |
60 | return error_flag; |
61 | } |
62 | void performMinMaxRed(int &min_val, int &max_val) { |
63 | int input_data[] = {7, 3, 12, 5, 8}; |
64 | int n_size = sizeof(input_data) / sizeof(input_data[0]); |
65 | min_val = INT_MAX; |
66 | max_val = INT_MIN; |
67 | #pragma omp for reduction(original(private), min : min_val) \ |
68 | reduction(original(private), max : max_val) |
69 | for (int i = 0; i < n_size; ++i) { |
70 | if (input_data[i] < min_val) |
71 | min_val = input_data[i]; |
72 | if (input_data[i] > max_val) |
73 | max_val = input_data[i]; |
74 | } |
75 | } |
76 | std::complex<double> doComplexReduction(std::complex<double> *arr) { |
77 | std::complex<double> result(1, 0); |
78 | |
79 | #pragma omp declare reduction(* : std::complex<double> : omp_out *= omp_in) \ |
80 | initializer(omp_priv = std::complex<double>(1, 0)) |
81 | |
82 | #pragma omp for reduction(original(private), * : result) |
83 | for (int i = 0; i < N; ++i) |
84 | result *= arr[i]; |
85 | |
86 | return result; |
87 | } |
88 | |
89 | void performReductions(int n_elements, const int *input_values, |
90 | int &sum_val_out, int &prod_val_out, |
91 | float &float_sum_val_out) { |
92 | // private variables for this thread's reduction. |
93 | sum_val_out = 0; |
94 | prod_val_out = 1; |
95 | float_sum_val_out = 0.0f; |
96 | |
97 | const float kPiValue = 3.14f; |
98 | #pragma omp for reduction(original(private), + : sum_val_out) \ |
99 | reduction(original(private), * : prod_val_out) \ |
100 | reduction(original(private), + : float_sum_val_out) |
101 | for (int i = 0; i < n_elements; ++i) { |
102 | sum_val_out += input_values[i]; |
103 | prod_val_out *= (i + 1); |
104 | float_sum_val_out += kPiValue; |
105 | } |
106 | } |
107 | int main(void) { |
108 | int input_array[N]; |
109 | int total_errors = 0; |
110 | const float kPiVal = 3.14f; |
111 | const int kExpectedSum = 45; // Sum of 0..9 |
112 | const int kExpectedProd = 3628800; // 10! |
113 | const float kExpectedFsum = 31.400000f; // 3.14f * 10 |
114 | const float kTolerance = 1e-4f; |
115 | const int kExpectedMin = 3; |
116 | const int kExpectedMax = 12; |
117 | std::complex<double> arr[N]; |
118 | std::complex<double> kExpectedComplex(1, 0); |
119 | // Initialize the array |
120 | for (int i = 1; i <= N; ++i) { |
121 | arr[i - 1] = std::complex<double>( |
122 | 1.0 + 0.1 * i, 0.5 * i); // Avoid zero to prevent multiplication by zero |
123 | kExpectedComplex *= arr[i - 1]; |
124 | } |
125 | |
126 | for (int i = 0; i < N; i++) |
127 | input_array[i] = i; |
128 | #pragma omp parallel num_threads(4) |
129 | { |
130 | |
131 | int t_sum_v; |
132 | int t_prod_v; |
133 | float t_fsum_v; |
134 | performReductions(N, input_values: input_array, sum_val_out&: t_sum_v, prod_val_out&: t_prod_v, float_sum_val_out&: t_fsum_v); |
135 | if (t_sum_v != kExpectedSum) |
136 | total_errors++; |
137 | if (t_prod_v != kExpectedProd) |
138 | total_errors++; |
139 | if (std::abs(x: t_fsum_v - kExpectedFsum) > kTolerance) |
140 | total_errors++; |
141 | } |
142 | #pragma omp parallel num_threads(4) |
143 | { |
144 | int t_min_v; |
145 | int t_max_v; |
146 | performMinMaxRed(min_val&: t_min_v, max_val&: t_max_v); |
147 | if (t_min_v != kExpectedMin) |
148 | total_errors++; |
149 | if (t_max_v != kExpectedMax) |
150 | total_errors++; |
151 | } |
152 | total_errors += checkUserDefinedReduction(); |
153 | #pragma omp parallel num_threads(4) |
154 | { |
155 | std::complex<double> result(1, 0); |
156 | result = doComplexReduction(arr); |
157 | if (std::abs(x: result.real() - kExpectedComplex.real()) > 1e-6 || |
158 | std::abs(x: result.imag() - kExpectedComplex.imag()) > 1e-6) { |
159 | total_errors++; |
160 | } |
161 | } |
162 | if (total_errors != 0) |
163 | fprintf(stderr, format: "ERROR: reduction on private variable %d\n" , total_errors); |
164 | |
165 | return total_errors; |
166 | } |
167 | |