Line | Branch | Exec | Source |
---|---|---|---|
1 | #pragma once | ||
2 | |||
3 | #include <omp.h> | ||
4 | |||
5 | #include <algorithm> | ||
6 | #include <chrono> | ||
7 | #include <core/util/include/util.hpp> | ||
8 | #include <cstdint> | ||
9 | #include <cstdlib> | ||
10 | #include <exception> | ||
11 | #include <fstream> | ||
12 | #include <functional> | ||
13 | #include <iomanip> | ||
14 | #include <iostream> | ||
15 | #include <memory> | ||
16 | #include <sstream> | ||
17 | #include <stdexcept> | ||
18 | #include <string> | ||
19 | #include <vector> | ||
20 | |||
21 | namespace ppc::core { | ||
22 | |||
23 | /// @brief Represents the type of task (parallelization technology). | ||
24 | /// @details Used to select the implementation type in tests and execution logic. | ||
25 | enum TypeOfTask : uint8_t { | ||
26 | /// Use all available implementations | ||
27 | kALL, | ||
28 | /// MPI (Message Passing Interface) | ||
29 | kMPI, | ||
30 | /// OpenMP (Open Multi-Processing) | ||
31 | kOMP, | ||
32 | /// Sequential implementation | ||
33 | kSEQ, | ||
34 | /// Standard Thread Library (STL threads) | ||
35 | kSTL, | ||
36 | /// Intel Threading Building Blocks (TBB) | ||
37 | kTBB, | ||
38 | /// Unknown task type | ||
39 | kUnknown | ||
40 | }; | ||
41 | |||
42 | /// @brief Indicates whether a task is enabled or disabled. | ||
43 | enum StatusOfTask : uint8_t { | ||
44 | /// Task is enabled and should be executed | ||
45 | kEnabled, | ||
46 | /// Task is disabled and will be skipped | ||
47 | kDisabled | ||
48 | }; | ||
49 | |||
50 | /// @brief Returns a string representation of the task status. | ||
51 | /// @param status_of_task Task status (enabled or disabled). | ||
52 | /// @return "enabled" if the task is enabled, otherwise "disabled". | ||
53 | inline std::string GetStringTaskStatus(StatusOfTask status_of_task) { | ||
54 | if (status_of_task == kDisabled) { | ||
55 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | return "disabled"; |
56 | } | ||
57 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | return "enabled"; |
58 | } | ||
59 | |||
60 | /// @brief Returns a string representation of the task type based on the JSON settings file. | ||
61 | /// @param type_of_task Type of the task. | ||
62 | /// @param settings_file_path Path to the JSON file containing task type strings. | ||
63 | /// @return Formatted string combining the task type and its corresponding value from the file. | ||
64 | /// @throws std::runtime_error If the file cannot be opened. | ||
65 | 718 | inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string &settings_file_path) { | |
66 | 718 | std::ifstream file(settings_file_path); | |
67 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 688 times.
|
718 | if (!file.is_open()) { |
68 |
2/4✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
|
60 | throw std::runtime_error("Failed to open " + settings_file_path); |
69 | } | ||
70 | |||
71 | auto list_settings = ppc::util::InitJSONPtr(); | ||
72 |
2/2✓ Branch 1 taken 678 times.
✓ Branch 2 taken 10 times.
|
688 | file >> *list_settings; |
73 | |||
74 | 658 | auto to_type_str = [&](const std::string &type) -> std::string { | |
75 |
3/6✓ Branch 2 taken 658 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 648 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 648 times.
✗ Branch 9 not taken.
|
3260 | return type + "_" + std::string((*list_settings)["tasks"][type]); |
76 |
7/7✓ Branch 0 taken 94 times.
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 94 times.
✓ Branch 3 taken 94 times.
✓ Branch 4 taken 94 times.
✓ Branch 5 taken 188 times.
✓ Branch 6 taken 20 times.
|
678 | }; |
77 | |||
78 | if (type_of_task == TypeOfTask::kALL) { | ||
79 |
2/4✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
|
188 | return to_type_str("all"); |
80 | } | ||
81 | if (type_of_task == TypeOfTask::kSTL) { | ||
82 |
2/4✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
|
188 | return to_type_str("stl"); |
83 | } | ||
84 | if (type_of_task == TypeOfTask::kOMP) { | ||
85 |
2/4✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
|
188 | return to_type_str("omp"); |
86 | } | ||
87 | if (type_of_task == TypeOfTask::kMPI) { | ||
88 |
2/4✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
|
188 | return to_type_str("mpi"); |
89 | } | ||
90 | if (type_of_task == TypeOfTask::kTBB) { | ||
91 |
2/4✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
|
188 | return to_type_str("tbb"); |
92 | } | ||
93 | if (type_of_task == TypeOfTask::kSEQ) { | ||
94 |
3/4✓ Branch 1 taken 188 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✓ Branch 5 taken 10 times.
|
366 | return to_type_str("seq"); |
95 | } | ||
96 |
2/4✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
|
40 | return "unknown"; |
97 | 718 | } | |
98 | |||
99 | enum StateOfTesting : uint8_t { kFunc, kPerf }; | ||
100 | |||
101 | template <typename InType, typename OutType> | ||
102 | /// @brief Base abstract class representing a generic task with a defined pipeline. | ||
103 | /// @tparam InType Input data type. | ||
104 | /// @tparam OutType Output data type. | ||
105 | class Task { | ||
106 | public: | ||
107 | /// @brief Constructs a new Task object. | ||
108 |
2/4✓ Branch 1 taken 466 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 466 times.
|
596 | explicit Task(StateOfTesting /*state_of_testing*/ = StateOfTesting::kFunc) { functions_order_.clear(); } |
109 | |||
110 | /// @brief Validates input data and task attributes before execution. | ||
111 | /// @return True if validation is successful. | ||
112 | 796 | virtual bool Validation() final { | |
113 |
1/2✓ Branch 2 taken 566 times.
✗ Branch 3 not taken.
|
796 | InternalOrderTest(ppc::util::FuncName()); |
114 | 796 | return ValidationImpl(); | |
115 | } | ||
116 | |||
117 | /// @brief Performs preprocessing on the input data. | ||
118 | /// @return True if preprocessing is successful. | ||
119 | 796 | virtual bool PreProcessing() final { | |
120 |
1/2✓ Branch 2 taken 566 times.
✗ Branch 3 not taken.
|
796 | InternalOrderTest(ppc::util::FuncName()); |
121 |
2/2✓ Branch 0 taken 416 times.
✓ Branch 1 taken 150 times.
|
796 | if (state_of_testing_ == StateOfTesting::kFunc) { |
122 |
1/2✓ Branch 2 taken 416 times.
✗ Branch 3 not taken.
|
992 | InternalTimeTest(ppc::util::FuncName()); |
123 | } | ||
124 | 796 | return PreProcessingImpl(); | |
125 | } | ||
126 | |||
127 | /// @brief Executes the main logic of the task. | ||
128 | /// @return True if execution is successful. | ||
129 | 956 | virtual bool Run() final { | |
130 |
1/2✓ Branch 2 taken 646 times.
✗ Branch 3 not taken.
|
956 | InternalOrderTest(ppc::util::FuncName()); |
131 | 956 | return RunImpl(); | |
132 | } | ||
133 | |||
134 | /// @brief Performs postprocessing on the output data. | ||
135 | /// @return True if postprocessing is successful. | ||
136 | 796 | virtual bool PostProcessing() final { | |
137 |
1/2✓ Branch 2 taken 566 times.
✗ Branch 3 not taken.
|
796 | InternalOrderTest(ppc::util::FuncName()); |
138 |
2/2✓ Branch 0 taken 416 times.
✓ Branch 1 taken 150 times.
|
796 | if (state_of_testing_ == StateOfTesting::kFunc) { |
139 |
2/2✓ Branch 2 taken 406 times.
✓ Branch 3 taken 10 times.
|
972 | InternalTimeTest(ppc::util::FuncName()); |
140 | } | ||
141 | 776 | return PostProcessingImpl(); | |
142 | } | ||
143 | |||
144 | /// @brief Returns the current testing mode. | ||
145 | /// @return Reference to the current StateOfTesting. | ||
146 | StateOfTesting &GetStateOfTesting() { return state_of_testing_; } | ||
147 | |||
148 | /// @brief Sets the dynamic task type. | ||
149 | /// @param type_of_task Task type to set. | ||
150 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
346 | void SetTypeOfTask(TypeOfTask type_of_task) { type_of_task_ = type_of_task; } |
151 | |||
152 | /// @brief Returns the dynamic task type. | ||
153 | /// @return Current dynamic task type. | ||
154 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | [[nodiscard]] TypeOfTask GetDynamicTypeOfTask() const { return type_of_task_; } |
155 | |||
156 | /// @brief Returns the current task status. | ||
157 | /// @return Task status (enabled or disabled). | ||
158 | [[nodiscard]] StatusOfTask GetStatusOfTask() const { return status_of_task_; } | ||
159 | |||
160 | /// @brief Returns the static task type. | ||
161 | /// @return Static task type (default: kUnknown). | ||
162 | static constexpr TypeOfTask GetStaticTypeOfTask() { return TypeOfTask::kUnknown; } | ||
163 | |||
164 | /// @brief Returns a reference to the input data. | ||
165 | /// @return Reference to the task's input data. | ||
166 |
5/10✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 30 times.
✗ Branch 14 not taken.
|
120 | InType &GetInput() { return input_; } |
167 | |||
168 | /// @brief Returns a reference to the output data. | ||
169 | /// @return Reference to the task's output data. | ||
170 |
3/6✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
|
366 | OutType &GetOutput() { return output_; } |
171 | |||
172 | /// @brief Destructor. Verifies that the pipeline was executed in the correct order. | ||
173 | /// @note Terminates the program if pipeline order is incorrect or incomplete. | ||
174 | 932 | virtual ~Task() { | |
175 |
2/4✓ Branch 0 taken 466 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 466 times.
✗ Branch 3 not taken.
|
932 | if (!functions_order_.empty() || !was_worked_) { |
176 | ✗ | std::cerr << "ORDER OF FUNCTIONS IS NOT RIGHT! \n Expected - \"Validation\", \"PreProcessing\", \"Run\", " | |
177 | "\"PostProcessing\" \n"; | ||
178 | ✗ | std::terminate(); | |
179 | } else { | ||
180 | functions_order_.clear(); | ||
181 | } | ||
182 | #if _OPENMP >= 201811 | ||
183 | omp_pause_resource_all(omp_pause_soft); | ||
184 | #endif | ||
185 |
1/2✓ Branch 0 taken 466 times.
✗ Branch 1 not taken.
|
1864 | } |
186 | |||
187 | protected: | ||
188 | /// @brief Verifies the correct order of pipeline method calls. | ||
189 | /// @param str Name of the method being called. | ||
190 | 3344 | virtual void InternalOrderTest(const std::string &str) final { | |
191 | 3344 | functions_order_.push_back(str); | |
192 |
3/4✓ Branch 0 taken 566 times.
✓ Branch 1 taken 1778 times.
✓ Branch 2 taken 566 times.
✗ Branch 3 not taken.
|
3344 | if (str == "PostProcessing" && IsFullPipelineStage()) { |
193 | functions_order_.clear(); | ||
194 | } else { | ||
195 | 2548 | was_worked_ = true; | |
196 | } | ||
197 | 3344 | } | |
198 | |||
199 | /// @brief Measures execution time between preprocessing and postprocessing steps. | ||
200 | /// @param str Name of the method being timed. | ||
201 | /// @throws std::runtime_error If execution exceeds the allowed time limit. | ||
202 | 992 | virtual void InternalTimeTest(const std::string &str) final { | |
203 |
2/2✓ Branch 0 taken 416 times.
✓ Branch 1 taken 416 times.
|
992 | if (str == "PreProcessing") { |
204 | 496 | tmp_time_point_ = std::chrono::high_resolution_clock::now(); | |
205 | } | ||
206 | |||
207 |
2/2✓ Branch 0 taken 416 times.
✓ Branch 1 taken 416 times.
|
992 | if (str == "PostProcessing") { |
208 | 496 | auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - | |
209 | tmp_time_point_) | ||
210 | .count(); | ||
211 | 496 | auto diff = static_cast<double>(duration) * 1e-9; | |
212 | |||
213 | 496 | std::stringstream err_msg; | |
214 |
2/2✓ Branch 0 taken 406 times.
✓ Branch 1 taken 10 times.
|
496 | if (diff < kMaxTestTime) { |
215 |
1/2✓ Branch 1 taken 406 times.
✗ Branch 2 not taken.
|
476 | err_msg << "Test time:" << std::fixed << std::setprecision(10) << diff << '\n'; |
216 | } else { | ||
217 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | err_msg << "\nTask execute time need to be: "; |
218 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | err_msg << "time < " << kMaxTestTime << " secs.\n"; |
219 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | err_msg << "Original time in secs: " << diff << '\n'; |
220 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
60 | throw std::runtime_error(err_msg.str().c_str()); |
221 | } | ||
222 | 496 | } | |
223 | 972 | } | |
224 | |||
225 | /// @brief User-defined validation logic. | ||
226 | /// @return True if validation is successful. | ||
227 | virtual bool ValidationImpl() = 0; | ||
228 | |||
229 | /// @brief User-defined preprocessing logic. | ||
230 | /// @return True if preprocessing is successful. | ||
231 | virtual bool PreProcessingImpl() = 0; | ||
232 | |||
233 | /// @brief User-defined task execution logic. | ||
234 | /// @return True if run is successful. | ||
235 | virtual bool RunImpl() = 0; | ||
236 | |||
237 | /// @brief User-defined postprocessing logic. | ||
238 | /// @return True if postprocessing is successful. | ||
239 | virtual bool PostProcessingImpl() = 0; | ||
240 | |||
241 | private: | ||
242 | InType input_; | ||
243 | OutType output_; | ||
244 | StateOfTesting state_of_testing_ = kFunc; | ||
245 | TypeOfTask type_of_task_ = kUnknown; | ||
246 | StatusOfTask status_of_task_ = kEnabled; | ||
247 | std::vector<std::string> functions_order_; | ||
248 | std::vector<std::string> right_functions_order_ = {"Validation", "PreProcessing", "Run", "PostProcessing"}; | ||
249 | static constexpr double kMaxTestTime = 1.0; | ||
250 | std::chrono::high_resolution_clock::time_point tmp_time_point_; | ||
251 | bool was_worked_ = false; | ||
252 | |||
253 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
|
566 | bool IsFullPipelineStage() { |
254 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
|
566 | if (functions_order_.size() < 4) { |
255 | return false; | ||
256 | } | ||
257 | |||
258 | 566 | auto it = std::adjacent_find(functions_order_.begin() + 2, | |
259 | functions_order_.begin() + static_cast<long>(functions_order_.size() - 2), | ||
260 | std::not_equal_to<>()); | ||
261 | |||
262 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
|
566 | return (functions_order_[0] == "Validation" && functions_order_[1] == "PreProcessing" && |
263 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 336 times.
|
566 | functions_order_[2] == "Run" && |
264 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
|
566 | it == (functions_order_.begin() + static_cast<long>(functions_order_.size() - 2)) && |
265 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
|
566 | functions_order_[functions_order_.size() - 1] == "PostProcessing"); |
266 | } | ||
267 | }; | ||
268 | |||
269 | /// @brief Smart pointer alias for Task. | ||
270 | /// @tparam InType Input data type. | ||
271 | /// @tparam OutType Output data type. | ||
272 | template <typename InType, typename OutType> | ||
273 | using TaskPtr = std::shared_ptr<Task<InType, OutType>>; | ||
274 | |||
275 | /// @brief Constructs and returns a shared pointer to a task with the given input. | ||
276 | /// @tparam TaskType Type of the task to create. | ||
277 | /// @tparam InType Type of the input. | ||
278 | /// @param in Input to pass to the task constructor. | ||
279 | /// @return Shared pointer to the newly created task. | ||
280 | template <typename TaskType, typename InType> | ||
281 | 336 | std::shared_ptr<TaskType> TaskGetter(InType in) { | |
282 | 336 | return std::make_shared<TaskType>(in); | |
283 | } | ||
284 | |||
285 | } // namespace ppc::core | ||
286 |