subpar
Substitutable parallelization for C++ libraries
Loading...
Searching...
No Matches
simple.hpp
Go to the documentation of this file.
1#ifndef SUBPAR_SIMPLE_HPP
2#define SUBPAR_SIMPLE_HPP
3
4#include "range.hpp"
5
6#ifndef SUBPAR_CUSTOM_PARALLELIZE_SIMPLE
7#include <vector>
8#include <stdexcept>
9#include <thread>
10#include <type_traits>
11#endif
12
18namespace subpar {
19
58template<bool nothrow_ = false, typename Task_, class Run_>
59void parallelize_simple(const Task_ num_tasks, const Run_ run_task) {
60#ifdef SUBPAR_CUSTOM_PARALLELIZE_SIMPLE
61 if constexpr(nothrow_) {
62#ifdef SUBPAR_CUSTOM_PARALLELIZE_SIMPLE_NOTHROW
63 SUBPAR_CUSTOM_PARALLELIZE_SIMPLE_NOTHROW(num_tasks, run_task);
64#else
65 SUBPAR_CUSTOM_PARALLELIZE_SIMPLE(num_tasks, run_task);
66#endif
67 } else {
68 SUBPAR_CUSTOM_PARALLELIZE_SIMPLE(num_tasks, run_task);
69 }
70
71#else
72 if (num_tasks <= 0) {
73 return;
74 } else if (num_tasks == 1) {
75 run_task(0);
76 return;
77 }
78
79 // Avoid instantiating a vector if it is known that the function can't throw.
80 auto errors = [&]{
81 if constexpr(nothrow_) {
82 return true;
83 } else {
84 return sanisizer::create<std::vector<std::exception_ptr> >(num_tasks);
85 }
86 }();
87
88#if defined(_OPENMP) && !defined(SUBPAR_NO_OPENMP_SIMPLE)
89#define SUBPAR_USES_OPENMP_SIMPLE 1
90
91 // OpenMP doesn't guarantee that we'll actually start 'num_tasks' workers,
92 // so we need to do a loop here to ensure that each task is executed.
93 #pragma omp parallel for num_threads(num_tasks)
94 for (Task_ w = 0; w < num_tasks; ++w) {
95 if constexpr(nothrow_) {
96 run_task(w);
97 } else {
98 try {
99 run_task(w);
100 } catch (...) {
101 errors[w] = std::current_exception();
102 }
103 }
104 }
105
106#else
107// Wiping it out, just in case.
108#undef SUBPAR_USES_OPENMP_SIMPLE
109
110 // We run the first job on the current thread, to avoid having to spin up an unnecessary worker.
111 std::vector<std::thread> workers;
112 sanisizer::reserve(workers, num_tasks - 1); // make sure we don't get alloc errors during emplace_back().
113
114 for (Task_ w = 1; w < num_tasks; ++w) {
115 if constexpr(nothrow_) {
116 workers.emplace_back(run_task, w);
117 } else {
118 workers.emplace_back([&run_task,&errors](int w) -> void {
119 try {
120 run_task(w);
121 } catch (...) {
122 errors[w] = std::current_exception();
123 }
124 }, w);
125 }
126 }
127
128 {
129 if constexpr(nothrow_) {
130 run_task(0);
131 } else {
132 try {
133 run_task(0);
134 } catch (...) {
135 errors[0] = std::current_exception();
136 }
137 }
138 }
139
140 for (auto& wrk : workers) {
141 wrk.join();
142 }
143#endif
144
145 if constexpr(!nothrow_) {
146 for (const auto& e : errors) {
147 if (e) {
148 std::rethrow_exception(e);
149 }
150 }
151 }
152#endif
153}
154
155}
156
157#endif
Substitutable parallelization functions.
void parallelize_simple(const Task_ num_tasks, const Run_ run_task)
Parallelize individual tasks across workers.
Definition simple.hpp:59
Parallelize across a range of tasks.