Skip to content

Commit 23cc672

Browse files
committed
fixed parallel_for for generic step logic, increased worker chase-lev queue size, minor formatting changes
1 parent f109e44 commit 23cc672

3 files changed

Lines changed: 48 additions & 26 deletions

File tree

fdaPDE/src/execution/concurrency.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ inline std::size_t available_concurrency() noexcept {
5151
// SLURM
5252
if (auto SLURM_NCPUS = internals::get_env_concurrency_count("SLURM_CPUS_ON_NODE")) { return *SLURM_NCPUS; }
5353
// check if OpenMP has been configured
54-
if (auto OMP_NUM_THREADS = internals::get_env_concurrency_count("OMP_NUM_THREADS")) {
55-
if (*OMP_NUM_THREADS > 1) return *OMP_NUM_THREADS;
54+
if (auto OMP_NCPUS = internals::get_env_concurrency_count("OMP_NUM_THREADS")) {
55+
if (*OMP_NCPUS > 1) return *OMP_NCPUS;
5656
}
5757

5858
// second, check OS-specific settings

fdaPDE/src/execution/parallel_for.h

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,44 +26,66 @@ namespace internals {
2626
struct task_parallel_for {
2727
task_parallel_for() = default;
2828

29-
template <typename LoopBody, typename NextFunctor>
30-
requires(std::is_invocable_v<LoopBody, int> && std::is_invocable_r_v<int, NextFunctor, int>)
31-
void run(threaded_executor_impl* executor, int begin, int end, int grain_size, LoopBody&& f, NextFunctor&& next) {
32-
int n = 0;
33-
for (int i = begin; i < end; i = next(i), n++);
34-
if (n <= 0) return; // nothing to loop on
35-
36-
grain_size = std::max(1, std::min(grain_size, n));
37-
dispatch_(executor, begin, end, grain_size, std::forward<LoopBody>(f), std::forward<NextFunctor>(next));
38-
return;
39-
}
29+
// optimized for loop with standard ++i increment
4030
template <typename LoopBody>
4131
requires(std::is_invocable_v<LoopBody, int>)
4232
void run(threaded_executor_impl* executor, int begin, int end, int grain_size, LoopBody&& f) {
43-
const int n = end - begin;
44-
if (n <= 0) return; // nothing to loop on
33+
const int size = end - begin;
34+
if (size <= 0) return; // nothing to loop on
35+
36+
grain_size = std::max(1, std::min(grain_size, size));
37+
std::atomic<int> local_task_count {1};
38+
39+
for (int local_begin = begin; local_begin < end; local_begin += grain_size) {
40+
// register task in the group
41+
local_task_count.fetch_add(1, std::memory_order_release);
4542

46-
grain_size = std::max(1, std::min(grain_size, n));
47-
dispatch_(executor, begin, end, grain_size, std::forward<LoopBody>(f), [](int i) { return ++i; });
43+
int local_end = ((end - local_begin) < grain_size) ? end : (local_begin + grain_size);
44+
auto loop_body = [local_begin, local_end, &f, &local_task_count]() {
45+
for (int it = local_begin; it < local_end; ++it) { f(it); }
46+
// signal task completion
47+
local_task_count.fetch_sub(1, std::memory_order_release);
48+
};
49+
executor->execute(std::move(loop_body));
50+
}
51+
// task_group collaborative wait
52+
local_task_count.fetch_sub(1, std::memory_order_release);
53+
executor->active_join(this_thread_id(), [&] {
54+
// help the pool while the task group is not fully consumed
55+
return local_task_count.load(std::memory_order_acquire) > 0;
56+
});
4857
return;
4958
}
50-
private:
59+
// for loop with custom step logic
5160
template <typename LoopBody, typename NextFunctor>
52-
void
53-
dispatch_(threaded_executor_impl* executor, int begin, int end, int grain_size, LoopBody&& f, NextFunctor&& next) {
61+
requires(std::is_invocable_v<LoopBody, int> && std::is_invocable_r_v<int, NextFunctor, int>)
62+
void run(threaded_executor_impl* executor, int begin, int end, int grain_size, LoopBody&& f, NextFunctor&& next) {
63+
int size = 0;
64+
for (int i = begin; i < end; i = next(i), size++);
65+
if (size <= 0) return; // nothing to loop on
66+
67+
grain_size = std::max(1, std::min(grain_size, size));
5468
std::atomic<int> local_task_count {1};
69+
int n_batches = std::ceil(double(size) / grain_size);
5570

56-
for (int j = begin; j < end; j += grain_size) {
71+
int local_begin = begin;
72+
int local_end = local_begin;
73+
for (int i = 0; i < grain_size && local_end < end; ++i) { local_end = next(local_end); }
74+
75+
for (int j = 0; j < n_batches; j++) {
5776
// register task in the group
5877
local_task_count.fetch_add(1, std::memory_order_release);
59-
60-
int k = ((end - j) < grain_size) ? end : (j + grain_size);
61-
auto loop_body = [j, k, next, &f, &local_task_count]() {
62-
for (int it = j; it < k; it = next(it)) { f(it); }
78+
auto loop_body = [local_begin, local_end, next, &f, &local_task_count]() {
79+
for (int it = local_begin; it < local_end; it = next(it)) { f(it); }
6380
// signal task completion
6481
local_task_count.fetch_sub(1, std::memory_order_release);
6582
};
6683
executor->execute(std::move(loop_body));
84+
85+
// update next task range
86+
local_begin = local_end;
87+
local_end = local_begin;
88+
for (int i = 0; i < grain_size && local_end < end; ++i) { local_end = next(local_end); }
6789
}
6890
// task_group collaborative wait
6991
local_task_count.fetch_sub(1, std::memory_order_release);

fdaPDE/src/execution/worker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ struct worker {
314314
using task_const_pointer = typename allocator_type::const_pointer;
315315
using task_queue_type = chase_lev_queue<task_pointer>;
316316
using task_buffer_type = mpsc_queue<task_pointer>;
317-
static constexpr int task_queue_size = 4096;
317+
static constexpr int task_queue_size = 8192; // 2^13
318318

319319
// constructor
320320
template <typename Executor>

0 commit comments

Comments
 (0)