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