From 95965bef190b2e62ba4334c8616d2b0c2cbeff0a Mon Sep 17 00:00:00 2001 From: crn4tt Date: Thu, 4 Jun 2026 19:52:26 +0300 Subject: [PATCH 1/7] seq done --- .../seq/include/ops_seq.hpp | 31 +++++++ .../seq/src/ops_seq.cpp | 92 +++++++++++++++++++ .../settings.json | 1 + .../tests/functional/main.cpp | 7 +- .../tests/performance/main.cpp | 7 +- 5 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 tasks/egashin_k_radix_simple_merge/seq/include/ops_seq.hpp create mode 100644 tasks/egashin_k_radix_simple_merge/seq/src/ops_seq.cpp diff --git a/tasks/egashin_k_radix_simple_merge/seq/include/ops_seq.hpp b/tasks/egashin_k_radix_simple_merge/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..97e7d25e7e --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/seq/include/ops_seq.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace egashin_k_radix_simple_merge { + +class EgashinKRadixSimpleMergeSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + + explicit EgashinKRadixSimpleMergeSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static void CountingPass(const std::vector &source, std::vector &destination, int byte_index); + static void RadixSort(std::vector &data); + + OutType result_; +}; + +} // namespace egashin_k_radix_simple_merge diff --git a/tasks/egashin_k_radix_simple_merge/seq/src/ops_seq.cpp b/tasks/egashin_k_radix_simple_merge/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..c89fd1434d --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/seq/src/ops_seq.cpp @@ -0,0 +1,92 @@ +#include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" + +#include +#include +#include +#include +#include +#include + +#include "egashin_k_radix_simple_merge/common/include/common.hpp" + +namespace egashin_k_radix_simple_merge { + +namespace { + +constexpr uint64_t kSignBit = 0x8000000000000000ULL; + +} // namespace + +EgashinKRadixSimpleMergeSEQ::EgashinKRadixSimpleMergeSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = {}; +} + +bool EgashinKRadixSimpleMergeSEQ::ValidationImpl() { + return true; +} + +bool EgashinKRadixSimpleMergeSEQ::PreProcessingImpl() { + result_ = GetInput(); + return true; +} + +bool EgashinKRadixSimpleMergeSEQ::RunImpl() { + RadixSort(result_); + return true; +} + +bool EgashinKRadixSimpleMergeSEQ::PostProcessingImpl() { + GetOutput() = result_; + return true; +} + +void EgashinKRadixSimpleMergeSEQ::CountingPass(const std::vector &source, std::vector &destination, + int byte_index) { + std::array count{}; + + for (uint64_t value : source) { + const auto byte = static_cast((value >> (byte_index * 8)) & 0xFFU); + count.at(byte)++; + } + + std::array position{}; + for (size_t i = 1; i < count.size(); ++i) { + position.at(i) = position.at(i - 1) + count.at(i - 1); + } + + for (uint64_t value : source) { + const auto byte = static_cast((value >> (byte_index * 8)) & 0xFFU); + destination[position.at(byte)++] = value; + } +} + +void EgashinKRadixSimpleMergeSEQ::RadixSort(std::vector &data) { + if (data.size() < 2) { + return; + } + + std::vector keys(data.size()); + std::vector buffer(data.size()); + + for (size_t i = 0; i < data.size(); ++i) { + uint64_t bits = 0; + std::memcpy(&bits, &data[i], sizeof(data[i])); + keys[i] = ((bits & kSignBit) != 0U) ? ~bits : (bits ^ kSignBit); + } + + auto *source = &keys; + auto *destination = &buffer; + for (int byte_index = 0; byte_index < 8; ++byte_index) { + CountingPass(*source, *destination, byte_index); + std::swap(source, destination); + } + + for (size_t i = 0; i < data.size(); ++i) { + const uint64_t bits = (((*source)[i] & kSignBit) != 0U) ? ((*source)[i] ^ kSignBit) : ~(*source)[i]; + std::memcpy(&data[i], &bits, sizeof(data[i])); + } +} + +} // namespace egashin_k_radix_simple_merge diff --git a/tasks/egashin_k_radix_simple_merge/settings.json b/tasks/egashin_k_radix_simple_merge/settings.json index b2d4dd1477..34fb0f5637 100644 --- a/tasks/egashin_k_radix_simple_merge/settings.json +++ b/tasks/egashin_k_radix_simple_merge/settings.json @@ -1,5 +1,6 @@ { "tasks": { + "seq": "enabled", "stl": "enabled" }, "tasks_type": "threads" diff --git a/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp index 3526e31e8a..e7f8da3548 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp @@ -6,8 +6,10 @@ #include #include #include +#include #include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" #include "egashin_k_radix_simple_merge/stl/include/ops_stl.hpp" #include "util/include/func_test_util.hpp" #include "util/include/util.hpp" @@ -59,8 +61,9 @@ const std::array kTestParam = {{ "with_infinities"}, }}; -const auto kTestTasksList = - ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge); +const auto kTestTasksList = std::tuple_cat( + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); diff --git a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp index 7dd32c2e09..9ccc8b5666 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp @@ -3,8 +3,10 @@ #include #include #include +#include #include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" #include "egashin_k_radix_simple_merge/stl/include/ops_stl.hpp" #include "util/include/perf_test_util.hpp" @@ -42,8 +44,9 @@ TEST_P(EgashinKRunPerfTestsThreads, RunPerfModes) { namespace { -const auto kAllPerfTasks = - ppc::util::MakeAllPerfTasks(PPC_SETTINGS_egashin_k_radix_simple_merge); +const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_egashin_k_radix_simple_merge); const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); From 868f9c589b3fb24e552308665fd10e0245c5b982 Mon Sep 17 00:00:00 2001 From: crn4tt Date: Thu, 4 Jun 2026 19:53:06 +0300 Subject: [PATCH 2/7] omp done --- .../omp/include/ops_omp.hpp | 25 +++++++ .../omp/src/ops_omp.cpp | 67 +++++++++++++++++++ .../settings.json | 1 + .../tests/functional/main.cpp | 2 + .../tests/performance/main.cpp | 2 + 5 files changed, 97 insertions(+) create mode 100644 tasks/egashin_k_radix_simple_merge/omp/include/ops_omp.hpp create mode 100644 tasks/egashin_k_radix_simple_merge/omp/src/ops_omp.cpp diff --git a/tasks/egashin_k_radix_simple_merge/omp/include/ops_omp.hpp b/tasks/egashin_k_radix_simple_merge/omp/include/ops_omp.hpp new file mode 100644 index 0000000000..14ea426219 --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/omp/include/ops_omp.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace egashin_k_radix_simple_merge { + +class EgashinKRadixSimpleMergeOMP : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kOMP; + } + + explicit EgashinKRadixSimpleMergeOMP(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + OutType result_; +}; + +} // namespace egashin_k_radix_simple_merge diff --git a/tasks/egashin_k_radix_simple_merge/omp/src/ops_omp.cpp b/tasks/egashin_k_radix_simple_merge/omp/src/ops_omp.cpp new file mode 100644 index 0000000000..c345a925ac --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/omp/src/ops_omp.cpp @@ -0,0 +1,67 @@ +#include "egashin_k_radix_simple_merge/omp/include/ops_omp.hpp" + +#include +#include +#include + +#include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "egashin_k_radix_simple_merge/common/include/radix_utils.hpp" +#include "util/include/util.hpp" + +namespace egashin_k_radix_simple_merge { + +EgashinKRadixSimpleMergeOMP::EgashinKRadixSimpleMergeOMP(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = {}; +} + +bool EgashinKRadixSimpleMergeOMP::ValidationImpl() { + return true; +} + +bool EgashinKRadixSimpleMergeOMP::PreProcessingImpl() { + result_ = GetInput(); + return true; +} + +bool EgashinKRadixSimpleMergeOMP::RunImpl() { + if (result_.size() < 2) { + return true; + } + + int workers = radix_utils::WorkerCount(result_.size(), ppc::util::GetNumThreads()); + auto ranges = radix_utils::MakeRanges(result_.size(), workers); + auto &result = result_; + +#pragma omp parallel for default(none) shared(result, ranges, workers) num_threads(workers) schedule(static) + for (int i = 0; i < workers; ++i) { + radix_utils::SortRange(result, ranges[static_cast(i)].first, ranges[static_cast(i)].second); + } + + auto parts = radix_utils::MakeParts(result_, ranges); + while (parts.size() > 1) { + size_t pair_count = parts.size() / 2; + std::vector> next((parts.size() + 1) / 2); + +#pragma omp parallel for default(none) shared(parts, next, pair_count) num_threads(workers) schedule(static) + for (size_t i = 0; i < pair_count; ++i) { + next[i] = radix_utils::Merge(parts[2 * i], parts[(2 * i) + 1]); + } + + if (parts.size() % 2 != 0) { + next.back() = std::move(parts.back()); + } + parts = std::move(next); + } + + result_ = std::move(parts.front()); + return true; +} + +bool EgashinKRadixSimpleMergeOMP::PostProcessingImpl() { + GetOutput() = result_; + return true; +} + +} // namespace egashin_k_radix_simple_merge diff --git a/tasks/egashin_k_radix_simple_merge/settings.json b/tasks/egashin_k_radix_simple_merge/settings.json index 34fb0f5637..1a90b45554 100644 --- a/tasks/egashin_k_radix_simple_merge/settings.json +++ b/tasks/egashin_k_radix_simple_merge/settings.json @@ -1,5 +1,6 @@ { "tasks": { + "omp": "enabled", "seq": "enabled", "stl": "enabled" }, diff --git a/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp index e7f8da3548..beb7bbd086 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp @@ -9,6 +9,7 @@ #include #include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "egashin_k_radix_simple_merge/omp/include/ops_omp.hpp" #include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" #include "egashin_k_radix_simple_merge/stl/include/ops_stl.hpp" #include "util/include/func_test_util.hpp" @@ -63,6 +64,7 @@ const std::array kTestParam = {{ const auto kTestTasksList = std::tuple_cat( ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); diff --git a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp index 9ccc8b5666..917332ee6e 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp @@ -6,6 +6,7 @@ #include #include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "egashin_k_radix_simple_merge/omp/include/ops_omp.hpp" #include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" #include "egashin_k_radix_simple_merge/stl/include/ops_stl.hpp" #include "util/include/perf_test_util.hpp" @@ -45,6 +46,7 @@ TEST_P(EgashinKRunPerfTestsThreads, RunPerfModes) { namespace { const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( PPC_SETTINGS_egashin_k_radix_simple_merge); From 783d5d610cfefea7d56c33e410857343b5b0a704 Mon Sep 17 00:00:00 2001 From: crn4tt Date: Thu, 4 Jun 2026 19:53:43 +0300 Subject: [PATCH 3/7] tbb done --- .../settings.json | 3 +- .../tbb/include/ops_tbb.hpp | 25 +++++++ .../tbb/src/ops_tbb.cpp | 75 +++++++++++++++++++ .../tests/functional/main.cpp | 2 + .../tests/performance/main.cpp | 2 + 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 tasks/egashin_k_radix_simple_merge/tbb/include/ops_tbb.hpp create mode 100644 tasks/egashin_k_radix_simple_merge/tbb/src/ops_tbb.cpp diff --git a/tasks/egashin_k_radix_simple_merge/settings.json b/tasks/egashin_k_radix_simple_merge/settings.json index 1a90b45554..8a03e634e9 100644 --- a/tasks/egashin_k_radix_simple_merge/settings.json +++ b/tasks/egashin_k_radix_simple_merge/settings.json @@ -2,7 +2,8 @@ "tasks": { "omp": "enabled", "seq": "enabled", - "stl": "enabled" + "stl": "enabled", + "tbb": "enabled" }, "tasks_type": "threads" } diff --git a/tasks/egashin_k_radix_simple_merge/tbb/include/ops_tbb.hpp b/tasks/egashin_k_radix_simple_merge/tbb/include/ops_tbb.hpp new file mode 100644 index 0000000000..605cb39166 --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/tbb/include/ops_tbb.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace egashin_k_radix_simple_merge { + +class EgashinKRadixSimpleMergeTBB : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kTBB; + } + + explicit EgashinKRadixSimpleMergeTBB(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + OutType result_; +}; + +} // namespace egashin_k_radix_simple_merge diff --git a/tasks/egashin_k_radix_simple_merge/tbb/src/ops_tbb.cpp b/tasks/egashin_k_radix_simple_merge/tbb/src/ops_tbb.cpp new file mode 100644 index 0000000000..abb91720c7 --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/tbb/src/ops_tbb.cpp @@ -0,0 +1,75 @@ +#include "egashin_k_radix_simple_merge/tbb/include/ops_tbb.hpp" + +#include +#include +#include + +#include +#include +#include + +#include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "egashin_k_radix_simple_merge/common/include/radix_utils.hpp" +#include "util/include/util.hpp" + +namespace egashin_k_radix_simple_merge { + +EgashinKRadixSimpleMergeTBB::EgashinKRadixSimpleMergeTBB(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = {}; +} + +bool EgashinKRadixSimpleMergeTBB::ValidationImpl() { + return true; +} + +bool EgashinKRadixSimpleMergeTBB::PreProcessingImpl() { + result_ = GetInput(); + return true; +} + +bool EgashinKRadixSimpleMergeTBB::RunImpl() { + if (result_.size() < 2) { + return true; + } + + const int workers = radix_utils::WorkerCount(result_.size(), ppc::util::GetNumThreads()); + oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, workers); + + const auto ranges = radix_utils::MakeRanges(result_.size(), workers); + oneapi::tbb::parallel_for(oneapi::tbb::blocked_range(0, ranges.size()), + [&](const oneapi::tbb::blocked_range &range) { + for (size_t i = range.begin(); i != range.end(); ++i) { + radix_utils::SortRange(result_, ranges[i].first, ranges[i].second); + } + }); + + auto parts = radix_utils::MakeParts(result_, ranges); + while (parts.size() > 1) { + const size_t pair_count = parts.size() / 2; + std::vector> next((parts.size() + 1) / 2); + + oneapi::tbb::parallel_for(oneapi::tbb::blocked_range(0, pair_count), + [&](const oneapi::tbb::blocked_range &range) { + for (size_t i = range.begin(); i != range.end(); ++i) { + next[i] = radix_utils::Merge(parts[2 * i], parts[(2 * i) + 1]); + } + }); + + if (parts.size() % 2 != 0) { + next.back() = std::move(parts.back()); + } + parts = std::move(next); + } + + result_ = std::move(parts.front()); + return true; +} + +bool EgashinKRadixSimpleMergeTBB::PostProcessingImpl() { + GetOutput() = result_; + return true; +} + +} // namespace egashin_k_radix_simple_merge diff --git a/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp index beb7bbd086..013d4c35a4 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp @@ -12,6 +12,7 @@ #include "egashin_k_radix_simple_merge/omp/include/ops_omp.hpp" #include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" #include "egashin_k_radix_simple_merge/stl/include/ops_stl.hpp" +#include "egashin_k_radix_simple_merge/tbb/include/ops_tbb.hpp" #include "util/include/func_test_util.hpp" #include "util/include/util.hpp" @@ -65,6 +66,7 @@ const std::array kTestParam = {{ const auto kTestTasksList = std::tuple_cat( ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); diff --git a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp index 917332ee6e..11057902f7 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp @@ -9,6 +9,7 @@ #include "egashin_k_radix_simple_merge/omp/include/ops_omp.hpp" #include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" #include "egashin_k_radix_simple_merge/stl/include/ops_stl.hpp" +#include "egashin_k_radix_simple_merge/tbb/include/ops_tbb.hpp" #include "util/include/perf_test_util.hpp" namespace egashin_k_radix_simple_merge { @@ -47,6 +48,7 @@ namespace { const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( PPC_SETTINGS_egashin_k_radix_simple_merge); From 74b7395c21f36d0b9dbc5c400ac2f4f6c90d1343 Mon Sep 17 00:00:00 2001 From: crn4tt Date: Thu, 4 Jun 2026 19:54:19 +0300 Subject: [PATCH 4/7] all done --- .../all/include/ops_all.hpp | 25 +++ .../all/src/ops_all.cpp | 149 ++++++++++++++++++ .../settings.json | 1 + .../tests/functional/main.cpp | 4 +- .../tests/performance/main.cpp | 4 +- 5 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 tasks/egashin_k_radix_simple_merge/all/include/ops_all.hpp create mode 100644 tasks/egashin_k_radix_simple_merge/all/src/ops_all.cpp diff --git a/tasks/egashin_k_radix_simple_merge/all/include/ops_all.hpp b/tasks/egashin_k_radix_simple_merge/all/include/ops_all.hpp new file mode 100644 index 0000000000..a0027fa070 --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/all/include/ops_all.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace egashin_k_radix_simple_merge { + +class EgashinKRadixSimpleMergeALL : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kALL; + } + + explicit EgashinKRadixSimpleMergeALL(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + OutType result_; +}; + +} // namespace egashin_k_radix_simple_merge diff --git a/tasks/egashin_k_radix_simple_merge/all/src/ops_all.cpp b/tasks/egashin_k_radix_simple_merge/all/src/ops_all.cpp new file mode 100644 index 0000000000..6e0699f60f --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/all/src/ops_all.cpp @@ -0,0 +1,149 @@ +#include "egashin_k_radix_simple_merge/all/include/ops_all.hpp" + +#include + +#include +#include +#include +#include + +#include "egashin_k_radix_simple_merge/common/include/common.hpp" +#include "egashin_k_radix_simple_merge/common/include/radix_utils.hpp" +#include "util/include/util.hpp" + +namespace egashin_k_radix_simple_merge { + +namespace { + +std::vector MakeCounts(size_t size, int rank_count) { + std::vector counts(static_cast(rank_count), 0); + const size_t base = size / static_cast(rank_count); + const size_t extra = size % static_cast(rank_count); + + for (int rank = 0; rank < rank_count; ++rank) { + const auto rank_index = static_cast(rank); + counts[rank_index] = static_cast(base + (std::cmp_less(rank, extra) ? size_t{1} : size_t{0})); + } + return counts; +} + +std::vector MakeDispls(const std::vector &counts) { + std::vector displs(counts.size(), 0); + for (size_t i = 1; i < counts.size(); ++i) { + displs[i] = displs[i - 1] + counts[i - 1]; + } + return displs; +} + +void SortLocal(std::vector &data) { + if (data.size() < 2) { + return; + } + + int workers = radix_utils::WorkerCount(data.size(), ppc::util::GetNumThreads()); + auto ranges = radix_utils::MakeRanges(data.size(), workers); + +#pragma omp parallel for default(none) shared(data, ranges, workers) num_threads(workers) schedule(static) + for (int i = 0; i < workers; ++i) { + radix_utils::SortRange(data, ranges[static_cast(i)].first, ranges[static_cast(i)].second); + } + + auto parts = radix_utils::MakeParts(data, ranges); + while (parts.size() > 1) { + size_t pair_count = parts.size() / 2; + std::vector> next((parts.size() + 1) / 2); + +#pragma omp parallel for default(none) shared(parts, next, pair_count, workers) num_threads(workers) schedule(static) + for (size_t i = 0; i < pair_count; ++i) { + next[i] = radix_utils::Merge(parts[2 * i], parts[(2 * i) + 1]); + } + + if (parts.size() % 2 != 0) { + next.back() = std::move(parts.back()); + } + parts = std::move(next); + } + + data = std::move(parts.front()); +} + +std::vector MergeGathered(const std::vector &data, const std::vector &counts, + const std::vector &displs) { + std::vector> parts(counts.size()); + for (size_t rank = 0; rank < counts.size(); ++rank) { + const auto begin = data.begin() + displs[rank]; + parts[rank] = std::vector(begin, begin + counts[rank]); + } + + while (parts.size() > 1) { + const size_t pair_count = parts.size() / 2; + std::vector> next((parts.size() + 1) / 2); + + for (size_t i = 0; i < pair_count; ++i) { + next[i] = radix_utils::Merge(parts[2 * i], parts[(2 * i) + 1]); + } + + if (parts.size() % 2 != 0) { + next.back() = std::move(parts.back()); + } + parts = std::move(next); + } + + return parts.empty() ? std::vector{} : std::move(parts.front()); +} + +} // namespace + +EgashinKRadixSimpleMergeALL::EgashinKRadixSimpleMergeALL(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = {}; +} + +bool EgashinKRadixSimpleMergeALL::ValidationImpl() { + return GetInput().size() <= static_cast(std::numeric_limits::max()); +} + +bool EgashinKRadixSimpleMergeALL::PreProcessingImpl() { + result_ = GetInput(); + return true; +} + +bool EgashinKRadixSimpleMergeALL::RunImpl() { + int rank = 0; + int rank_count = 1; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &rank_count); + + const auto counts = MakeCounts(result_.size(), rank_count); + const auto displs = MakeDispls(counts); + const int local_count = counts[static_cast(rank)]; + const int local_shift = displs[static_cast(rank)]; + + std::vector local(result_.begin() + local_shift, result_.begin() + local_shift + local_count); + SortLocal(local); + + std::vector gathered; + if (rank == 0) { + gathered = result_; + } + + MPI_Gatherv(local.data(), local_count, MPI_DOUBLE, gathered.data(), counts.data(), displs.data(), MPI_DOUBLE, 0, + MPI_COMM_WORLD); + + if (rank == 0) { + result_ = MergeGathered(gathered, counts, displs); + } else { + result_.resize(GetInput().size()); + } + + MPI_Bcast(result_.data(), static_cast(result_.size()), MPI_DOUBLE, 0, MPI_COMM_WORLD); + return true; +} + +bool EgashinKRadixSimpleMergeALL::PostProcessingImpl() { + GetOutput() = result_; + return true; +} + +} // namespace egashin_k_radix_simple_merge diff --git a/tasks/egashin_k_radix_simple_merge/settings.json b/tasks/egashin_k_radix_simple_merge/settings.json index 8a03e634e9..0be0208fc6 100644 --- a/tasks/egashin_k_radix_simple_merge/settings.json +++ b/tasks/egashin_k_radix_simple_merge/settings.json @@ -1,5 +1,6 @@ { "tasks": { + "all": "enabled", "omp": "enabled", "seq": "enabled", "stl": "enabled", diff --git a/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp index 013d4c35a4..f992317af8 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/functional/main.cpp @@ -8,6 +8,7 @@ #include #include +#include "egashin_k_radix_simple_merge/all/include/ops_all.hpp" #include "egashin_k_radix_simple_merge/common/include/common.hpp" #include "egashin_k_radix_simple_merge/omp/include/ops_omp.hpp" #include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" @@ -67,7 +68,8 @@ const auto kTestTasksList = std::tuple_cat( ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), - ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge)); + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_egashin_k_radix_simple_merge)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); diff --git a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp index 11057902f7..434cb86be7 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp @@ -5,6 +5,7 @@ #include #include +#include "egashin_k_radix_simple_merge/all/include/ops_all.hpp" #include "egashin_k_radix_simple_merge/common/include/common.hpp" #include "egashin_k_radix_simple_merge/omp/include/ops_omp.hpp" #include "egashin_k_radix_simple_merge/seq/include/ops_seq.hpp" @@ -49,7 +50,8 @@ namespace { const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( + EgashinKRadixSimpleMergeSTL, + EgashinKRadixSimpleMergeALL>( PPC_SETTINGS_egashin_k_radix_simple_merge); const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); From 992353940ce2ee5b1d8d145fcbae8d7840257ab1 Mon Sep 17 00:00:00 2001 From: crn4tt Date: Thu, 4 Jun 2026 19:55:29 +0300 Subject: [PATCH 5/7] report done --- .../all/report.md | 80 ++++++++++++ .../omp/report.md | 78 ++++++++++++ tasks/egashin_k_radix_simple_merge/report.md | 117 ++++++++++++++++++ .../seq/report.md | 77 ++++++++++++ .../stl/report.md | 78 ++++++++++++ .../tbb/report.md | 76 ++++++++++++ 6 files changed, 506 insertions(+) create mode 100644 tasks/egashin_k_radix_simple_merge/all/report.md create mode 100644 tasks/egashin_k_radix_simple_merge/omp/report.md create mode 100644 tasks/egashin_k_radix_simple_merge/report.md create mode 100644 tasks/egashin_k_radix_simple_merge/seq/report.md create mode 100644 tasks/egashin_k_radix_simple_merge/stl/report.md create mode 100644 tasks/egashin_k_radix_simple_merge/tbb/report.md diff --git a/tasks/egashin_k_radix_simple_merge/all/report.md b/tasks/egashin_k_radix_simple_merge/all/report.md new file mode 100644 index 0000000000..41386c684d --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/all/report.md @@ -0,0 +1,80 @@ +# Поразрядная сортировка double с простым слиянием - ALL + +- Student: Егашин Кирилл Олегович, group 3823Б1ФИ2 +- Technology: ALL +- Variant: 19 + +## 1. Введение + +ALL-версия использует гибридную схему: MPI делит данные между рангами, а внутри каждого ранга блок +сортируется потоками OpenMP. + +## 2. Постановка задачи + +Нужно отсортировать общий входной `std::vector` по возрастанию. После выполнения результат должен быть +одинаковым на всех рангах, чтобы общий тест мог проверить выход каждой задачи. + +## 3. Базовый алгоритм + +Внутри каждого локального блока применяется тот же radix sort по 64-битным ключам. После локальной сортировки +блоки собираются на ранге 0 и сливаются простым merge. + +## 4. Межпроцессная схема + +Каждый ранг получает свой диапазон индексов. В текущей тестовой инфраструктуре вход доступен каждому рангу, +поэтому ранг локально берет свой срез входного массива. + +После локальной сортировки используется `MPI_Gatherv`, чтобы собрать отсортированные блоки на ранге 0. Ранг 0 +выполняет простое слияние всех частей. Затем итоговый массив рассылается всем рангам через `MPI_Bcast`. + +## 5. Внутрипроцессная схема + +Внутри ранга локальный блок дополнительно делится на подблоки по `PPC_NUM_THREADS`. Подблоки сортируются +через `#pragma omp parallel for`, затем сливаются уровнями. + +## 6. Детали реализации + +Основные файлы: + +- `common/include/radix_utils.hpp` +- `all/include/ops_all.hpp` +- `all/src/ops_all.cpp` + +`ValidationImpl` проверяет, что размер входа помещается в `int`, потому что MPI counts и displacements +передаются как `int`. + +## 7. Проверка корректности + +ALL подключена к общим functional и performance tests. При обычном запуске без MPI functional runner +пропускает ALL-тесты. При запуске через `mpirun` результат проверяется на каждом ранге после `MPI_Bcast`. + +## 8. Экспериментальная среда + +Окружение: + +- OS: Ubuntu 24.04 +- Compiler: `g++-14` +- Build type: `Release` +- Processes: `PPC_NUM_PROC=2` +- Threads per process: `PPC_NUM_THREADS=2` +- Metric: `task_run` + +Команды: + +```bash +cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release +cmake --build build --parallel +PPC_NUM_THREADS=2 scripts/run_tests.py --running-type=processes --counts 1 2 4 --build-dir build +PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance --build-dir build +``` + +## 9. Результаты + +| Ранги | Потоки на ранг | Total workers | Time, s | Speedup vs seq | Efficiency | +| ----- | -------------- | ------------- | ------- | -------------- | ---------- | +| 2 | 2 | 4 | 0.030749 | 1.16 | 0.29 | + +## 10. Вывод + +Гибридная версия полезна для проверки процессного и потокового уровней параллелизма. Для малых размеров +ожидаются заметные накладные расходы на `MPI_Gatherv`, `MPI_Bcast` и финальное слияние на ранге 0. diff --git a/tasks/egashin_k_radix_simple_merge/omp/report.md b/tasks/egashin_k_radix_simple_merge/omp/report.md new file mode 100644 index 0000000000..98413717ef --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/omp/report.md @@ -0,0 +1,78 @@ +# Поразрядная сортировка double с простым слиянием - OMP + +- Student: Егашин Кирилл Олегович, group 3823Б1ФИ2 +- Technology: OMP +- Variant: 19 + +## 1. Введение + +OMP-версия распараллеливает сортировку за счет разбиения входного массива на независимые блоки. Каждый блок +сортируется поразрядной сортировкой, затем блоки попарно сливаются. + +## 2. Постановка задачи + +Вход и выход совпадают с SEQ-версией: нужно получить отсортированный `std::vector`. Корректность +проверяется сравнением с результатом `std::ranges::sort`. + +## 3. Базовый алгоритм + +Внутри каждого блока используется тот же radix sort, что и в последовательной версии: 8 проходов counting sort +по байтам 64-битного ключа. + +## 4. Схема распараллеливания + +Количество рабочих потоков берется из `PPC_NUM_THREADS`. Если элементов меньше, чем потоков, число потоков +уменьшается до размера массива. + +Массив делится на почти равные диапазоны. Для сортировки блоков используется `#pragma omp parallel for` с +`schedule(static)`. Запись в `result_` безопасна, потому что каждый поток работает только со своим диапазоном. + +После сортировки создаются отсортированные части. На каждом уровне merge пары частей сливаются параллельно. +Каждая пара записывает результат в отдельную ячейку `next`, поэтому `critical`, `atomic` и `reduction` не +нужны. + +## 5. Детали реализации + +Основные файлы: + +- `common/include/radix_utils.hpp` +- `omp/include/ops_omp.hpp` +- `omp/src/ops_omp.cpp` + +Общий helper содержит преобразование `double` в сортируемый ключ, counting pass, сортировку диапазона и +простое слияние двух отсортированных массивов. + +## 6. Проверка корректности + +Тесты подключают SEQ и OMP реализации к одному набору входов. Для OMP проверяются те же случаи, что и для SEQ: +пустой массив, один элемент, отрицательные числа, повторы и бесконечности. + +## 7. Экспериментальная среда + +Окружение: + +- OS: Ubuntu 24.04 +- Compiler: `g++-14` +- Build type: `Release` +- Threads: `PPC_NUM_THREADS=2` +- Metric: `task_run` + +Команды: + +```bash +cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release +cmake --build build --parallel +scripts/run_tests.py --running-type=threads --counts 1 2 4 --build-dir build +PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance --build-dir build +``` + +## 8. Результаты + +| Threads | Time, s | Speedup vs seq | Efficiency | +| ------- | ------- | -------------- | ---------- | +| 2 | 0.024104 | 1.48 | 0.74 | + +## 9. Вывод + +OMP-версия подходит для этой задачи, потому что сортировка блоков независима, а простое слияние можно +выполнять уровнями без общей записи в один результат. diff --git a/tasks/egashin_k_radix_simple_merge/report.md b/tasks/egashin_k_radix_simple_merge/report.md new file mode 100644 index 0000000000..c6ae37be5e --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/report.md @@ -0,0 +1,117 @@ +# Поразрядная сортировка для вещественных чисел double с простым слиянием + +- Student: Егашин Кирилл Олегович, group 3823Б1ФИ2 +- Variant: 19 +- Technology reports: `seq/report.md`, `omp/report.md`, `tbb/report.md`, `stl/report.md`, `all/report.md` + +## 1. Введение + +Работа сравнивает пять реализаций одной задачи: SEQ, OMP, TBB, STL и ALL. Задача хорошо подходит для сравнения +моделей параллелизма, потому что массив можно разделить на независимые части, отсортировать их отдельно и +затем объединить простым слиянием. + +## 2. Постановка задачи + +Вход: массив `std::vector`. + +Выход: отсортированный по возрастанию массив той же длины. + +Критерий корректности: выход должен совпадать с результатом `std::ranges::sort` для тех же входных данных. + +## 3. Единая методика + +Для всех реализаций используется один базовый подход: + +1. Преобразовать `double` в сортируемый 64-битный ключ. +2. Выполнить LSD radix sort по байтам ключа. +3. Для параллельных версий разделить вход на блоки. +4. Отсортировать блоки независимо. +5. Объединить отсортированные блоки простым merge. + +SEQ сортирует весь массив одним блоком. OMP, TBB и STL сортируют блоки в потоках. ALL дополнительно делит +данные между MPI рангами и внутри каждого ранга использует OpenMP. + +## 4. Сводка реализации + +Основные файлы: + +- `common/include/common.hpp` +- `common/include/radix_utils.hpp` +- `seq/src/ops_seq.cpp` +- `omp/src/ops_omp.cpp` +- `tbb/src/ops_tbb.cpp` +- `stl/src/ops_stl.cpp` +- `all/src/ops_all.cpp` +- `tests/functional/main.cpp` +- `tests/performance/main.cpp` + +Общий helper `radix_utils.hpp` используется новыми параллельными версиями для сортировки диапазона и простого +слияния. SEQ оставлена без рефакторинга, чтобы не менять уже готовую базовую реализацию. + +## 5. Корректность + +Функциональные тесты покрывают: + +- пустой массив +- один элемент +- уже отсортированный массив +- обратный порядок +- смешанные знаки +- повторы +- разные масштабы значений +- положительную и отрицательную бесконечность + +Performance test проверяет, что результат отсортирован, через `std::ranges::is_sorted`. + +## 6. Экспериментальная среда + +Окружение: + +- OS: Ubuntu 24.04 +- Compiler: `g++-14` +- Build type: `Release` +- Build system: CMake + Ninja +- Threads: `PPC_NUM_THREADS=2` +- Processes: `PPC_NUM_PROC=2` +- Metric: `task_run` + +## 7. Команды воспроизведения + +```bash +cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release +cmake --build build --parallel +scripts/run_tests.py --running-type=threads --counts 1 2 4 --build-dir build +PPC_NUM_THREADS=2 scripts/run_tests.py --running-type=processes --counts 1 2 4 --build-dir build +PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance --build-dir build +``` + +## 8. Агрегированные результаты + +| Backend | Configuration | Time, s | Speedup vs seq | Efficiency | +| ------- | ------------------- | ------- | -------------- | ---------- | +| seq | 1 worker | 0.035714 | 1.00 | N/A | +| omp | 2 threads | 0.024104 | 1.48 | 0.74 | +| tbb | 2 workers | 0.023022 | 1.55 | 0.78 | +| stl | 2 threads | 0.024620 | 1.45 | 0.73 | +| all | 2 ранга, 2 потока | 0.030749 | 1.16 | 0.29 | + +## 9. Интерпретация + +SEQ задает baseline и проверочный результат. + +OMP ожидаемо должен давать выигрыш на больших массивах, потому что сортировка блоков независима. + +TBB использует ту же блочную схему, но планирование выполняет runtime TBB. + +STL дает ручной контроль над потоками, но платит за создание и синхронизацию `std::thread`. + +ALL добавляет процессный уровень. На больших входах это может помочь, но на малых входах коммуникации и +финальное слияние на ранге 0 могут перекрыть выигрыш. + +## 10. Источники + +1. Документация курса: `docs/common_information/report.rst` +2. Документация курса: `docs/common_information/threading_tasks.rst` +3. OpenMP specification: +4. oneTBB documentation: +5. MPI standard: diff --git a/tasks/egashin_k_radix_simple_merge/seq/report.md b/tasks/egashin_k_radix_simple_merge/seq/report.md new file mode 100644 index 0000000000..5aa8635f5e --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/seq/report.md @@ -0,0 +1,77 @@ +# Поразрядная сортировка double с простым слиянием - SEQ + +- Student: Егашин Кирилл Олегович, group 3823Б1ФИ2 +- Technology: SEQ +- Variant: 19 + +## 1. Введение + +Задача состоит в сортировке массива чисел типа `double` по возрастанию. Последовательная версия используется +как эталон корректности для всех остальных реализаций. + +## 2. Постановка задачи + +Входные данные: `std::vector`. + +Выходные данные: отсортированный `std::vector` той же длины. + +Пустой массив и массив из одного элемента считаются корректными входами. В функциональных тестах проверяются +пустой массив, один элемент, уже отсортированные данные, обратный порядок, отрицательные числа, повторы, +разные масштабы и бесконечности. + +## 3. Алгоритм + +Каждое значение `double` преобразуется в 64-битный ключ. Для положительных чисел инвертируется знаковый бит, +для отрицательных инвертируются все биты. После такого преобразования обычный порядок ключей соответствует +порядку исходных чисел. + +Далее выполняются 8 устойчивых проходов counting sort по одному байту. После последнего прохода ключи +преобразуются обратно в `double`. + +Сложность по времени: `O(8 * (n + 256))`, то есть `O(n)`. + +Сложность по памяти: `O(n)`. + +## 4. Детали реализации + +Основные файлы: + +- `seq/include/ops_seq.hpp` +- `seq/src/ops_seq.cpp` + +`PreProcessingImpl` копирует входные данные во внутренний буфер. `RunImpl` запускает radix sort. +`PostProcessingImpl` записывает результат в выход задачи. + +## 5. Проверка корректности + +Функциональные тесты строят ожидаемый результат через `std::ranges::sort` и сравнивают его с выходом задачи. +Тест производительности дополнительно проверяет инвариант `std::ranges::is_sorted`. + +## 6. Экспериментальная среда + +Окружение: + +- OS: Ubuntu 24.04 +- Compiler: `g++-14` +- Build type: `Release` +- Threads: `PPC_NUM_THREADS=2` +- Metric: `task_run` + +Команды: + +```bash +cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release +cmake --build build --parallel +scripts/run_tests.py --running-type=threads --counts 1 2 4 --build-dir build +PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance --build-dir build +``` + +## 7. Результаты + +| Backend | Workers | Time, s | +| ------- | ------- | ------- | +| seq | 1 | 0.035714 | + +## 8. Вывод + +SEQ-версия задает базовый корректный результат и baseline для расчета ускорения в OMP, TBB, STL и ALL. diff --git a/tasks/egashin_k_radix_simple_merge/stl/report.md b/tasks/egashin_k_radix_simple_merge/stl/report.md new file mode 100644 index 0000000000..dca9efaec3 --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/stl/report.md @@ -0,0 +1,78 @@ +# Поразрядная сортировка double с простым слиянием - STL + +- Student: Егашин Кирилл Олегович, group 3823Б1ФИ2 +- Technology: STL +- Variant: 19 + +## 1. Введение + +STL-версия использует ручное управление потоками через `std::thread`. Цель этой реализации - явно задать +разбиение данных, запуск потоков и точку `join`. + +## 2. Постановка задачи + +Нужно получить отсортированный `std::vector`. Результат должен совпадать с SEQ-версией и с результатом +стандартной сортировки. + +## 3. Базовый алгоритм + +Каждый блок сортируется LSD radix sort. Затем отсортированные блоки объединяются простым попарным merge до +одного массива. + +## 4. Схема распараллеливания + +Число потоков берется из `PPC_NUM_THREADS` и ограничивается размером массива. + +На первом этапе создаются `workers` потоков. Каждый поток сортирует свой диапазон `result_`. Потоки +запускаются все вместе, а `join` вызывается только после создания всех рабочих потоков. + +На этапе слияния пары частей также делятся между потоками. Каждый поток обрабатывает диапазон индексов пар и +записывает только в свои элементы вектора `next`. + +Синхронизация через `mutex` не требуется, потому что нет одновременной записи в один и тот же элемент +контейнера. + +## 5. Детали реализации + +Основные файлы: + +- `common/include/radix_utils.hpp` +- `stl/include/ops_stl.hpp` +- `stl/src/ops_stl.cpp` + +Общий helper используется для сортировки диапазона и простого merge. STL-файл отвечает только за ручное +распределение работы между потоками. + +## 6. Проверка корректности + +STL-реализация добавлена в общие functional и performance tests. Проверяются те же входы, что и для SEQ. + +## 7. Экспериментальная среда + +Окружение: + +- OS: Ubuntu 24.04 +- Compiler: `g++-14` +- Build type: `Release` +- Threads: `PPC_NUM_THREADS=2` +- Metric: `task_run` + +Команды: + +```bash +cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release +cmake --build build --parallel +scripts/run_tests.py --running-type=threads --counts 1 2 4 --build-dir build +PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance --build-dir build +``` + +## 8. Результаты + +| Threads | Time, s | Speedup vs seq | Efficiency | +| ------- | ------- | -------------- | ---------- | +| 2 | 0.024620 | 1.45 | 0.73 | + +## 9. Вывод + +STL-версия показывает ту же блочную схему, но с ручным управлением потоками. Основные накладные расходы +связаны с созданием потоков и последовательной подготовкой уровней merge. diff --git a/tasks/egashin_k_radix_simple_merge/tbb/report.md b/tasks/egashin_k_radix_simple_merge/tbb/report.md new file mode 100644 index 0000000000..6f282215a5 --- /dev/null +++ b/tasks/egashin_k_radix_simple_merge/tbb/report.md @@ -0,0 +1,76 @@ +# Поразрядная сортировка double с простым слиянием - TBB + +- Student: Егашин Кирилл Олегович, group 3823Б1ФИ2 +- Technology: TBB +- Variant: 19 + +## 1. Введение + +TBB-версия использует задачно-ориентированное разбиение работы. Сначала параллельно сортируются блоки массива, +затем параллельно выполняются уровни простого слияния. + +## 2. Постановка задачи + +Нужно отсортировать `std::vector` по возрастанию. Последовательная версия считается baseline для +корректности и расчета ускорения. + +## 3. Базовый алгоритм + +В каждом блоке применяется LSD radix sort по 8 байтам ключа. Ключ строится так, чтобы порядок `uint64_t` +совпадал с порядком исходных `double`. + +## 4. Схема распараллеливания + +Фактическая конкуренция ограничивается через `oneapi::tbb::global_control` значением `PPC_NUM_THREADS`. + +Для сортировки блоков используется `oneapi::tbb::parallel_for` с `blocked_range`. Диапазон +`0..ranges.size()` соответствует списку блоков. Каждый блок сортируется независимо. + +Для merge используется та же схема: на каждом уровне `parallel_for` обрабатывает пары отсортированных частей. +Каждая задача пишет в свой индекс в векторе `next`, поэтому гонки отсутствуют. + +## 5. Детали реализации + +Основные файлы: + +- `common/include/radix_utils.hpp` +- `tbb/include/ops_tbb.hpp` +- `tbb/src/ops_tbb.cpp` + +Размер зерна оставлен по умолчанию у `blocked_range`, потому что единица работы уже является крупным блоком +сортировки или слияния, а не отдельным элементом. + +## 6. Проверка корректности + +TBB подключена к общему набору функциональных тестов. Результат проверяется через точное сравнение с +отсортированной копией входа. + +## 7. Экспериментальная среда + +Окружение: + +- OS: Ubuntu 24.04 +- Compiler: `g++-14` +- Build type: `Release` +- Workers: `PPC_NUM_THREADS=2` +- Metric: `task_run` + +Команды: + +```bash +cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release +cmake --build build --parallel +scripts/run_tests.py --running-type=threads --counts 1 2 4 --build-dir build +PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance --build-dir build +``` + +## 8. Результаты + +| Workers | Time, s | Speedup vs seq | Efficiency | +| ------- | ------- | -------------- | ---------- | +| 2 | 0.023022 | 1.55 | 0.78 | + +## 9. Вывод + +TBB-реализация повторяет алгоритмическую схему OMP, но передает планирование runtime TBB. Это удобно для +блочной сортировки и параллельных уровней merge. From 4d29dc9f039d8d95b0b14fa250abd936af8b20ef Mon Sep 17 00:00:00 2001 From: crn4tt Date: Thu, 4 Jun 2026 20:39:09 +0300 Subject: [PATCH 6/7] fix --- tasks/egashin_k_radix_simple_merge/all/report.md | 4 ++-- tasks/egashin_k_radix_simple_merge/omp/report.md | 4 ++-- tasks/egashin_k_radix_simple_merge/report.md | 14 +++++++------- tasks/egashin_k_radix_simple_merge/seq/report.md | 4 ++-- tasks/egashin_k_radix_simple_merge/stl/report.md | 4 ++-- tasks/egashin_k_radix_simple_merge/tbb/report.md | 4 ++-- .../tests/performance/main.cpp | 10 ++++------ 7 files changed, 21 insertions(+), 23 deletions(-) diff --git a/tasks/egashin_k_radix_simple_merge/all/report.md b/tasks/egashin_k_radix_simple_merge/all/report.md index 41386c684d..5ffec6decc 100644 --- a/tasks/egashin_k_radix_simple_merge/all/report.md +++ b/tasks/egashin_k_radix_simple_merge/all/report.md @@ -70,8 +70,8 @@ PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance ## 9. Результаты -| Ранги | Потоки на ранг | Total workers | Time, s | Speedup vs seq | Efficiency | -| ----- | -------------- | ------------- | ------- | -------------- | ---------- | +| Ранги | Потоки на ранг | Total workers | Time, s | Speedup vs seq | Efficiency | +| ----- | -------------- | ------------- | -------- | -------------- | ---------- | | 2 | 2 | 4 | 0.030749 | 1.16 | 0.29 | ## 10. Вывод diff --git a/tasks/egashin_k_radix_simple_merge/omp/report.md b/tasks/egashin_k_radix_simple_merge/omp/report.md index 98413717ef..4f921e630b 100644 --- a/tasks/egashin_k_radix_simple_merge/omp/report.md +++ b/tasks/egashin_k_radix_simple_merge/omp/report.md @@ -68,8 +68,8 @@ PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance ## 8. Результаты -| Threads | Time, s | Speedup vs seq | Efficiency | -| ------- | ------- | -------------- | ---------- | +| Threads | Time, s | Speedup vs seq | Efficiency | +| ------- | -------- | -------------- | ---------- | | 2 | 0.024104 | 1.48 | 0.74 | ## 9. Вывод diff --git a/tasks/egashin_k_radix_simple_merge/report.md b/tasks/egashin_k_radix_simple_merge/report.md index c6ae37be5e..2d5d578674 100644 --- a/tasks/egashin_k_radix_simple_merge/report.md +++ b/tasks/egashin_k_radix_simple_merge/report.md @@ -87,13 +87,13 @@ PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance ## 8. Агрегированные результаты -| Backend | Configuration | Time, s | Speedup vs seq | Efficiency | -| ------- | ------------------- | ------- | -------------- | ---------- | -| seq | 1 worker | 0.035714 | 1.00 | N/A | -| omp | 2 threads | 0.024104 | 1.48 | 0.74 | -| tbb | 2 workers | 0.023022 | 1.55 | 0.78 | -| stl | 2 threads | 0.024620 | 1.45 | 0.73 | -| all | 2 ранга, 2 потока | 0.030749 | 1.16 | 0.29 | +| Backend | Configuration | Time, s | Speedup vs seq | Efficiency | +| ------- | ----------------- | -------- | -------------- | ---------- | +| seq | 1 worker | 0.035714 | 1.00 | N/A | +| omp | 2 threads | 0.024104 | 1.48 | 0.74 | +| tbb | 2 workers | 0.023022 | 1.55 | 0.78 | +| stl | 2 threads | 0.024620 | 1.45 | 0.73 | +| all | 2 ранга, 2 потока | 0.030749 | 1.16 | 0.29 | ## 9. Интерпретация diff --git a/tasks/egashin_k_radix_simple_merge/seq/report.md b/tasks/egashin_k_radix_simple_merge/seq/report.md index 5aa8635f5e..748c030e4c 100644 --- a/tasks/egashin_k_radix_simple_merge/seq/report.md +++ b/tasks/egashin_k_radix_simple_merge/seq/report.md @@ -68,8 +68,8 @@ PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance ## 7. Результаты -| Backend | Workers | Time, s | -| ------- | ------- | ------- | +| Backend | Workers | Time, s | +| ------- | ------- | -------- | | seq | 1 | 0.035714 | ## 8. Вывод diff --git a/tasks/egashin_k_radix_simple_merge/stl/report.md b/tasks/egashin_k_radix_simple_merge/stl/report.md index dca9efaec3..5ad3f77fbd 100644 --- a/tasks/egashin_k_radix_simple_merge/stl/report.md +++ b/tasks/egashin_k_radix_simple_merge/stl/report.md @@ -68,8 +68,8 @@ PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance ## 8. Результаты -| Threads | Time, s | Speedup vs seq | Efficiency | -| ------- | ------- | -------------- | ---------- | +| Threads | Time, s | Speedup vs seq | Efficiency | +| ------- | -------- | -------------- | ---------- | | 2 | 0.024620 | 1.45 | 0.73 | ## 9. Вывод diff --git a/tasks/egashin_k_radix_simple_merge/tbb/report.md b/tasks/egashin_k_radix_simple_merge/tbb/report.md index 6f282215a5..abf6f2add7 100644 --- a/tasks/egashin_k_radix_simple_merge/tbb/report.md +++ b/tasks/egashin_k_radix_simple_merge/tbb/report.md @@ -66,8 +66,8 @@ PPC_NUM_THREADS=2 PPC_NUM_PROC=2 scripts/run_tests.py --running-type=performance ## 8. Результаты -| Workers | Time, s | Speedup vs seq | Efficiency | -| ------- | ------- | -------------- | ---------- | +| Workers | Time, s | Speedup vs seq | Efficiency | +| ------- | -------- | -------------- | ---------- | | 2 | 0.023022 | 1.55 | 0.78 | ## 9. Вывод diff --git a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp index 434cb86be7..82a76b217b 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp @@ -47,12 +47,10 @@ TEST_P(EgashinKRunPerfTestsThreads, RunPerfModes) { namespace { -const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( - PPC_SETTINGS_egashin_k_radix_simple_merge); +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_egashin_k_radix_simple_merge); const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); From 46172f589665c49576e13cd300bb4e9d90ff82d3 Mon Sep 17 00:00:00 2001 From: crn4tt Date: Thu, 4 Jun 2026 22:27:24 +0300 Subject: [PATCH 7/7] fix --- tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp index 82a76b217b..ff786e6846 100644 --- a/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp +++ b/tasks/egashin_k_radix_simple_merge/tests/performance/main.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "egashin_k_radix_simple_merge/all/include/ops_all.hpp" #include "egashin_k_radix_simple_merge/common/include/common.hpp"