Skip to content

Commit 1836412

Browse files
committed
Enhance parallel execution support in integration tests
- Added checks for parallel execution support using std::execution. - Implemented fallback to sequential execution for platforms without parallel support. - Updated test functions to utilize parallel execution where available, ensuring compatibility across different environments.
1 parent a27e11e commit 1836412

File tree

1 file changed

+78
-13
lines changed

1 file changed

+78
-13
lines changed

test/integration/test_standard_parallelism.cpp

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
#include <iostream>
22
#include <vector>
33
#include <algorithm>
4-
#include <execution>
54
#include <cassert>
65
#include <chrono>
76
#include <functional>
87
#include <cmath>
8+
#include <numeric>
9+
10+
// Check for parallel execution support
11+
#if defined(__cpp_lib_execution) && __cpp_lib_execution >= 201902L
12+
#include <execution>
13+
#define PARALLEL_EXECUTION_AVAILABLE
14+
#endif
915

1016
/**
1117
* @brief Test standard library parallelism integration
@@ -58,16 +64,29 @@ void test_std_execution_multiple_conditions() {
5864
std::iota(indices.begin(), indices.end(), 0);
5965

6066
// Parallel execution using std::execution::par (no custom classes!)
67+
#ifdef PARALLEL_EXECUTION_AVAILABLE
6168
std::for_each(std::execution::par, indices.begin(), indices.end(),
62-
[&](size_t i) {
63-
auto ode_function = [&](double t, const std::vector<double>& y, std::vector<double>& dydt) {
69+
[&system, &states, &times, dt, steps](size_t i) {
70+
auto ode_function = [&system](double t, const std::vector<double>& y, std::vector<double>& dydt) {
6471
system(t, y, dydt);
6572
};
6673

67-
for (int step = 0; step < steps; ++step) {
74+
for (int j = 0; j < steps; ++j) {
6875
euler_step(ode_function, states[i], times[i], dt);
6976
}
7077
});
78+
#else
79+
// Sequential fallback for platforms without parallel execution support
80+
for (size_t i : indices) {
81+
auto ode_function = [&system](double t, const std::vector<double>& y, std::vector<double>& dydt) {
82+
system(t, y, dydt);
83+
};
84+
85+
for (int j = 0; j < steps; ++j) {
86+
euler_step(ode_function, states[i], times[i], dt);
87+
}
88+
}
89+
#endif
7190

7291
// Verify exponential decay
7392
double expected = 1.0 * std::exp(-system.decay_rate * steps * dt);
@@ -94,8 +113,9 @@ void test_parameter_sweep() {
94113
std::iota(indices.begin(), indices.end(), 0);
95114

96115
// Parallel parameter sweep using std::execution
116+
#ifdef PARALLEL_EXECUTION_AVAILABLE
97117
std::for_each(std::execution::par, indices.begin(), indices.end(),
98-
[&](size_t i) {
118+
[&decay_rates, &results, &times, &initial_state, dt, steps](size_t i) {
99119
results[i] = initial_state;
100120
times[i] = 0.0;
101121

@@ -104,10 +124,26 @@ void test_parameter_sweep() {
104124
dydt[0] = -decay_rate * y[0]; // Parameterized decay rate
105125
};
106126

107-
for (int step = 0; step < steps; ++step) {
127+
for (int j = 0; j < steps; ++j) {
108128
euler_step(ode_function, results[i], times[i], dt);
109129
}
110130
});
131+
#else
132+
// Sequential fallback for platforms without parallel execution support
133+
for (size_t i : indices) {
134+
results[i] = initial_state;
135+
times[i] = 0.0;
136+
137+
double decay_rate = decay_rates[i];
138+
auto ode_function = [decay_rate](double t, const std::vector<double>& y, std::vector<double>& dydt) {
139+
dydt[0] = -decay_rate * y[0]; // Parameterized decay rate
140+
};
141+
142+
for (int j = 0; j < steps; ++j) {
143+
euler_step(ode_function, results[i], times[i], dt);
144+
}
145+
}
146+
#endif
111147

112148
// Verify that different parameters give different results
113149
assert(results.size() == decay_rates.size());
@@ -140,21 +176,22 @@ void test_different_integrators() {
140176
std::iota(indices.begin(), indices.end(), 0);
141177

142178
// Compare different integration methods in parallel
179+
#ifdef PARALLEL_EXECUTION_AVAILABLE
143180
std::for_each(std::execution::par, indices.begin(), indices.end(),
144-
[&](size_t i) {
181+
[&system, &euler_results, &rk2_results, dt, steps](size_t i) {
145182
double time_euler = 0.0, time_rk2 = 0.0;
146183

147-
auto ode_function = [&](double t, const std::vector<double>& y, std::vector<double>& dydt) {
184+
auto ode_function = [&system](double t, const std::vector<double>& y, std::vector<double>& dydt) {
148185
system(t, y, dydt);
149186
};
150187

151188
// Euler method
152-
for (int step = 0; step < steps; ++step) {
189+
for (int j = 0; j < steps; ++j) {
153190
euler_step(ode_function, euler_results[i], time_euler, dt);
154191
}
155192

156193
// Simple RK2 (midpoint method)
157-
for (int step = 0; step < steps; ++step) {
194+
for (int k = 0; k < steps; ++k) {
158195
std::vector<double> k1(1), k2(1), temp_state(1);
159196
double temp_time = time_rk2;
160197

@@ -166,6 +203,34 @@ void test_different_integrators() {
166203
time_rk2 += dt;
167204
}
168205
});
206+
#else
207+
// Sequential fallback for platforms without parallel execution support
208+
for (size_t i : indices) {
209+
double time_euler = 0.0, time_rk2 = 0.0;
210+
211+
auto ode_function = [&system](double t, const std::vector<double>& y, std::vector<double>& dydt) {
212+
system(t, y, dydt);
213+
};
214+
215+
// Euler method
216+
for (int j = 0; j < steps; ++j) {
217+
euler_step(ode_function, euler_results[i], time_euler, dt);
218+
}
219+
220+
// Simple RK2 (midpoint method)
221+
for (int k = 0; k < steps; ++k) {
222+
std::vector<double> k1(1), k2(1), temp_state(1);
223+
double temp_time = time_rk2;
224+
225+
ode_function(temp_time, rk2_results[i], k1);
226+
temp_state[0] = rk2_results[i][0] + dt * k1[0] / 2.0;
227+
temp_time += dt / 2.0;
228+
ode_function(temp_time, temp_state, k2);
229+
rk2_results[i][0] += dt * k2[0];
230+
time_rk2 += dt;
231+
}
232+
}
233+
#endif
169234

170235
// Verify both methods give reasonable results
171236
assert(euler_results[0][0] > 0.1 && euler_results[0][0] < 1.0);
@@ -195,11 +260,11 @@ void test_openmp() {
195260
// OpenMP parallel loop - no custom classes needed!
196261
#pragma omp parallel for
197262
for (int i = 0; i < num_conditions; ++i) {
198-
auto ode_function = [&](double t, const std::vector<double>& y, std::vector<double>& dydt) {
263+
auto ode_function = [&system](double t, const std::vector<double>& y, std::vector<double>& dydt) {
199264
system(t, y, dydt);
200265
};
201266

202-
for (int step = 0; step < steps; ++step) {
267+
for (int j = 0; j < steps; ++j) {
203268
euler_step(ode_function, states[i], times[i], dt);
204269
}
205270
}
@@ -217,7 +282,7 @@ void test_hardware_detection() {
217282
std::cout << "Testing standard library hardware detection...\n";
218283

219284
// std::execution availability
220-
#ifdef __cpp_lib_execution
285+
#ifdef PARALLEL_EXECUTION_AVAILABLE
221286
bool std_execution_available = true;
222287
#else
223288
bool std_execution_available = false;

0 commit comments

Comments
 (0)