@@ -26,44 +26,66 @@ namespace internals {
2626struct 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);
0 commit comments